libroot/posix/stdio: Remove unused portions.
[haiku.git] / src / apps / soundrecorder / TransportButton.cpp
bloba4246851e099dd7b83a16e753368665143f9c82e
1 /*
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
6 * and Producers)
7 */
9 #include <Bitmap.h>
10 #include <Debug.h>
11 #include <MessageFilter.h>
12 #include <Window.h>
14 #include <map>
16 #include "TransportButton.h"
17 #include "DrawingTidbits.h"
19 using std::map;
21 class BitmapStash {
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.
27 public:
28 BitmapStash(TransportButton *);
29 ~BitmapStash();
30 BBitmap *GetBitmap(uint32 signature);
32 private:
33 TransportButton *owner;
34 map<uint32, BBitmap *> stash;
38 BitmapStash::BitmapStash(TransportButton *owner)
39 : owner(owner)
44 BBitmap *
45 BitmapStash::GetBitmap(uint32 signature)
47 if (stash.find(signature) == stash.end()) {
48 BBitmap *newBits = owner->MakeBitmap(signature);
49 ASSERT(newBits);
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++)
62 delete (*i).second;
66 class PeriodicMessageSender {
67 // used to send a specified message repeatedly when holding down a button
68 public:
69 static PeriodicMessageSender *Launch(BMessenger target,
70 const BMessage *message, bigtime_t period);
71 void Quit();
73 private:
74 PeriodicMessageSender(BMessenger target, const BMessage *message,
75 bigtime_t period);
76 ~PeriodicMessageSender() {}
77 // use quit
79 static status_t TrackBinder(void *);
80 void Run();
82 BMessenger target;
83 BMessage message;
85 bigtime_t period;
87 bool requestToQuit;
91 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
92 const BMessage *message, bigtime_t period)
93 : target(target),
94 message(*message),
95 period(period),
96 requestToQuit(false)
101 PeriodicMessageSender *
102 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
103 bigtime_t period)
105 PeriodicMessageSender *result = new PeriodicMessageSender(target,
106 message, period);
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
112 delete result;
113 result = 0;
116 return result;
120 void
121 PeriodicMessageSender::Quit()
123 requestToQuit = true;
127 status_t
128 PeriodicMessageSender::TrackBinder(void *castToThis)
130 ((PeriodicMessageSender *)castToThis)->Run();
131 return 0;
135 void
136 PeriodicMessageSender::Run()
138 for (;;) {
139 snooze(period);
140 if (requestToQuit)
141 break;
142 target.SendMessage(&message);
144 delete this;
148 class SkipButtonKeypressFilter : public BMessageFilter {
149 public:
150 SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
151 TransportButton *target);
153 protected:
154 filter_result Filter(BMessage *message, BHandler **handler);
156 private:
157 uint32 shortcutKey;
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),
168 target(target)
173 filter_result
174 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
176 if (target->IsEnabled()
177 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
178 uint32 modifiers;
179 uint32 rawKeyChar = 0;
180 uint8 byte = 0;
181 int32 key = 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();
196 else
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),
225 mouseDown(false),
226 keyDown(false),
227 messageSender(0),
228 keyPressFilter(0)
230 if (key)
231 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
235 void
236 TransportButton::AttachedToWindow()
238 _inherited::AttachedToWindow();
239 if (keyPressFilter)
240 Window()->AddCommonFilter(keyPressFilter);
242 // transparent to reduce flicker
243 SetViewColor(B_TRANSPARENT_COLOR);
247 void
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;
263 delete bitmaps;
267 void
268 TransportButton::WindowActivated(bool state)
270 if (!state)
271 ShortcutKeyUp();
273 _inherited::WindowActivated(state);
277 void
278 TransportButton::SetEnabled(bool on)
280 _inherited::SetEnabled(on);
281 if (!on)
282 ShortcutKeyUp();
286 const unsigned char *
287 TransportButton::BitsForMask(uint32 mask) const
289 switch (mask) {
290 case 0:
291 return normalBits;
292 case kDisabledMask:
293 return disabledBits;
294 case kPressedMask:
295 return pressedBits;
296 default:
297 break;
299 TRESPASS();
300 return 0;
304 BBitmap *
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());
313 return result;
317 uint32
318 TransportButton::ModeMask() const
320 return (IsEnabled() ? 0 : kDisabledMask)
321 | (Value() ? kPressedMask : 0);
325 void
326 TransportButton::Draw(BRect)
328 DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
332 void
333 TransportButton::StartPressing()
335 SetValue(1);
336 if (startPressingMessage)
337 Invoke(startPressingMessage);
339 if (pressingMessage) {
340 ASSERT(pressingMessage);
341 messageSender = PeriodicMessageSender::Launch(Messenger(),
342 pressingMessage, pressingPeriod);
347 void
348 TransportButton::MouseCancelPressing()
350 if (!mouseDown || keyDown)
351 return;
353 mouseDown = false;
355 if (pressingMessage) {
356 ASSERT(messageSender);
357 PeriodicMessageSender *sender = messageSender;
358 messageSender = 0;
359 sender->Quit();
362 if (donePressingMessage)
363 Invoke(donePressingMessage);
364 SetValue(0);
368 void
369 TransportButton::DonePressing()
371 if (pressingMessage) {
372 ASSERT(messageSender);
373 PeriodicMessageSender *sender = messageSender;
374 messageSender = 0;
375 sender->Quit();
378 Invoke();
379 SetValue(0);
383 void
384 TransportButton::MouseStartPressing()
386 if (mouseDown)
387 return;
389 mouseDown = true;
390 if (!keyDown)
391 StartPressing();
395 void
396 TransportButton::MouseDonePressing()
398 if (!mouseDown)
399 return;
401 mouseDown = false;
402 if (!keyDown)
403 DonePressing();
407 void
408 TransportButton::ShortcutKeyDown()
410 if (!IsEnabled())
411 return;
413 if (keyDown)
414 return;
416 keyDown = true;
417 if (!mouseDown)
418 StartPressing();
422 void
423 TransportButton::ShortcutKeyUp()
425 if (!keyDown)
426 return;
428 keyDown = false;
429 if (!mouseDown)
430 DonePressing();
434 void
435 TransportButton::MouseDown(BPoint)
437 if (!IsEnabled())
438 return;
440 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
441 SetTracking(true);
442 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
443 MouseStartPressing();
446 void
447 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
449 if (IsTracking() && Bounds().Contains(point) != Value()) {
450 if (!Value())
451 MouseStartPressing();
452 else
453 MouseCancelPressing();
457 void
458 TransportButton::MouseUp(BPoint point)
460 if (IsTracking()) {
461 if (Bounds().Contains(point))
462 MouseDonePressing();
463 else
464 MouseCancelPressing();
465 SetTracking(false);
469 void
470 TransportButton::SetStartPressingMessage(BMessage *message)
472 delete startPressingMessage;
473 startPressingMessage = message;
476 void
477 TransportButton::SetPressingMessage(BMessage *message)
479 delete pressingMessage;
480 pressingMessage = message;
483 void
484 TransportButton::SetDonePressingMessage(BMessage *message)
486 delete donePressingMessage;
487 donePressingMessage = message;
490 void
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),
505 fLastModeMask(0),
506 fRunner(NULL),
507 fBlinkMessage(blinkMessage)
511 void
512 PlayPauseButton::SetStopped()
514 if (fState == kStopped || fState == kAboutToPlay)
515 return;
517 fState = kStopped;
518 delete fRunner;
519 fRunner = NULL;
520 Invalidate();
523 void
524 PlayPauseButton::SetPlaying()
526 if (fState == kAboutToPause)
527 return;
529 // in playing state blink the LED on and off
530 if (fState == kPlayingLedOn)
531 fState = kPlayingLedOff;
532 else
533 fState = kPlayingLedOn;
535 Invalidate();
538 const bigtime_t kPlayingBlinkPeriod = 600000;
540 void
541 PlayPauseButton::SetPaused()
543 if (fState == kAboutToPlay)
544 return;
546 // in paused state blink the LED on and off
547 if (fState == kPausedLedOn)
548 fState = kPausedLedOff;
549 else
550 fState = kPausedLedOn;
552 Invalidate();
555 uint32
556 PlayPauseButton::ModeMask() const
558 if (!IsEnabled())
559 return kDisabledMask;
561 uint32 result = 0;
563 if (Value())
564 result = kPressedMask;
566 if (fState == kPlayingLedOn || fState == kAboutToPlay)
567 result |= kPlayingMask;
568 else if (fState == kAboutToPause || fState == kPausedLedOn)
569 result |= kPausedMask;
571 return result;
574 const unsigned char *
575 PlayPauseButton::BitsForMask(uint32 mask) const
577 switch (mask) {
578 case kPlayingMask:
579 return kPlayingPlayButtonBitmapBits;
580 case kPlayingMask | kPressedMask:
581 return kPressedPlayingPlayButtonBitmapBits;
582 case kPausedMask:
583 return kPausedPlayButtonBitmapBits;
584 case kPausedMask | kPressedMask:
585 return kPressedPausedPlayButtonBitmapBits;
586 default:
587 return _inherited::BitsForMask(mask);
589 TRESPASS();
590 return 0;
594 void
595 PlayPauseButton::StartPressing()
597 if (fState == kPlayingLedOn || fState == kPlayingLedOff)
598 fState = kAboutToPause;
599 else
600 fState = kAboutToPlay;
602 _inherited::StartPressing();
605 void
606 PlayPauseButton::MouseCancelPressing()
608 if (fState == kAboutToPause)
609 fState = kPlayingLedOn;
610 else
611 fState = kStopped;
613 _inherited::MouseCancelPressing();
616 void
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),
640 fLastModeMask(0),
641 fRunner(NULL),
642 fBlinkMessage(blinkMessage)
646 void
647 RecordButton::SetStopped()
649 if (fState == kStopped || fState == kAboutToRecord)
650 return;
652 fState = kStopped;
653 delete fRunner;
654 fRunner = NULL;
655 Invalidate();
658 const bigtime_t kRecordingBlinkPeriod = 600000;
660 void
661 RecordButton::SetRecording()
663 if (fState == kAboutToStop)
664 return;
666 if (fState == kRecordingLedOff)
667 fState = kRecordingLedOn;
668 else
669 fState = kRecordingLedOff;
671 Invalidate();
674 uint32
675 RecordButton::ModeMask() const
677 if (!IsEnabled())
678 return kDisabledMask;
680 uint32 result = 0;
682 if (Value())
683 result = kPressedMask;
685 if (fState == kAboutToStop || fState == kRecordingLedOn)
686 result |= kRecordingMask;
688 return result;
691 const unsigned char *
692 RecordButton::BitsForMask(uint32 mask) const
694 switch (mask) {
695 case kRecordingMask:
696 return kRecordingRecordButtonBitmapBits;
697 case kRecordingMask | kPressedMask:
698 return kPressedRecordingRecordButtonBitmapBits;
699 default:
700 return _inherited::BitsForMask(mask);
702 TRESPASS();
703 return 0;
707 void
708 RecordButton::StartPressing()
710 if (fState == kRecordingLedOn || fState == kRecordingLedOff)
711 fState = kAboutToStop;
712 else
713 fState = kAboutToRecord;
715 _inherited::StartPressing();
718 void
719 RecordButton::MouseCancelPressing()
721 if (fState == kAboutToStop)
722 fState = kRecordingLedOn;
723 else
724 fState = kStopped;
726 _inherited::MouseCancelPressing();
729 void
730 RecordButton::DonePressing()
732 if (fState == kAboutToStop) {
733 fState = kStopped;
734 delete fRunner;
735 fRunner = NULL;
736 } else if (fState == kAboutToRecord) {
737 fState = kRecordingLedOn;
738 if (!fRunner && fBlinkMessage)
739 fRunner = new BMessageRunner(Messenger(), fBlinkMessage,
740 kRecordingBlinkPeriod);
743 _inherited::DonePressing();