1 /*****************************************************************************
3 *****************************************************************************
4 * Copyright (C) 2001 the VideoLAN team
7 * Authors: Tony Castley <tcastley@mail.powerup.com.au>
8 * Stephan Aßmus <stippi@yellowbites.com>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
27 #include <MessageFilter.h>
33 #include "TransportButton.h"
34 #include "DrawingTidbits.h"
37 // Bitmap stash is a simple class to hold all the lazily-allocated
38 // bitmaps that the TransportButton needs when rendering itself.
39 // signature is a combination of the different enabled, pressed, playing, etc.
40 // flavors of a bitmap. If the stash does not have a particular bitmap,
41 // it turns around to ask the button to create one and stores it for next time.
43 BitmapStash(TransportButton *);
45 BBitmap *GetBitmap(uint32 signature);
48 TransportButton *owner;
49 map<uint32, BBitmap *> stash;
52 BitmapStash::BitmapStash(TransportButton *owner)
58 BitmapStash::GetBitmap(uint32 signature)
60 if (stash.find(signature) == stash.end()) {
61 BBitmap *newBits = owner->MakeBitmap(signature);
63 stash[signature] = newBits;
66 return stash[signature];
69 BitmapStash::~BitmapStash()
71 // delete all the bitmaps
72 for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++)
77 class PeriodicMessageSender {
78 // used to send a specified message repeatedly when holding down a button
80 static PeriodicMessageSender *Launch(BMessenger target,
81 const BMessage *message, bigtime_t period);
85 PeriodicMessageSender(BMessenger target, const BMessage *message,
87 ~PeriodicMessageSender() {}
90 static status_t TrackBinder(void *);
102 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
103 const BMessage *message, bigtime_t period)
111 PeriodicMessageSender *
112 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
115 PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
116 thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
117 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
119 if (thread <= 0 || resume_thread(thread) != B_OK) {
120 // didn't start, don't leak self
129 PeriodicMessageSender::Quit()
131 requestToQuit = true;
135 PeriodicMessageSender::TrackBinder(void *castToThis)
137 ((PeriodicMessageSender *)castToThis)->Run();
142 PeriodicMessageSender::Run()
148 target.SendMessage(&message);
153 class SkipButtonKeypressFilter : public BMessageFilter {
155 SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
156 TransportButton *target);
159 filter_result Filter(BMessage *message, BHandler **handler);
163 uint32 shortcutModifier;
164 TransportButton *target;
167 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
168 uint32 shortcutModifier, TransportButton *target)
169 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
170 shortcutKey(shortcutKey),
171 shortcutModifier(shortcutModifier),
177 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
179 if (target->IsEnabled()
180 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
182 uint32 rawKeyChar = 0;
186 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
187 || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
188 || message->FindInt8("byte", (int8 *)&byte) != B_OK
189 || message->FindInt32("key", &key) != B_OK)
190 return B_DISPATCH_MESSAGE;
192 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
193 | B_OPTION_KEY | B_MENU_KEY;
194 // strip caps lock, etc.
196 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
197 if (message->what == B_KEY_DOWN)
198 target->ShortcutKeyDown();
200 target->ShortcutKeyUp();
202 return B_SKIP_MESSAGE;
206 // let others deal with this
207 return B_DISPATCH_MESSAGE;
210 TransportButton::TransportButton(BRect frame, const char *name,
211 const unsigned char *normalBits,
212 const unsigned char *pressedBits,
213 const unsigned char *disabledBits,
214 BMessage *invokeMessage, BMessage *startPressingMessage,
215 BMessage *pressingMessage, BMessage *donePressingMessage, bigtime_t period,
216 uint32 key, uint32 modifiers, uint32 resizeFlags)
217 : BControl(frame, name, "", invokeMessage, resizeFlags, B_WILL_DRAW | B_NAVIGABLE),
218 bitmaps(new BitmapStash(this)),
219 normalBits(normalBits),
220 pressedBits(pressedBits),
221 disabledBits(disabledBits),
222 startPressingMessage(startPressingMessage),
223 pressingMessage(pressingMessage),
224 donePressingMessage(donePressingMessage),
225 pressingPeriod(period),
232 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
237 TransportButton::AttachedToWindow()
239 _inherited::AttachedToWindow();
241 Window()->AddCommonFilter(keyPressFilter);
243 // transparent to reduce flicker
244 SetViewColor(B_TRANSPARENT_COLOR);
248 TransportButton::DetachedFromWindow()
251 Window()->RemoveCommonFilter(keyPressFilter);
252 _inherited::DetachedFromWindow();
256 TransportButton::~TransportButton()
258 delete startPressingMessage;
259 delete pressingMessage;
260 delete donePressingMessage;
262 delete keyPressFilter;
266 TransportButton::WindowActivated(bool state)
271 _inherited::WindowActivated(state);
275 TransportButton::SetEnabled(bool on)
277 if (on != IsEnabled()) {
278 _inherited::SetEnabled(on);
284 const unsigned char *
285 TransportButton::BitsForMask(uint32 mask) const
303 TransportButton::MakeBitmap(uint32 mask)
306 BBitmap *result = new BBitmap(r, B_CMAP8);
308 uint8* src = (uint8*)BitsForMask(mask);
310 if (src && result && result->IsValid()) {
311 // int32 width = r.IntegerWidth() + 1;
312 int32 height = r.IntegerHeight() + 1;
313 int32 bpr = result->BytesPerRow();
314 uint8* dst = (uint8*)result->Bits();
315 // copy source bits into bitmap line by line,
316 // taking possible alignment into account
317 // since the source data has been generated
318 // by QuickRes, it still contains aligment too
319 // (hence skipping bpr and not width bytes)
320 for (int32 y = 0; y < height; y++) {
321 memcpy(dst, src, bpr);
325 ReplaceTransparentColor(result, Parent()->ViewColor());
335 TransportButton::ModeMask() const
337 return (IsEnabled() ? 0 : kDisabledMask)
338 | (Value() ? kPressedMask : 0);
342 TransportButton::Draw(BRect)
344 DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
349 TransportButton::StartPressing()
352 if (startPressingMessage)
353 Invoke(startPressingMessage);
355 if (pressingMessage) {
356 ASSERT(pressingMessage);
357 messageSender = PeriodicMessageSender::Launch(Messenger(),
358 pressingMessage, pressingPeriod);
363 TransportButton::MouseCancelPressing()
365 if (!mouseDown || keyDown)
370 if (pressingMessage) {
371 ASSERT(messageSender);
372 PeriodicMessageSender *sender = messageSender;
377 if (donePressingMessage)
378 Invoke(donePressingMessage);
383 TransportButton::DonePressing()
385 if (pressingMessage) {
386 ASSERT(messageSender);
387 PeriodicMessageSender *sender = messageSender;
397 TransportButton::MouseStartPressing()
408 TransportButton::MouseDonePressing()
419 TransportButton::ShortcutKeyDown()
433 TransportButton::ShortcutKeyUp()
445 TransportButton::MouseDown(BPoint)
450 ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
452 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
453 MouseStartPressing();
457 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
459 if (IsTracking() && Bounds().Contains(point) != Value()) {
461 MouseStartPressing();
463 MouseCancelPressing();
468 TransportButton::MouseUp(BPoint point)
471 if (Bounds().Contains(point))
474 MouseCancelPressing();
480 TransportButton::SetStartPressingMessage(BMessage *message)
482 delete startPressingMessage;
483 startPressingMessage = message;
487 TransportButton::SetPressingMessage(BMessage *message)
489 delete pressingMessage;
490 pressingMessage = message;
494 TransportButton::SetDonePressingMessage(BMessage *message)
496 delete donePressingMessage;
497 donePressingMessage = message;
501 TransportButton::SetPressingPeriod(bigtime_t newTime)
503 pressingPeriod = newTime;
507 PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
508 const unsigned char *normalBits, const unsigned char *pressedBits,
509 const unsigned char *disabledBits, const unsigned char *normalPlayingBits,
510 const unsigned char *pressedPlayingBits, const unsigned char *normalPausedBits,
511 const unsigned char *pressedPausedBits,
512 BMessage *invokeMessage, uint32 key, uint32 modifiers, uint32 resizeFlags)
513 : TransportButton(frame, name, normalBits, pressedBits,
514 disabledBits, invokeMessage, 0,
515 0, 0, 0, key, modifiers, resizeFlags),
516 normalPlayingBits(normalPlayingBits),
517 pressedPlayingBits(pressedPlayingBits),
518 normalPausedBits(normalPausedBits),
519 pressedPausedBits(pressedPausedBits),
520 state(PlayPauseButton::kStopped),
521 lastPauseBlinkTime(0),
527 PlayPauseButton::SetStopped()
529 if (state == kStopped || state == kAboutToPlay)
537 PlayPauseButton::SetPlaying()
539 if (state == kPlaying || state == kAboutToPause)
546 const bigtime_t kPauseBlinkPeriod = 600000;
549 PlayPauseButton::SetPaused()
551 if (state == kAboutToPlay)
554 // in paused state blink the LED on and off
555 bigtime_t now = system_time();
556 if (state == kPausedLedOn || state == kPausedLedOff) {
557 if (now - lastPauseBlinkTime < kPauseBlinkPeriod)
560 if (state == kPausedLedOn)
561 state = kPausedLedOff;
563 state = kPausedLedOn;
565 state = kPausedLedOn;
567 lastPauseBlinkTime = now;
572 PlayPauseButton::ModeMask() const
575 return kDisabledMask;
580 result = kPressedMask;
582 if (state == kPlaying || state == kAboutToPlay)
583 result |= kPlayingMask;
584 else if (state == kAboutToPause || state == kPausedLedOn)
585 result |= kPausedMask;
590 const unsigned char *
591 PlayPauseButton::BitsForMask(uint32 mask) const
595 return normalPlayingBits;
596 case kPlayingMask | kPressedMask:
597 return pressedPlayingBits;
599 return normalPausedBits;
600 case kPausedMask | kPressedMask:
601 return pressedPausedBits;
603 return _inherited::BitsForMask(mask);
611 PlayPauseButton::StartPressing()
613 if (state == kPlaying)
614 state = kAboutToPause;
616 state = kAboutToPlay;
618 _inherited::StartPressing();
622 PlayPauseButton::MouseCancelPressing()
624 if (state == kAboutToPause)
629 _inherited::MouseCancelPressing();
633 PlayPauseButton::DonePressing()
635 if (state == kAboutToPause) {
636 state = kPausedLedOn;
637 lastPauseBlinkTime = system_time();
638 } else if (state == kAboutToPlay)
641 _inherited::DonePressing();