]> git.sesse.net Git - vlc/blob - modules/gui/beos/TransportButton.cpp
* ./modules/*: moved plugins to the new tree. Yet untested builds include
[vlc] / modules / gui / beos / TransportButton.cpp
1 /*****************************************************************************
2  * TransportButton.cpp
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: TransportButton.cpp,v 1.1 2002/08/04 17:23:43 sam Exp $
6  *
7  * Authors: Tony Castley <tcastley@mail.powerup.com.au>
8  *
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.
13  * 
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.
18  *
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  *****************************************************************************/
23
24 #include <Bitmap.h>
25 #include <Debug.h>
26 #include <MessageFilter.h>
27 #include <Screen.h>
28 #include <Window.h>
29
30 #include <map>
31
32 #include "TransportButton.h"
33 #include "DrawingTidbits.h"
34
35 class BitmapStash {
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.
41 public:
42         BitmapStash(TransportButton *);
43         ~BitmapStash();
44         BBitmap *GetBitmap(uint32 signature);
45         
46 private:
47         TransportButton *owner;
48         map<uint32, BBitmap *> stash;
49 };
50
51 BitmapStash::BitmapStash(TransportButton *owner)
52         :       owner(owner)
53 {
54 }
55
56 BBitmap *
57 BitmapStash::GetBitmap(uint32 signature)
58 {
59         if (stash.find(signature) == stash.end()) {
60                 BBitmap *newBits = owner->MakeBitmap(signature);
61                 ASSERT(newBits);
62                 stash[signature] = newBits;
63         }
64         
65         return stash[signature];
66 }
67
68 BitmapStash::~BitmapStash()
69 {
70         // delete all the bitmaps
71         for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++) 
72                 delete (*i).second;
73 }
74
75
76 class PeriodicMessageSender {
77         // used to send a specified message repeatedly when holding down a button
78 public:
79         static PeriodicMessageSender *Launch(BMessenger target,
80                 const BMessage *message, bigtime_t period);
81         void Quit();
82
83 private:
84         PeriodicMessageSender(BMessenger target, const BMessage *message,
85                 bigtime_t period);
86         ~PeriodicMessageSender() {}
87                 // use quit
88
89         static status_t TrackBinder(void *);
90         void Run();
91         
92         BMessenger target;
93         BMessage message;
94
95         bigtime_t period;
96         
97         bool requestToQuit;
98 };
99
100
101 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
102         const BMessage *message, bigtime_t period)
103         :       target(target),
104                 message(*message),
105                 period(period),
106                 requestToQuit(false)
107 {
108 }
109
110 PeriodicMessageSender *
111 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
112         bigtime_t period)
113 {
114         PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
115         thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
116                 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
117         
118         if (thread <= 0 || resume_thread(thread) != B_OK) {
119                 // didn't start, don't leak self
120                 delete result;
121                 result = 0;
122         }
123
124         return result;
125 }
126
127 void 
128 PeriodicMessageSender::Quit()
129 {
130         requestToQuit = true;
131 }
132
133 status_t 
134 PeriodicMessageSender::TrackBinder(void *castToThis)
135 {
136         ((PeriodicMessageSender *)castToThis)->Run();
137         return 0;
138 }
139
140 void 
141 PeriodicMessageSender::Run()
142 {
143         for (;;) {
144                 snooze(period);
145                 if (requestToQuit)
146                         break;
147                 target.SendMessage(&message);
148         }
149         delete this;
150 }
151
152 class SkipButtonKeypressFilter : public BMessageFilter {
153 public:
154         SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
155                 TransportButton *target);
156
157 protected:
158         filter_result Filter(BMessage *message, BHandler **handler);
159
160 private:
161         uint32 shortcutKey;
162         uint32 shortcutModifier;
163         TransportButton *target;
164 };
165
166 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
167         uint32 shortcutModifier, TransportButton *target)
168         :       BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
169                 shortcutKey(shortcutKey),
170                 shortcutModifier(shortcutModifier),
171                 target(target)
172 {
173 }
174
175 filter_result 
176 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
177 {
178         if (target->IsEnabled()
179                 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
180                 uint32 modifiers;
181                 uint32 rawKeyChar = 0;
182                 uint8 byte = 0;
183                 int32 key = 0;
184                 
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;
190
191                 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
192                         | B_OPTION_KEY | B_MENU_KEY;
193                         // strip caps lock, etc.
194
195                 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
196                         if (message->what == B_KEY_DOWN)
197                                 target->ShortcutKeyDown();
198                         else
199                                 target->ShortcutKeyUp();
200                         
201                         return B_SKIP_MESSAGE;
202                 }
203         }
204
205         // let others deal with this
206         return B_DISPATCH_MESSAGE;
207 }
208
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),
225                 mouseDown(false),
226                 keyDown(false),
227                 messageSender(0),
228                 keyPressFilter(0)
229 {
230         if (key)
231                 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
232 }
233
234
235 void 
236 TransportButton::AttachedToWindow()
237 {
238         _inherited::AttachedToWindow();
239         if (keyPressFilter)
240                 Window()->AddCommonFilter(keyPressFilter);
241         
242         // transparent to reduce flicker
243         SetViewColor(B_TRANSPARENT_COLOR);
244 }
245
246 void 
247 TransportButton::DetachedFromWindow()
248 {
249         if (keyPressFilter) {
250                 Window()->RemoveCommonFilter(keyPressFilter);
251                 delete keyPressFilter;
252         }
253         _inherited::DetachedFromWindow();
254 }
255
256
257 TransportButton::~TransportButton()
258 {
259         delete startPressingMessage;
260         delete pressingMessage;
261         delete donePressingMessage;
262         delete bitmaps;
263 }
264
265 void 
266 TransportButton::WindowActivated(bool state)
267 {
268         if (!state)
269                 ShortcutKeyUp();
270         
271         _inherited::WindowActivated(state);
272 }
273
274 void 
275 TransportButton::SetEnabled(bool on)
276 {
277         _inherited::SetEnabled(on);
278         if (!on)
279                 ShortcutKeyUp();        
280 }
281
282 const unsigned char *
283 TransportButton::BitsForMask(uint32 mask) const
284 {
285         switch (mask) {
286                 case 0:
287                         return normalBits;
288                 case kDisabledMask:
289                         return disabledBits;
290                 case kPressedMask:
291                         return pressedBits;
292                 default:
293                         break;
294         }       
295         TRESPASS();
296         return 0;
297 }
298
299
300 BBitmap *
301 TransportButton::MakeBitmap(uint32 mask)
302 {
303         BBitmap *result = new BBitmap(Bounds(), B_COLOR_8_BIT);
304         result->SetBits(BitsForMask(mask), (Bounds().Width() + 1) * (Bounds().Height() + 1),
305                 0, B_COLOR_8_BIT);
306
307         ReplaceTransparentColor(result, Parent()->ViewColor());
308         
309         return result;
310 }
311
312 uint32 
313 TransportButton::ModeMask() const
314 {
315         return (IsEnabled() ? 0 : kDisabledMask)
316                 | (Value() ? kPressedMask : 0);
317 }
318
319 void 
320 TransportButton::Draw(BRect)
321 {
322         DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
323 }
324
325
326 void 
327 TransportButton::StartPressing()
328 {
329         SetValue(1);
330         if (startPressingMessage)
331                 Invoke(startPressingMessage);
332         
333         if (pressingMessage) {
334                 ASSERT(pressingMessage);
335                 messageSender = PeriodicMessageSender::Launch(Messenger(),
336                         pressingMessage, pressingPeriod);
337         }
338 }
339
340 void 
341 TransportButton::MouseCancelPressing()
342 {
343         if (!mouseDown || keyDown)
344                 return;
345
346         mouseDown = false;
347
348         if (pressingMessage) {
349                 ASSERT(messageSender);
350                 PeriodicMessageSender *sender = messageSender;
351                 messageSender = 0;
352                 sender->Quit();
353         }
354
355         if (donePressingMessage)
356                 Invoke(donePressingMessage);
357         SetValue(0);
358 }
359
360 void 
361 TransportButton::DonePressing()
362 {       
363         if (pressingMessage) {
364                 ASSERT(messageSender);
365                 PeriodicMessageSender *sender = messageSender;
366                 messageSender = 0;
367                 sender->Quit();
368         }
369
370         Invoke();
371         SetValue(0);
372 }
373
374 void 
375 TransportButton::MouseStartPressing()
376 {
377         if (mouseDown)
378                 return;
379         
380         mouseDown = true;
381         if (!keyDown)
382                 StartPressing();
383 }
384
385 void 
386 TransportButton::MouseDonePressing()
387 {
388         if (!mouseDown)
389                 return;
390         
391         mouseDown = false;
392         if (!keyDown)
393                 DonePressing();
394 }
395
396 void 
397 TransportButton::ShortcutKeyDown()
398 {
399         if (!IsEnabled())
400                 return;
401
402         if (keyDown)
403                 return;
404         
405         keyDown = true;
406         if (!mouseDown)
407                 StartPressing();
408 }
409
410 void 
411 TransportButton::ShortcutKeyUp()
412 {
413         if (!keyDown)
414                 return;
415         
416         keyDown = false;
417         if (!mouseDown)
418                 DonePressing();
419 }
420
421
422 void 
423 TransportButton::MouseDown(BPoint)
424 {
425         if (!IsEnabled())
426                 return;
427
428         ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
429         SetTracking(true);
430         SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
431         MouseStartPressing();
432 }
433
434 void 
435 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
436 {
437         if (IsTracking() && Bounds().Contains(point) != Value()) {
438                 if (!Value())
439                         MouseStartPressing();
440                 else
441                         MouseCancelPressing();
442         }
443 }
444
445 void 
446 TransportButton::MouseUp(BPoint point)
447 {
448         if (IsTracking()) {
449                 if (Bounds().Contains(point))
450                         MouseDonePressing();
451                 else
452                         MouseCancelPressing();
453                 SetTracking(false);
454         }
455 }
456
457 void 
458 TransportButton::SetStartPressingMessage(BMessage *message)
459 {
460         delete startPressingMessage;
461         startPressingMessage = message;
462 }
463
464 void 
465 TransportButton::SetPressingMessage(BMessage *message)
466 {
467         delete pressingMessage;
468         pressingMessage = message;
469 }
470
471 void 
472 TransportButton::SetDonePressingMessage(BMessage *message)
473 {
474         delete donePressingMessage;
475         donePressingMessage = message;
476 }
477
478 void 
479 TransportButton::SetPressingPeriod(bigtime_t newTime)
480 {
481         pressingPeriod = newTime;
482 }
483
484
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),
500                 lastModeMask(0)
501 {
502 }
503
504 void 
505 PlayPauseButton::SetStopped()
506 {
507         if (state == kStopped || state == kAboutToPlay)
508                 return;
509         
510         state = kStopped;
511         Invalidate();
512 }
513
514 void 
515 PlayPauseButton::SetPlaying()
516 {
517         if (state == kPlaying || state == kAboutToPause)
518                 return;
519         
520         state = kPlaying;
521         Invalidate();
522 }
523
524 const bigtime_t kPauseBlinkPeriod = 600000;
525
526 void 
527 PlayPauseButton::SetPaused()
528 {
529         if (state == kAboutToPlay)
530                 return;
531
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)
536                         return;
537                 
538                 if (state == kPausedLedOn)
539                         state = kPausedLedOff;
540                 else
541                         state = kPausedLedOn;
542         } else
543                 state = kPausedLedOn;
544         
545         lastPauseBlinkTime = now;
546         Invalidate();
547 }
548
549 uint32 
550 PlayPauseButton::ModeMask() const
551 {
552         if (!IsEnabled())
553                 return kDisabledMask;
554         
555         uint32 result = 0;
556
557         if (Value())
558                 result = kPressedMask;
559
560         if (state == kPlaying || state == kAboutToPlay)
561                 result |= kPlayingMask;
562         else if (state == kAboutToPause || state == kPausedLedOn)               
563                 result |= kPausedMask;
564         
565         return result;
566 }
567
568 const unsigned char *
569 PlayPauseButton::BitsForMask(uint32 mask) const
570 {
571         switch (mask) {
572                 case kPlayingMask:
573                         return normalPlayingBits;
574                 case kPlayingMask | kPressedMask:
575                         return pressedPlayingBits;
576                 case kPausedMask:
577                         return normalPausedBits;
578                 case kPausedMask | kPressedMask:
579                         return pressedPausedBits;
580                 default:
581                         return _inherited::BitsForMask(mask);
582         }       
583         TRESPASS();
584         return 0;
585 }
586
587
588 void 
589 PlayPauseButton::StartPressing()
590 {
591         if (state == kPlaying)
592                 state = kAboutToPause;
593         else
594                 state = kAboutToPlay;
595         
596         _inherited::StartPressing();
597 }
598
599 void 
600 PlayPauseButton::MouseCancelPressing()
601 {
602         if (state == kAboutToPause)
603                 state = kPlaying;
604         else
605                 state = kStopped;
606         
607         _inherited::MouseCancelPressing();
608 }
609
610 void 
611 PlayPauseButton::DonePressing()
612 {
613         if (state == kAboutToPause) {
614                 state = kPausedLedOn;
615                 lastPauseBlinkTime = system_time();
616         } else if (state == kAboutToPlay)
617                 state = kPlaying;
618         
619         _inherited::DonePressing();
620 }
621
622