1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
5 * $Id: TransportButton.cpp,v 1.3 2001/03/21 13:42:33 sam Exp $
7 * Authors: Tony Castley <tcastley@mail.powerup.com.au>
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
26 #include <MessageFilter.h>
32 #include "TransportButton.h"
33 #include "DrawingTidbits.h"
36 // Bitmap stash is a simple class to hold all the lazily-allocated
37 // bitmaps that the TransportButton needs when rendering itself.
38 // signature is a combination of the different enabled, pressed, playing, etc.
39 // flavors of a bitmap. If the stash does not have a particular bitmap,
40 // it turns around to ask the button to create one and stores it for next time.
42 BitmapStash(TransportButton *);
44 BBitmap *GetBitmap(uint32 signature);
47 TransportButton *owner;
48 map<uint32, BBitmap *> stash;
51 BitmapStash::BitmapStash(TransportButton *owner)
57 BitmapStash::GetBitmap(uint32 signature)
59 if (stash.find(signature) == stash.end()) {
60 BBitmap *newBits = owner->MakeBitmap(signature);
62 stash[signature] = newBits;
65 return stash[signature];
68 BitmapStash::~BitmapStash()
70 // delete all the bitmaps
71 for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++)
76 class PeriodicMessageSender {
77 // used to send a specified message repeatedly when holding down a button
79 static PeriodicMessageSender *Launch(BMessenger target,
80 const BMessage *message, bigtime_t period);
84 PeriodicMessageSender(BMessenger target, const BMessage *message,
86 ~PeriodicMessageSender() {}
89 static status_t TrackBinder(void *);
101 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
102 const BMessage *message, bigtime_t period)
110 PeriodicMessageSender *
111 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
114 PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
115 thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
116 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
118 if (thread <= 0 || resume_thread(thread) != B_OK) {
119 // didn't start, don't leak self
128 PeriodicMessageSender::Quit()
130 requestToQuit = true;
134 PeriodicMessageSender::TrackBinder(void *castToThis)
136 ((PeriodicMessageSender *)castToThis)->Run();
141 PeriodicMessageSender::Run()
147 target.SendMessage(&message);
152 class SkipButtonKeypressFilter : public BMessageFilter {
154 SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
155 TransportButton *target);
158 filter_result Filter(BMessage *message, BHandler **handler);
162 uint32 shortcutModifier;
163 TransportButton *target;
166 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
167 uint32 shortcutModifier, TransportButton *target)
168 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
169 shortcutKey(shortcutKey),
170 shortcutModifier(shortcutModifier),
176 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
178 if (target->IsEnabled()
179 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
181 uint32 rawKeyChar = 0;
185 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
186 || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
187 || message->FindInt8("byte", (int8 *)&byte) != B_OK
188 || message->FindInt32("key", &key) != B_OK)
189 return B_DISPATCH_MESSAGE;
191 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
192 | B_OPTION_KEY | B_MENU_KEY;
193 // strip caps lock, etc.
195 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
196 if (message->what == B_KEY_DOWN)
197 target->ShortcutKeyDown();
199 target->ShortcutKeyUp();
201 return B_SKIP_MESSAGE;
205 // let others deal with this
206 return B_DISPATCH_MESSAGE;
209 TransportButton::TransportButton(BRect frame, const char *name,
210 const unsigned char *normalBits,
211 const unsigned char *pressedBits,
212 const unsigned char *disabledBits,
213 BMessage *invokeMessage, BMessage *startPressingMessage,
214 BMessage *pressingMessage, BMessage *donePressingMessage, bigtime_t period,
215 uint32 key, uint32 modifiers, uint32 resizeFlags)
216 : BControl(frame, name, "", invokeMessage, resizeFlags, 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);
247 TransportButton::DetachedFromWindow()
249 if (keyPressFilter) {
250 Window()->RemoveCommonFilter(keyPressFilter);
251 delete keyPressFilter;
253 _inherited::DetachedFromWindow();
257 TransportButton::~TransportButton()
259 delete startPressingMessage;
260 delete pressingMessage;
261 delete donePressingMessage;
266 TransportButton::WindowActivated(bool state)
271 _inherited::WindowActivated(state);
275 TransportButton::SetEnabled(bool on)
277 _inherited::SetEnabled(on);
282 const unsigned char *
283 TransportButton::BitsForMask(uint32 mask) const
301 TransportButton::MakeBitmap(uint32 mask)
303 BBitmap *result = new BBitmap(Bounds(), B_COLOR_8_BIT);
304 result->SetBits(BitsForMask(mask), (Bounds().Width() + 1) * (Bounds().Height() + 1),
307 ReplaceTransparentColor(result, Parent()->ViewColor());
313 TransportButton::ModeMask() const
315 return (IsEnabled() ? 0 : kDisabledMask)
316 | (Value() ? kPressedMask : 0);
320 TransportButton::Draw(BRect)
322 DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
327 TransportButton::StartPressing()
330 if (startPressingMessage)
331 Invoke(startPressingMessage);
333 if (pressingMessage) {
334 ASSERT(pressingMessage);
335 messageSender = PeriodicMessageSender::Launch(Messenger(),
336 pressingMessage, pressingPeriod);
341 TransportButton::MouseCancelPressing()
343 if (!mouseDown || keyDown)
348 if (pressingMessage) {
349 ASSERT(messageSender);
350 PeriodicMessageSender *sender = messageSender;
355 if (donePressingMessage)
356 Invoke(donePressingMessage);
361 TransportButton::DonePressing()
363 if (pressingMessage) {
364 ASSERT(messageSender);
365 PeriodicMessageSender *sender = messageSender;
375 TransportButton::MouseStartPressing()
386 TransportButton::MouseDonePressing()
397 TransportButton::ShortcutKeyDown()
411 TransportButton::ShortcutKeyUp()
423 TransportButton::MouseDown(BPoint)
428 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
430 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
431 MouseStartPressing();
435 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
437 if (IsTracking() && Bounds().Contains(point) != Value()) {
439 MouseStartPressing();
441 MouseCancelPressing();
446 TransportButton::MouseUp(BPoint point)
449 if (Bounds().Contains(point))
452 MouseCancelPressing();
458 TransportButton::SetStartPressingMessage(BMessage *message)
460 delete startPressingMessage;
461 startPressingMessage = message;
465 TransportButton::SetPressingMessage(BMessage *message)
467 delete pressingMessage;
468 pressingMessage = message;
472 TransportButton::SetDonePressingMessage(BMessage *message)
474 delete donePressingMessage;
475 donePressingMessage = message;
479 TransportButton::SetPressingPeriod(bigtime_t newTime)
481 pressingPeriod = newTime;
485 PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
486 const unsigned char *normalBits, const unsigned char *pressedBits,
487 const unsigned char *disabledBits, const unsigned char *normalPlayingBits,
488 const unsigned char *pressedPlayingBits, const unsigned char *normalPausedBits,
489 const unsigned char *pressedPausedBits,
490 BMessage *invokeMessage, uint32 key, uint32 modifiers, uint32 resizeFlags)
491 : TransportButton(frame, name, normalBits, pressedBits,
492 disabledBits, invokeMessage, 0,
493 0, 0, 0, key, modifiers, resizeFlags),
494 normalPlayingBits(normalPlayingBits),
495 pressedPlayingBits(pressedPlayingBits),
496 normalPausedBits(normalPausedBits),
497 pressedPausedBits(pressedPausedBits),
498 state(PlayPauseButton::kStopped),
499 lastPauseBlinkTime(0),
505 PlayPauseButton::SetStopped()
507 if (state == kStopped || state == kAboutToPlay)
515 PlayPauseButton::SetPlaying()
517 if (state == kPlaying || state == kAboutToPause)
524 const bigtime_t kPauseBlinkPeriod = 600000;
527 PlayPauseButton::SetPaused()
529 if (state == kAboutToPlay)
532 // in paused state blink the LED on and off
533 bigtime_t now = system_time();
534 if (state == kPausedLedOn || state == kPausedLedOff) {
535 if (now - lastPauseBlinkTime < kPauseBlinkPeriod)
538 if (state == kPausedLedOn)
539 state = kPausedLedOff;
541 state = kPausedLedOn;
543 state = kPausedLedOn;
545 lastPauseBlinkTime = now;
550 PlayPauseButton::ModeMask() const
553 return kDisabledMask;
558 result = kPressedMask;
560 if (state == kPlaying || state == kAboutToPlay)
561 result |= kPlayingMask;
562 else if (state == kAboutToPause || state == kPausedLedOn)
563 result |= kPausedMask;
568 const unsigned char *
569 PlayPauseButton::BitsForMask(uint32 mask) const
573 return normalPlayingBits;
574 case kPlayingMask | kPressedMask:
575 return pressedPlayingBits;
577 return normalPausedBits;
578 case kPausedMask | kPressedMask:
579 return pressedPausedBits;
581 return _inherited::BitsForMask(mask);
589 PlayPauseButton::StartPressing()
591 if (state == kPlaying)
592 state = kAboutToPause;
594 state = kAboutToPlay;
596 _inherited::StartPressing();
600 PlayPauseButton::MouseCancelPressing()
602 if (state == kAboutToPause)
607 _inherited::MouseCancelPressing();
611 PlayPauseButton::DonePressing()
613 if (state == kAboutToPause) {
614 state = kPausedLedOn;
615 lastPauseBlinkTime = system_time();
616 } else if (state == kAboutToPlay)
619 _inherited::DonePressing();