1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
25 #include <MessageFilter.h>
31 #include "TransportButton.h"
32 #include "DrawingTidbits.h"
35 // Bitmap stash is a simple class to hold all the lazily-allocated
36 // bitmaps that the TransportButton needs when rendering itself.
37 // signature is a combination of the different enabled, pressed, playing, etc.
38 // flavors of a bitmap. If the stash does not have a particular bitmap,
39 // it turns around to ask the button to create one and stores it for next time.
41 BitmapStash(TransportButton *);
43 BBitmap *GetBitmap(uint32 signature);
46 TransportButton *owner;
47 map<uint32, BBitmap *> stash;
50 BitmapStash::BitmapStash(TransportButton *owner)
56 BitmapStash::GetBitmap(uint32 signature)
58 if (stash.find(signature) == stash.end()) {
59 BBitmap *newBits = owner->MakeBitmap(signature);
61 stash[signature] = newBits;
64 return stash[signature];
67 BitmapStash::~BitmapStash()
69 // delete all the bitmaps
70 for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++)
75 class PeriodicMessageSender {
76 // used to send a specified message repeatedly when holding down a button
78 static PeriodicMessageSender *Launch(BMessenger target,
79 const BMessage *message, bigtime_t period);
83 PeriodicMessageSender(BMessenger target, const BMessage *message,
85 ~PeriodicMessageSender() {}
88 static status_t TrackBinder(void *);
100 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
101 const BMessage *message, bigtime_t period)
109 PeriodicMessageSender *
110 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
113 PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
114 thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
115 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
117 if (thread <= 0 || resume_thread(thread) != B_OK) {
118 // didn't start, don't leak self
127 PeriodicMessageSender::Quit()
129 requestToQuit = true;
133 PeriodicMessageSender::TrackBinder(void *castToThis)
135 ((PeriodicMessageSender *)castToThis)->Run();
140 PeriodicMessageSender::Run()
146 target.SendMessage(&message);
151 class SkipButtonKeypressFilter : public BMessageFilter {
153 SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
154 TransportButton *target);
157 filter_result Filter(BMessage *message, BHandler **handler);
161 uint32 shortcutModifier;
162 TransportButton *target;
165 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
166 uint32 shortcutModifier, TransportButton *target)
167 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
168 shortcutKey(shortcutKey),
169 shortcutModifier(shortcutModifier),
175 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
177 if (target->IsEnabled()
178 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
180 uint32 rawKeyChar = 0;
184 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
185 || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
186 || message->FindInt8("byte", (int8 *)&byte) != B_OK
187 || message->FindInt32("key", &key) != B_OK)
188 return B_DISPATCH_MESSAGE;
190 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
191 | B_OPTION_KEY | B_MENU_KEY;
192 // strip caps lock, etc.
194 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
195 if (message->what == B_KEY_DOWN)
196 target->ShortcutKeyDown();
198 target->ShortcutKeyUp();
200 return B_SKIP_MESSAGE;
204 // let others deal with this
205 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, B_WILL_DRAW | B_NAVIGABLE),
216 bitmaps(new BitmapStash(this)),
217 normalBits(normalBits),
218 pressedBits(pressedBits),
219 disabledBits(disabledBits),
220 startPressingMessage(startPressingMessage),
221 pressingMessage(pressingMessage),
222 donePressingMessage(donePressingMessage),
223 pressingPeriod(period),
230 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
235 TransportButton::AttachedToWindow()
237 _inherited::AttachedToWindow();
239 Window()->AddCommonFilter(keyPressFilter);
241 // transparent to reduce flicker
242 SetViewColor(B_TRANSPARENT_COLOR);
246 TransportButton::DetachedFromWindow()
248 if (keyPressFilter) {
249 Window()->RemoveCommonFilter(keyPressFilter);
250 delete keyPressFilter;
252 _inherited::DetachedFromWindow();
256 TransportButton::~TransportButton()
258 delete startPressingMessage;
259 delete pressingMessage;
260 delete donePressingMessage;
265 TransportButton::WindowActivated(bool state)
270 _inherited::WindowActivated(state);
274 TransportButton::SetEnabled(bool on)
276 _inherited::SetEnabled(on);
281 const unsigned char *
282 TransportButton::BitsForMask(uint32 mask) const
300 TransportButton::MakeBitmap(uint32 mask)
302 BBitmap *result = new BBitmap(Bounds(), B_COLOR_8_BIT);
303 result->SetBits(BitsForMask(mask), (Bounds().Width() + 1) * (Bounds().Height() + 1),
306 ReplaceTransparentColor(result, Parent()->ViewColor());
312 TransportButton::ModeMask() const
314 return (IsEnabled() ? 0 : kDisabledMask)
315 | (Value() ? kPressedMask : 0);
319 TransportButton::Draw(BRect)
321 DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
326 TransportButton::StartPressing()
329 if (startPressingMessage)
330 Invoke(startPressingMessage);
332 if (pressingMessage) {
333 ASSERT(pressingMessage);
334 messageSender = PeriodicMessageSender::Launch(Messenger(),
335 pressingMessage, pressingPeriod);
340 TransportButton::MouseCancelPressing()
342 if (!mouseDown || keyDown)
347 if (pressingMessage) {
348 ASSERT(messageSender);
349 PeriodicMessageSender *sender = messageSender;
354 if (donePressingMessage)
355 Invoke(donePressingMessage);
360 TransportButton::DonePressing()
362 if (pressingMessage) {
363 ASSERT(messageSender);
364 PeriodicMessageSender *sender = messageSender;
374 TransportButton::MouseStartPressing()
385 TransportButton::MouseDonePressing()
396 TransportButton::ShortcutKeyDown()
410 TransportButton::ShortcutKeyUp()
422 TransportButton::MouseDown(BPoint)
427 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
429 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
430 MouseStartPressing();
434 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
436 if (IsTracking() && Bounds().Contains(point) != Value()) {
438 MouseStartPressing();
440 MouseCancelPressing();
445 TransportButton::MouseUp(BPoint point)
448 if (Bounds().Contains(point))
451 MouseCancelPressing();
457 TransportButton::SetStartPressingMessage(BMessage *message)
459 delete startPressingMessage;
460 startPressingMessage = message;
464 TransportButton::SetPressingMessage(BMessage *message)
466 delete pressingMessage;
467 pressingMessage = message;
471 TransportButton::SetDonePressingMessage(BMessage *message)
473 delete donePressingMessage;
474 donePressingMessage = message;
478 TransportButton::SetPressingPeriod(bigtime_t newTime)
480 pressingPeriod = newTime;
484 PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
485 const unsigned char *normalBits, const unsigned char *pressedBits,
486 const unsigned char *disabledBits, const unsigned char *normalPlayingBits,
487 const unsigned char *pressedPlayingBits, const unsigned char *normalPausedBits,
488 const unsigned char *pressedPausedBits,
489 BMessage *invokeMessage, uint32 key, uint32 modifiers, uint32 resizeFlags)
490 : TransportButton(frame, name, normalBits, pressedBits,
491 disabledBits, invokeMessage, 0,
492 0, 0, 0, key, modifiers, resizeFlags),
493 normalPlayingBits(normalPlayingBits),
494 pressedPlayingBits(pressedPlayingBits),
495 normalPausedBits(normalPausedBits),
496 pressedPausedBits(pressedPausedBits),
497 state(PlayPauseButton::kStopped),
498 lastPauseBlinkTime(0),
504 PlayPauseButton::SetStopped()
506 if (state == kStopped || state == kAboutToPlay)
514 PlayPauseButton::SetPlaying()
516 if (state == kPlaying || state == kAboutToPause)
523 const bigtime_t kPauseBlinkPeriod = 600000;
526 PlayPauseButton::SetPaused()
528 if (state == kAboutToPlay)
531 // in paused state blink the LED on and off
532 bigtime_t now = system_time();
533 if (state == kPausedLedOn || state == kPausedLedOff) {
534 if (now - lastPauseBlinkTime < kPauseBlinkPeriod)
537 if (state == kPausedLedOn)
538 state = kPausedLedOff;
540 state = kPausedLedOn;
542 state = kPausedLedOn;
544 lastPauseBlinkTime = now;
549 PlayPauseButton::ModeMask() const
552 return kDisabledMask;
557 result = kPressedMask;
559 if (state == kPlaying || state == kAboutToPlay)
560 result |= kPlayingMask;
561 else if (state == kAboutToPause || state == kPausedLedOn)
562 result |= kPausedMask;
567 const unsigned char *
568 PlayPauseButton::BitsForMask(uint32 mask) const
572 return normalPlayingBits;
573 case kPlayingMask | kPressedMask:
574 return pressedPlayingBits;
576 return normalPausedBits;
577 case kPausedMask | kPressedMask:
578 return pressedPausedBits;
580 return _inherited::BitsForMask(mask);
588 PlayPauseButton::StartPressing()
590 if (state == kPlaying)
591 state = kAboutToPause;
593 state = kAboutToPlay;
595 _inherited::StartPressing();
599 PlayPauseButton::MouseCancelPressing()
601 if (state == kAboutToPause)
606 _inherited::MouseCancelPressing();
610 PlayPauseButton::DonePressing()
612 if (state == kAboutToPause) {
613 state = kPausedLedOn;
614 lastPauseBlinkTime = system_time();
615 } else if (state == kAboutToPlay)
618 _inherited::DonePressing();