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