00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025 #include <Bitmap.h>
00026 #include <Debug.h>
00027 #include <MessageFilter.h>
00028 #include <Screen.h>
00029 #include <Window.h>
00030
00031 #include <map>
00032
00033 #include "TransportButton.h"
00034 #include "DrawingTidbits.h"
00035
00036 class BitmapStash {
00037
00038
00039
00040
00041
00042 public:
00043 BitmapStash(TransportButton *);
00044 ~BitmapStash();
00045 BBitmap *GetBitmap(uint32 signature);
00046
00047 private:
00048 TransportButton *owner;
00049 map<uint32, BBitmap *> stash;
00050 };
00051
00052 BitmapStash::BitmapStash(TransportButton *owner)
00053 : owner(owner)
00054 {
00055 }
00056
00057 BBitmap *
00058 BitmapStash::GetBitmap(uint32 signature)
00059 {
00060 if (stash.find(signature) == stash.end()) {
00061 BBitmap *newBits = owner->MakeBitmap(signature);
00062 ASSERT(newBits);
00063 stash[signature] = newBits;
00064 }
00065
00066 return stash[signature];
00067 }
00068
00069 BitmapStash::~BitmapStash()
00070 {
00071
00072 for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++)
00073 delete (*i).second;
00074 }
00075
00076
00077 class PeriodicMessageSender {
00078
00079 public:
00080 static PeriodicMessageSender *Launch(BMessenger target,
00081 const BMessage *message, bigtime_t period);
00082 void Quit();
00083
00084 private:
00085 PeriodicMessageSender(BMessenger target, const BMessage *message,
00086 bigtime_t period);
00087 ~PeriodicMessageSender() {}
00088
00089
00090 static status_t TrackBinder(void *);
00091 void Run();
00092
00093 BMessenger target;
00094 BMessage message;
00095
00096 bigtime_t period;
00097
00098 bool requestToQuit;
00099 };
00100
00101
00102 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
00103 const BMessage *message, bigtime_t period)
00104 : target(target),
00105 message(*message),
00106 period(period),
00107 requestToQuit(false)
00108 {
00109 }
00110
00111 PeriodicMessageSender *
00112 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
00113 bigtime_t period)
00114 {
00115 PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
00116 thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
00117 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
00118
00119 if (thread <= 0 || resume_thread(thread) != B_OK) {
00120
00121 delete result;
00122 result = 0;
00123 }
00124
00125 return result;
00126 }
00127
00128 void
00129 PeriodicMessageSender::Quit()
00130 {
00131 requestToQuit = true;
00132 }
00133
00134 status_t
00135 PeriodicMessageSender::TrackBinder(void *castToThis)
00136 {
00137 ((PeriodicMessageSender *)castToThis)->Run();
00138 return 0;
00139 }
00140
00141 void
00142 PeriodicMessageSender::Run()
00143 {
00144 for (;;) {
00145 snooze(period);
00146 if (requestToQuit)
00147 break;
00148 target.SendMessage(&message);
00149 }
00150 delete this;
00151 }
00152
00153 class SkipButtonKeypressFilter : public BMessageFilter {
00154 public:
00155 SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
00156 TransportButton *target);
00157
00158 protected:
00159 filter_result Filter(BMessage *message, BHandler **handler);
00160
00161 private:
00162 uint32 shortcutKey;
00163 uint32 shortcutModifier;
00164 TransportButton *target;
00165 };
00166
00167 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
00168 uint32 shortcutModifier, TransportButton *target)
00169 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
00170 shortcutKey(shortcutKey),
00171 shortcutModifier(shortcutModifier),
00172 target(target)
00173 {
00174 }
00175
00176 filter_result
00177 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
00178 {
00179 if (target->IsEnabled()
00180 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
00181 uint32 modifiers;
00182 uint32 rawKeyChar = 0;
00183 uint8 byte = 0;
00184 int32 key = 0;
00185
00186 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
00187 || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
00188 || message->FindInt8("byte", (int8 *)&byte) != B_OK
00189 || message->FindInt32("key", &key) != B_OK)
00190 return B_DISPATCH_MESSAGE;
00191
00192 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
00193 | B_OPTION_KEY | B_MENU_KEY;
00194
00195
00196 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
00197 if (message->what == B_KEY_DOWN)
00198 target->ShortcutKeyDown();
00199 else
00200 target->ShortcutKeyUp();
00201
00202 return B_SKIP_MESSAGE;
00203 }
00204 }
00205
00206
00207 return B_DISPATCH_MESSAGE;
00208 }
00209
00210 TransportButton::TransportButton(BRect frame, const char *name,
00211 const unsigned char *normalBits,
00212 const unsigned char *pressedBits,
00213 const unsigned char *disabledBits,
00214 BMessage *invokeMessage, BMessage *startPressingMessage,
00215 BMessage *pressingMessage, BMessage *donePressingMessage, bigtime_t period,
00216 uint32 key, uint32 modifiers, uint32 resizeFlags)
00217 : BControl(frame, name, "", invokeMessage, resizeFlags, B_WILL_DRAW | B_NAVIGABLE),
00218 bitmaps(new BitmapStash(this)),
00219 normalBits(normalBits),
00220 pressedBits(pressedBits),
00221 disabledBits(disabledBits),
00222 startPressingMessage(startPressingMessage),
00223 pressingMessage(pressingMessage),
00224 donePressingMessage(donePressingMessage),
00225 pressingPeriod(period),
00226 mouseDown(false),
00227 keyDown(false),
00228 messageSender(0),
00229 keyPressFilter(0)
00230 {
00231 if (key)
00232 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
00233 }
00234
00235
00236 void
00237 TransportButton::AttachedToWindow()
00238 {
00239 _inherited::AttachedToWindow();
00240 if (keyPressFilter)
00241 Window()->AddCommonFilter(keyPressFilter);
00242
00243
00244 SetViewColor(B_TRANSPARENT_COLOR);
00245 }
00246
00247 void
00248 TransportButton::DetachedFromWindow()
00249 {
00250 if (keyPressFilter)
00251 Window()->RemoveCommonFilter(keyPressFilter);
00252 _inherited::DetachedFromWindow();
00253 }
00254
00255
00256 TransportButton::~TransportButton()
00257 {
00258 delete startPressingMessage;
00259 delete pressingMessage;
00260 delete donePressingMessage;
00261 delete bitmaps;
00262 delete keyPressFilter;
00263 }
00264
00265 void
00266 TransportButton::WindowActivated(bool state)
00267 {
00268 if (!state)
00269 ShortcutKeyUp();
00270
00271 _inherited::WindowActivated(state);
00272 }
00273
00274 void
00275 TransportButton::SetEnabled(bool on)
00276 {
00277 if (on != IsEnabled()) {
00278 _inherited::SetEnabled(on);
00279 if (!on)
00280 ShortcutKeyUp();
00281 }
00282 }
00283
00284 const unsigned char *
00285 TransportButton::BitsForMask(uint32 mask) const
00286 {
00287 switch (mask) {
00288 case 0:
00289 return normalBits;
00290 case kDisabledMask:
00291 return disabledBits;
00292 case kPressedMask:
00293 return pressedBits;
00294 default:
00295 break;
00296 }
00297 TRESPASS();
00298 return 0;
00299 }
00300
00301
00302 BBitmap *
00303 TransportButton::MakeBitmap(uint32 mask)
00304 {
00305 BRect r(Bounds());
00306 BBitmap *result = new BBitmap(r, B_CMAP8);
00307
00308 uint8* src = (uint8*)BitsForMask(mask);
00309
00310 if (src && result && result->IsValid()) {
00311
00312 int32 height = r.IntegerHeight() + 1;
00313 int32 bpr = result->BytesPerRow();
00314 uint8* dst = (uint8*)result->Bits();
00315
00316
00317
00318
00319
00320 for (int32 y = 0; y < height; y++) {
00321 memcpy(dst, src, bpr);
00322 src += bpr;
00323 dst += bpr;
00324 }
00325 ReplaceTransparentColor(result, Parent()->ViewColor());
00326 } else {
00327 delete result;
00328 result = NULL;
00329 }
00330
00331 return result;
00332 }
00333
00334 uint32
00335 TransportButton::ModeMask() const
00336 {
00337 return (IsEnabled() ? 0 : kDisabledMask)
00338 | (Value() ? kPressedMask : 0);
00339 }
00340
00341 void
00342 TransportButton::Draw(BRect)
00343 {
00344 DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
00345 }
00346
00347
00348 void
00349 TransportButton::StartPressing()
00350 {
00351 SetValue(1);
00352 if (startPressingMessage)
00353 Invoke(startPressingMessage);
00354
00355 if (pressingMessage) {
00356 ASSERT(pressingMessage);
00357 messageSender = PeriodicMessageSender::Launch(Messenger(),
00358 pressingMessage, pressingPeriod);
00359 }
00360 }
00361
00362 void
00363 TransportButton::MouseCancelPressing()
00364 {
00365 if (!mouseDown || keyDown)
00366 return;
00367
00368 mouseDown = false;
00369
00370 if (pressingMessage) {
00371 ASSERT(messageSender);
00372 PeriodicMessageSender *sender = messageSender;
00373 messageSender = 0;
00374 sender->Quit();
00375 }
00376
00377 if (donePressingMessage)
00378 Invoke(donePressingMessage);
00379 SetValue(0);
00380 }
00381
00382 void
00383 TransportButton::DonePressing()
00384 {
00385 if (pressingMessage) {
00386 ASSERT(messageSender);
00387 PeriodicMessageSender *sender = messageSender;
00388 messageSender = 0;
00389 sender->Quit();
00390 }
00391
00392 Invoke();
00393 SetValue(0);
00394 }
00395
00396 void
00397 TransportButton::MouseStartPressing()
00398 {
00399 if (mouseDown)
00400 return;
00401
00402 mouseDown = true;
00403 if (!keyDown)
00404 StartPressing();
00405 }
00406
00407 void
00408 TransportButton::MouseDonePressing()
00409 {
00410 if (!mouseDown)
00411 return;
00412
00413 mouseDown = false;
00414 if (!keyDown)
00415 DonePressing();
00416 }
00417
00418 void
00419 TransportButton::ShortcutKeyDown()
00420 {
00421 if (!IsEnabled())
00422 return;
00423
00424 if (keyDown)
00425 return;
00426
00427 keyDown = true;
00428 if (!mouseDown)
00429 StartPressing();
00430 }
00431
00432 void
00433 TransportButton::ShortcutKeyUp()
00434 {
00435 if (!keyDown)
00436 return;
00437
00438 keyDown = false;
00439 if (!mouseDown)
00440 DonePressing();
00441 }
00442
00443
00444 void
00445 TransportButton::MouseDown(BPoint)
00446 {
00447 if (!IsEnabled())
00448 return;
00449
00450 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
00451 SetTracking(true);
00452 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
00453 MouseStartPressing();
00454 }
00455
00456 void
00457 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
00458 {
00459 if (IsTracking() && Bounds().Contains(point) != Value()) {
00460 if (!Value())
00461 MouseStartPressing();
00462 else
00463 MouseCancelPressing();
00464 }
00465 }
00466
00467 void
00468 TransportButton::MouseUp(BPoint point)
00469 {
00470 if (IsTracking()) {
00471 if (Bounds().Contains(point))
00472 MouseDonePressing();
00473 else
00474 MouseCancelPressing();
00475 SetTracking(false);
00476 }
00477 }
00478
00479 void
00480 TransportButton::SetStartPressingMessage(BMessage *message)
00481 {
00482 delete startPressingMessage;
00483 startPressingMessage = message;
00484 }
00485
00486 void
00487 TransportButton::SetPressingMessage(BMessage *message)
00488 {
00489 delete pressingMessage;
00490 pressingMessage = message;
00491 }
00492
00493 void
00494 TransportButton::SetDonePressingMessage(BMessage *message)
00495 {
00496 delete donePressingMessage;
00497 donePressingMessage = message;
00498 }
00499
00500 void
00501 TransportButton::SetPressingPeriod(bigtime_t newTime)
00502 {
00503 pressingPeriod = newTime;
00504 }
00505
00506
00507 PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
00508 const unsigned char *normalBits, const unsigned char *pressedBits,
00509 const unsigned char *disabledBits, const unsigned char *normalPlayingBits,
00510 const unsigned char *pressedPlayingBits, const unsigned char *normalPausedBits,
00511 const unsigned char *pressedPausedBits,
00512 BMessage *invokeMessage, uint32 key, uint32 modifiers, uint32 resizeFlags)
00513 : TransportButton(frame, name, normalBits, pressedBits,
00514 disabledBits, invokeMessage, 0,
00515 0, 0, 0, key, modifiers, resizeFlags),
00516 normalPlayingBits(normalPlayingBits),
00517 pressedPlayingBits(pressedPlayingBits),
00518 normalPausedBits(normalPausedBits),
00519 pressedPausedBits(pressedPausedBits),
00520 state(PlayPauseButton::kStopped),
00521 lastPauseBlinkTime(0),
00522 lastModeMask(0)
00523 {
00524 }
00525
00526 void
00527 PlayPauseButton::SetStopped()
00528 {
00529 if (state == kStopped || state == kAboutToPlay)
00530 return;
00531
00532 state = kStopped;
00533 Invalidate();
00534 }
00535
00536 void
00537 PlayPauseButton::SetPlaying()
00538 {
00539 if (state == kPlaying || state == kAboutToPause)
00540 return;
00541
00542 state = kPlaying;
00543 Invalidate();
00544 }
00545
00546 const bigtime_t kPauseBlinkPeriod = 600000;
00547
00548 void
00549 PlayPauseButton::SetPaused()
00550 {
00551 if (state == kAboutToPlay)
00552 return;
00553
00554
00555 bigtime_t now = system_time();
00556 if (state == kPausedLedOn || state == kPausedLedOff) {
00557 if (now - lastPauseBlinkTime < kPauseBlinkPeriod)
00558 return;
00559
00560 if (state == kPausedLedOn)
00561 state = kPausedLedOff;
00562 else
00563 state = kPausedLedOn;
00564 } else
00565 state = kPausedLedOn;
00566
00567 lastPauseBlinkTime = now;
00568 Invalidate();
00569 }
00570
00571 uint32
00572 PlayPauseButton::ModeMask() const
00573 {
00574 if (!IsEnabled())
00575 return kDisabledMask;
00576
00577 uint32 result = 0;
00578
00579 if (Value())
00580 result = kPressedMask;
00581
00582 if (state == kPlaying || state == kAboutToPlay)
00583 result |= kPlayingMask;
00584 else if (state == kAboutToPause || state == kPausedLedOn)
00585 result |= kPausedMask;
00586
00587 return result;
00588 }
00589
00590 const unsigned char *
00591 PlayPauseButton::BitsForMask(uint32 mask) const
00592 {
00593 switch (mask) {
00594 case kPlayingMask:
00595 return normalPlayingBits;
00596 case kPlayingMask | kPressedMask:
00597 return pressedPlayingBits;
00598 case kPausedMask:
00599 return normalPausedBits;
00600 case kPausedMask | kPressedMask:
00601 return pressedPausedBits;
00602 default:
00603 return _inherited::BitsForMask(mask);
00604 }
00605 TRESPASS();
00606 return 0;
00607 }
00608
00609
00610 void
00611 PlayPauseButton::StartPressing()
00612 {
00613 if (state == kPlaying)
00614 state = kAboutToPause;
00615 else
00616 state = kAboutToPlay;
00617
00618 _inherited::StartPressing();
00619 }
00620
00621 void
00622 PlayPauseButton::MouseCancelPressing()
00623 {
00624 if (state == kAboutToPause)
00625 state = kPlaying;
00626 else
00627 state = kStopped;
00628
00629 _inherited::MouseCancelPressing();
00630 }
00631
00632 void
00633 PlayPauseButton::DonePressing()
00634 {
00635 if (state == kAboutToPause) {
00636 state = kPausedLedOn;
00637 lastPauseBlinkTime = system_time();
00638 } else if (state == kAboutToPlay)
00639 state = kPlaying;
00640
00641 _inherited::DonePressing();
00642 }
00643
00644