]> git.sesse.net Git - vlc/blob - modules/gui/beos/TransportButton.cpp
Removes trailing spaces. Removes tabs.
[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