2 * Copyright 2005, Jérôme Duval. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Inspired by SoundCapture from Be newsletter (Media Kit Basics: Consumers
11 #include <MessageFilter.h>
16 #include "TransportButton.h"
17 #include "DrawingTidbits.h"
22 // Bitmap stash is a simple class to hold all the lazily-allocated
23 // bitmaps that the TransportButton needs when rendering itself.
24 // signature is a combination of the different enabled, pressed, playing, etc.
25 // flavors of a bitmap. If the stash does not have a particular bitmap,
26 // it turns around to ask the button to create one and stores it for next time.
28 BitmapStash(TransportButton
*);
30 BBitmap
*GetBitmap(uint32 signature
);
33 TransportButton
*owner
;
34 map
<uint32
, BBitmap
*> stash
;
38 BitmapStash::BitmapStash(TransportButton
*owner
)
45 BitmapStash::GetBitmap(uint32 signature
)
47 if (stash
.find(signature
) == stash
.end()) {
48 BBitmap
*newBits
= owner
->MakeBitmap(signature
);
50 stash
[signature
] = newBits
;
53 return stash
[signature
];
57 BitmapStash::~BitmapStash()
59 // delete all the bitmaps
60 for (map
<uint32
, BBitmap
*>::iterator i
= stash
.begin();
61 i
!= stash
.end(); i
++)
66 class PeriodicMessageSender
{
67 // used to send a specified message repeatedly when holding down a button
69 static PeriodicMessageSender
*Launch(BMessenger target
,
70 const BMessage
*message
, bigtime_t period
);
74 PeriodicMessageSender(BMessenger target
, const BMessage
*message
,
76 ~PeriodicMessageSender() {}
79 static status_t
TrackBinder(void *);
91 PeriodicMessageSender::PeriodicMessageSender(BMessenger target
,
92 const BMessage
*message
, bigtime_t period
)
101 PeriodicMessageSender
*
102 PeriodicMessageSender::Launch(BMessenger target
, const BMessage
*message
,
105 PeriodicMessageSender
*result
= new PeriodicMessageSender(target
,
107 thread_id thread
= spawn_thread(&PeriodicMessageSender::TrackBinder
,
108 "ButtonRepeatingThread", B_NORMAL_PRIORITY
, result
);
110 if (thread
<= 0 || resume_thread(thread
) != B_OK
) {
111 // didn't start, don't leak self
121 PeriodicMessageSender::Quit()
123 requestToQuit
= true;
128 PeriodicMessageSender::TrackBinder(void *castToThis
)
130 ((PeriodicMessageSender
*)castToThis
)->Run();
136 PeriodicMessageSender::Run()
142 target
.SendMessage(&message
);
148 class SkipButtonKeypressFilter
: public BMessageFilter
{
150 SkipButtonKeypressFilter(uint32 shortcutKey
, uint32 shortcutModifier
,
151 TransportButton
*target
);
154 filter_result
Filter(BMessage
*message
, BHandler
**handler
);
158 uint32 shortcutModifier
;
159 TransportButton
*target
;
163 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey
,
164 uint32 shortcutModifier
, TransportButton
*target
)
165 : BMessageFilter(B_ANY_DELIVERY
, B_ANY_SOURCE
),
166 shortcutKey(shortcutKey
),
167 shortcutModifier(shortcutModifier
),
174 SkipButtonKeypressFilter::Filter(BMessage
*message
, BHandler
**handler
)
176 if (target
->IsEnabled()
177 && (message
->what
== B_KEY_DOWN
|| message
->what
== B_KEY_UP
)) {
179 uint32 rawKeyChar
= 0;
183 if (message
->FindInt32("modifiers", (int32
*)&modifiers
) != B_OK
184 || message
->FindInt32("raw_char", (int32
*)&rawKeyChar
) != B_OK
185 || message
->FindInt8("byte", (int8
*)&byte
) != B_OK
186 || message
->FindInt32("key", &key
) != B_OK
)
187 return B_DISPATCH_MESSAGE
;
189 modifiers
&= B_SHIFT_KEY
| B_COMMAND_KEY
| B_CONTROL_KEY
190 | B_OPTION_KEY
| B_MENU_KEY
;
191 // strip caps lock, etc.
193 if (modifiers
== shortcutModifier
&& rawKeyChar
== shortcutKey
) {
194 if (message
->what
== B_KEY_DOWN
)
195 target
->ShortcutKeyDown();
197 target
->ShortcutKeyUp();
199 return B_SKIP_MESSAGE
;
203 // let others deal with this
204 return B_DISPATCH_MESSAGE
;
208 TransportButton::TransportButton(BRect frame
, const char *name
,
209 const unsigned char *normalBits
,
210 const unsigned char *pressedBits
,
211 const unsigned char *disabledBits
,
212 BMessage
*invokeMessage
, BMessage
*startPressingMessage
,
213 BMessage
*pressingMessage
, BMessage
*donePressingMessage
, bigtime_t period
,
214 uint32 key
, uint32 modifiers
, uint32 resizeFlags
)
215 : BControl(frame
, name
, "", invokeMessage
, resizeFlags
,
216 B_WILL_DRAW
| B_NAVIGABLE
),
217 bitmaps(new BitmapStash(this)),
218 normalBits(normalBits
),
219 pressedBits(pressedBits
),
220 disabledBits(disabledBits
),
221 startPressingMessage(startPressingMessage
),
222 pressingMessage(pressingMessage
),
223 donePressingMessage(donePressingMessage
),
224 pressingPeriod(period
),
231 keyPressFilter
= new SkipButtonKeypressFilter(key
, modifiers
, this);
236 TransportButton::AttachedToWindow()
238 _inherited::AttachedToWindow();
240 Window()->AddCommonFilter(keyPressFilter
);
242 // transparent to reduce flicker
243 SetViewColor(B_TRANSPARENT_COLOR
);
248 TransportButton::DetachedFromWindow()
250 if (keyPressFilter
) {
251 Window()->RemoveCommonFilter(keyPressFilter
);
252 delete keyPressFilter
;
254 _inherited::DetachedFromWindow();
258 TransportButton::~TransportButton()
260 delete startPressingMessage
;
261 delete pressingMessage
;
262 delete donePressingMessage
;
268 TransportButton::WindowActivated(bool state
)
273 _inherited::WindowActivated(state
);
278 TransportButton::SetEnabled(bool on
)
280 _inherited::SetEnabled(on
);
286 const unsigned char *
287 TransportButton::BitsForMask(uint32 mask
) const
305 TransportButton::MakeBitmap(uint32 mask
)
307 BBitmap
*result
= new BBitmap(Bounds(), B_CMAP8
);
308 result
->SetBits(BitsForMask(mask
), (Bounds().Width() + 1)
309 * (Bounds().Height() + 1), 0, B_CMAP8
);
311 ReplaceTransparentColor(result
, Parent()->ViewColor());
318 TransportButton::ModeMask() const
320 return (IsEnabled() ? 0 : kDisabledMask
)
321 | (Value() ? kPressedMask
: 0);
326 TransportButton::Draw(BRect
)
328 DrawBitmapAsync(bitmaps
->GetBitmap(ModeMask()));
333 TransportButton::StartPressing()
336 if (startPressingMessage
)
337 Invoke(startPressingMessage
);
339 if (pressingMessage
) {
340 ASSERT(pressingMessage
);
341 messageSender
= PeriodicMessageSender::Launch(Messenger(),
342 pressingMessage
, pressingPeriod
);
348 TransportButton::MouseCancelPressing()
350 if (!mouseDown
|| keyDown
)
355 if (pressingMessage
) {
356 ASSERT(messageSender
);
357 PeriodicMessageSender
*sender
= messageSender
;
362 if (donePressingMessage
)
363 Invoke(donePressingMessage
);
369 TransportButton::DonePressing()
371 if (pressingMessage
) {
372 ASSERT(messageSender
);
373 PeriodicMessageSender
*sender
= messageSender
;
384 TransportButton::MouseStartPressing()
396 TransportButton::MouseDonePressing()
408 TransportButton::ShortcutKeyDown()
423 TransportButton::ShortcutKeyUp()
435 TransportButton::MouseDown(BPoint
)
440 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS
);
442 SetMouseEventMask(B_POINTER_EVENTS
, B_LOCK_WINDOW_FOCUS
);
443 MouseStartPressing();
447 TransportButton::MouseMoved(BPoint point
, uint32 code
, const BMessage
*)
449 if (IsTracking() && Bounds().Contains(point
) != Value()) {
451 MouseStartPressing();
453 MouseCancelPressing();
458 TransportButton::MouseUp(BPoint point
)
461 if (Bounds().Contains(point
))
464 MouseCancelPressing();
470 TransportButton::SetStartPressingMessage(BMessage
*message
)
472 delete startPressingMessage
;
473 startPressingMessage
= message
;
477 TransportButton::SetPressingMessage(BMessage
*message
)
479 delete pressingMessage
;
480 pressingMessage
= message
;
484 TransportButton::SetDonePressingMessage(BMessage
*message
)
486 delete donePressingMessage
;
487 donePressingMessage
= message
;
491 TransportButton::SetPressingPeriod(bigtime_t newTime
)
493 pressingPeriod
= newTime
;
497 PlayPauseButton::PlayPauseButton(BRect frame
, const char *name
,
498 BMessage
*invokeMessage
, BMessage
*blinkMessage
,
499 uint32 key
, uint32 modifiers
, uint32 resizeFlags
)
500 : TransportButton(frame
, name
, kPlayButtonBitmapBits
,
501 kPressedPlayButtonBitmapBits
,
502 kDisabledPlayButtonBitmapBits
, invokeMessage
, NULL
,
503 NULL
, NULL
, 0, key
, modifiers
, resizeFlags
),
504 fState(PlayPauseButton::kStopped
),
507 fBlinkMessage(blinkMessage
)
512 PlayPauseButton::SetStopped()
514 if (fState
== kStopped
|| fState
== kAboutToPlay
)
524 PlayPauseButton::SetPlaying()
526 if (fState
== kAboutToPause
)
529 // in playing state blink the LED on and off
530 if (fState
== kPlayingLedOn
)
531 fState
= kPlayingLedOff
;
533 fState
= kPlayingLedOn
;
538 const bigtime_t kPlayingBlinkPeriod
= 600000;
541 PlayPauseButton::SetPaused()
543 if (fState
== kAboutToPlay
)
546 // in paused state blink the LED on and off
547 if (fState
== kPausedLedOn
)
548 fState
= kPausedLedOff
;
550 fState
= kPausedLedOn
;
556 PlayPauseButton::ModeMask() const
559 return kDisabledMask
;
564 result
= kPressedMask
;
566 if (fState
== kPlayingLedOn
|| fState
== kAboutToPlay
)
567 result
|= kPlayingMask
;
568 else if (fState
== kAboutToPause
|| fState
== kPausedLedOn
)
569 result
|= kPausedMask
;
574 const unsigned char *
575 PlayPauseButton::BitsForMask(uint32 mask
) const
579 return kPlayingPlayButtonBitmapBits
;
580 case kPlayingMask
| kPressedMask
:
581 return kPressedPlayingPlayButtonBitmapBits
;
583 return kPausedPlayButtonBitmapBits
;
584 case kPausedMask
| kPressedMask
:
585 return kPressedPausedPlayButtonBitmapBits
;
587 return _inherited::BitsForMask(mask
);
595 PlayPauseButton::StartPressing()
597 if (fState
== kPlayingLedOn
|| fState
== kPlayingLedOff
)
598 fState
= kAboutToPause
;
600 fState
= kAboutToPlay
;
602 _inherited::StartPressing();
606 PlayPauseButton::MouseCancelPressing()
608 if (fState
== kAboutToPause
)
609 fState
= kPlayingLedOn
;
613 _inherited::MouseCancelPressing();
617 PlayPauseButton::DonePressing()
619 if (fState
== kAboutToPause
) {
620 fState
= kPausedLedOn
;
621 } else if (fState
== kAboutToPlay
) {
622 fState
= kPlayingLedOn
;
623 if (!fRunner
&& fBlinkMessage
)
624 fRunner
= new BMessageRunner(Messenger(), fBlinkMessage
,
625 kPlayingBlinkPeriod
);
628 _inherited::DonePressing();
632 RecordButton::RecordButton(BRect frame
, const char *name
,
633 BMessage
*invokeMessage
, BMessage
*blinkMessage
,
634 uint32 key
, uint32 modifiers
, uint32 resizeFlags
)
635 : TransportButton(frame
, name
, kRecordButtonBitmapBits
,
636 kPressedRecordButtonBitmapBits
,
637 kDisabledRecordButtonBitmapBits
, invokeMessage
, NULL
, NULL
,
638 NULL
, 0, key
, modifiers
, resizeFlags
),
639 fState(RecordButton::kStopped
),
642 fBlinkMessage(blinkMessage
)
647 RecordButton::SetStopped()
649 if (fState
== kStopped
|| fState
== kAboutToRecord
)
658 const bigtime_t kRecordingBlinkPeriod
= 600000;
661 RecordButton::SetRecording()
663 if (fState
== kAboutToStop
)
666 if (fState
== kRecordingLedOff
)
667 fState
= kRecordingLedOn
;
669 fState
= kRecordingLedOff
;
675 RecordButton::ModeMask() const
678 return kDisabledMask
;
683 result
= kPressedMask
;
685 if (fState
== kAboutToStop
|| fState
== kRecordingLedOn
)
686 result
|= kRecordingMask
;
691 const unsigned char *
692 RecordButton::BitsForMask(uint32 mask
) const
696 return kRecordingRecordButtonBitmapBits
;
697 case kRecordingMask
| kPressedMask
:
698 return kPressedRecordingRecordButtonBitmapBits
;
700 return _inherited::BitsForMask(mask
);
708 RecordButton::StartPressing()
710 if (fState
== kRecordingLedOn
|| fState
== kRecordingLedOff
)
711 fState
= kAboutToStop
;
713 fState
= kAboutToRecord
;
715 _inherited::StartPressing();
719 RecordButton::MouseCancelPressing()
721 if (fState
== kAboutToStop
)
722 fState
= kRecordingLedOn
;
726 _inherited::MouseCancelPressing();
730 RecordButton::DonePressing()
732 if (fState
== kAboutToStop
) {
736 } else if (fState
== kAboutToRecord
) {
737 fState
= kRecordingLedOn
;
738 if (!fRunner
&& fBlinkMessage
)
739 fRunner
= new BMessageRunner(Messenger(), fBlinkMessage
,
740 kRecordingBlinkPeriod
);
743 _inherited::DonePressing();