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