Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

TransportButton.cpp

00001 /*****************************************************************************
00002  * TransportButton.cpp
00003  *****************************************************************************
00004  * Copyright (C) 2001 the VideoLAN team
00005  * $Id: TransportButton.cpp 11664 2005-07-09 06:17:09Z courmisch $
00006  *
00007  * Authors: Tony Castley <[email protected]>
00008  *          Stephan Aßmus <[email protected]>
00009  *
00010  * This program is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version.
00014  *
00015  * This program is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License
00021  * along with this program; if not, write to the Free Software
00022  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00023  *****************************************************************************/
00024 
00025 #include <Bitmap.h>
00026 #include <Debug.h>
00027 #include <MessageFilter.h>
00028 #include <Screen.h>
00029 #include <Window.h>
00030 
00031 #include <map>
00032 
00033 #include "TransportButton.h"
00034 #include "DrawingTidbits.h"
00035 
00036 class BitmapStash {
00037 // Bitmap stash is a simple class to hold all the lazily-allocated
00038 // bitmaps that the TransportButton needs when rendering itself.
00039 // signature is a combination of the different enabled, pressed, playing, etc.
00040 // flavors of a bitmap. If the stash does not have a particular bitmap,
00041 // it turns around to ask the button to create one and stores it for next time.
00042 public:
00043         BitmapStash(TransportButton *);
00044         ~BitmapStash();
00045         BBitmap *GetBitmap(uint32 signature);
00046         
00047 private:
00048         TransportButton *owner;
00049         map<uint32, BBitmap *> stash;
00050 };
00051 
00052 BitmapStash::BitmapStash(TransportButton *owner)
00053         :       owner(owner)
00054 {
00055 }
00056 
00057 BBitmap *
00058 BitmapStash::GetBitmap(uint32 signature)
00059 {
00060         if (stash.find(signature) == stash.end()) {
00061                 BBitmap *newBits = owner->MakeBitmap(signature);
00062                 ASSERT(newBits);
00063                 stash[signature] = newBits;
00064         }
00065         
00066         return stash[signature];
00067 }
00068 
00069 BitmapStash::~BitmapStash()
00070 {
00071         // delete all the bitmaps
00072         for (map<uint32, BBitmap *>::iterator i = stash.begin(); i != stash.end(); i++) 
00073                 delete (*i).second;
00074 }
00075 
00076 
00077 class PeriodicMessageSender {
00078         // used to send a specified message repeatedly when holding down a button
00079 public:
00080         static PeriodicMessageSender *Launch(BMessenger target,
00081                 const BMessage *message, bigtime_t period);
00082         void Quit();
00083 
00084 private:
00085         PeriodicMessageSender(BMessenger target, const BMessage *message,
00086                 bigtime_t period);
00087         ~PeriodicMessageSender() {}
00088                 // use quit
00089 
00090         static status_t TrackBinder(void *);
00091         void Run();
00092         
00093         BMessenger target;
00094         BMessage message;
00095 
00096         bigtime_t period;
00097         
00098         bool requestToQuit;
00099 };
00100 
00101 
00102 PeriodicMessageSender::PeriodicMessageSender(BMessenger target,
00103         const BMessage *message, bigtime_t period)
00104         :       target(target),
00105                 message(*message),
00106                 period(period),
00107                 requestToQuit(false)
00108 {
00109 }
00110 
00111 PeriodicMessageSender *
00112 PeriodicMessageSender::Launch(BMessenger target, const BMessage *message,
00113         bigtime_t period)
00114 {
00115         PeriodicMessageSender *result = new PeriodicMessageSender(target, message, period);
00116         thread_id thread = spawn_thread(&PeriodicMessageSender::TrackBinder,
00117                 "ButtonRepeatingThread", B_NORMAL_PRIORITY, result);
00118         
00119         if (thread <= 0 || resume_thread(thread) != B_OK) {
00120                 // didn't start, don't leak self
00121                 delete result;
00122                 result = 0;
00123         }
00124 
00125         return result;
00126 }
00127 
00128 void 
00129 PeriodicMessageSender::Quit()
00130 {
00131         requestToQuit = true;
00132 }
00133 
00134 status_t 
00135 PeriodicMessageSender::TrackBinder(void *castToThis)
00136 {
00137         ((PeriodicMessageSender *)castToThis)->Run();
00138         return 0;
00139 }
00140 
00141 void 
00142 PeriodicMessageSender::Run()
00143 {
00144         for (;;) {
00145                 snooze(period);
00146                 if (requestToQuit)
00147                         break;
00148                 target.SendMessage(&message);
00149         }
00150         delete this;
00151 }
00152 
00153 class SkipButtonKeypressFilter : public BMessageFilter {
00154 public:
00155         SkipButtonKeypressFilter(uint32 shortcutKey, uint32 shortcutModifier,
00156                 TransportButton *target);
00157 
00158 protected:
00159         filter_result Filter(BMessage *message, BHandler **handler);
00160 
00161 private:
00162         uint32 shortcutKey;
00163         uint32 shortcutModifier;
00164         TransportButton *target;
00165 };
00166 
00167 SkipButtonKeypressFilter::SkipButtonKeypressFilter(uint32 shortcutKey,
00168         uint32 shortcutModifier, TransportButton *target)
00169         :       BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
00170                 shortcutKey(shortcutKey),
00171                 shortcutModifier(shortcutModifier),
00172                 target(target)
00173 {
00174 }
00175 
00176 filter_result 
00177 SkipButtonKeypressFilter::Filter(BMessage *message, BHandler **handler)
00178 {
00179         if (target->IsEnabled()
00180                 && (message->what == B_KEY_DOWN || message->what == B_KEY_UP)) {
00181                 uint32 modifiers;
00182                 uint32 rawKeyChar = 0;
00183                 uint8 byte = 0;
00184                 int32 key = 0;
00185                 
00186                 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
00187                         || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
00188                         || message->FindInt8("byte", (int8 *)&byte) != B_OK
00189                         || message->FindInt32("key", &key) != B_OK)
00190                         return B_DISPATCH_MESSAGE;
00191 
00192                 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
00193                         | B_OPTION_KEY | B_MENU_KEY;
00194                         // strip caps lock, etc.
00195 
00196                 if (modifiers == shortcutModifier && rawKeyChar == shortcutKey) {
00197                         if (message->what == B_KEY_DOWN)
00198                                 target->ShortcutKeyDown();
00199                         else
00200                                 target->ShortcutKeyUp();
00201                         
00202                         return B_SKIP_MESSAGE;
00203                 }
00204         }
00205 
00206         // let others deal with this
00207         return B_DISPATCH_MESSAGE;
00208 }
00209 
00210 TransportButton::TransportButton(BRect frame, const char *name,
00211         const unsigned char *normalBits,
00212         const unsigned char *pressedBits,
00213         const unsigned char *disabledBits,
00214         BMessage *invokeMessage, BMessage *startPressingMessage,
00215         BMessage *pressingMessage, BMessage *donePressingMessage, bigtime_t period,
00216         uint32 key, uint32 modifiers, uint32 resizeFlags)
00217         :       BControl(frame, name, "", invokeMessage, resizeFlags, B_WILL_DRAW | B_NAVIGABLE),
00218                 bitmaps(new BitmapStash(this)),
00219                 normalBits(normalBits),
00220                 pressedBits(pressedBits),
00221                 disabledBits(disabledBits),
00222                 startPressingMessage(startPressingMessage),
00223                 pressingMessage(pressingMessage),
00224                 donePressingMessage(donePressingMessage),
00225                 pressingPeriod(period),
00226                 mouseDown(false),
00227                 keyDown(false),
00228                 messageSender(0),
00229                 keyPressFilter(0)
00230 {
00231         if (key)
00232                 keyPressFilter = new SkipButtonKeypressFilter(key, modifiers, this);
00233 }
00234 
00235 
00236 void 
00237 TransportButton::AttachedToWindow()
00238 {
00239         _inherited::AttachedToWindow();
00240         if (keyPressFilter)
00241                 Window()->AddCommonFilter(keyPressFilter);
00242         
00243         // transparent to reduce flicker
00244         SetViewColor(B_TRANSPARENT_COLOR);
00245 }
00246 
00247 void 
00248 TransportButton::DetachedFromWindow()
00249 {
00250         if (keyPressFilter)
00251                 Window()->RemoveCommonFilter(keyPressFilter);
00252         _inherited::DetachedFromWindow();
00253 }
00254 
00255 
00256 TransportButton::~TransportButton()
00257 {
00258         delete startPressingMessage;
00259         delete pressingMessage;
00260         delete donePressingMessage;
00261         delete bitmaps;
00262         delete keyPressFilter;
00263 }
00264 
00265 void 
00266 TransportButton::WindowActivated(bool state)
00267 {
00268         if (!state)
00269                 ShortcutKeyUp();
00270         
00271         _inherited::WindowActivated(state);
00272 }
00273 
00274 void 
00275 TransportButton::SetEnabled(bool on)
00276 {
00277         if (on != IsEnabled()) {
00278                 _inherited::SetEnabled(on);
00279                 if (!on)
00280                         ShortcutKeyUp();
00281         }       
00282 }
00283 
00284 const unsigned char *
00285 TransportButton::BitsForMask(uint32 mask) const
00286 {
00287         switch (mask) {
00288                 case 0:
00289                         return normalBits;
00290                 case kDisabledMask:
00291                         return disabledBits;
00292                 case kPressedMask:
00293                         return pressedBits;
00294                 default:
00295                         break;
00296         }       
00297         TRESPASS();
00298         return 0;
00299 }
00300 
00301 
00302 BBitmap *
00303 TransportButton::MakeBitmap(uint32 mask)
00304 {
00305         BRect r(Bounds());
00306         BBitmap *result = new BBitmap(r, B_CMAP8);
00307 
00308         uint8* src = (uint8*)BitsForMask(mask);
00309 
00310         if (src && result && result->IsValid()) {
00311                 // int32 width = r.IntegerWidth() + 1;
00312                 int32 height = r.IntegerHeight() + 1;
00313                 int32 bpr = result->BytesPerRow();
00314                 uint8* dst = (uint8*)result->Bits();
00315                 // copy source bits into bitmap line by line,
00316                 // taking possible alignment into account
00317                 // since the source data has been generated
00318                 // by QuickRes, it still contains aligment too
00319                 // (hence skipping bpr and not width bytes)
00320                 for (int32 y = 0; y < height; y++) {
00321                         memcpy(dst, src, bpr);
00322                         src += bpr;
00323                         dst += bpr;
00324                 }
00325                 ReplaceTransparentColor(result, Parent()->ViewColor());
00326         } else {
00327                 delete result;
00328                 result = NULL;
00329         }
00330         
00331         return result;
00332 }
00333 
00334 uint32 
00335 TransportButton::ModeMask() const
00336 {
00337         return (IsEnabled() ? 0 : kDisabledMask)
00338                 | (Value() ? kPressedMask : 0);
00339 }
00340 
00341 void 
00342 TransportButton::Draw(BRect)
00343 {
00344         DrawBitmapAsync(bitmaps->GetBitmap(ModeMask()));
00345 }
00346 
00347 
00348 void 
00349 TransportButton::StartPressing()
00350 {
00351         SetValue(1);
00352         if (startPressingMessage)
00353                 Invoke(startPressingMessage);
00354         
00355         if (pressingMessage) {
00356                 ASSERT(pressingMessage);
00357                 messageSender = PeriodicMessageSender::Launch(Messenger(),
00358                         pressingMessage, pressingPeriod);
00359         }
00360 }
00361 
00362 void 
00363 TransportButton::MouseCancelPressing()
00364 {
00365         if (!mouseDown || keyDown)
00366                 return;
00367 
00368         mouseDown = false;
00369 
00370         if (pressingMessage) {
00371                 ASSERT(messageSender);
00372                 PeriodicMessageSender *sender = messageSender;
00373                 messageSender = 0;
00374                 sender->Quit();
00375         }
00376 
00377         if (donePressingMessage)
00378                 Invoke(donePressingMessage);
00379         SetValue(0);
00380 }
00381 
00382 void 
00383 TransportButton::DonePressing()
00384 {       
00385         if (pressingMessage) {
00386                 ASSERT(messageSender);
00387                 PeriodicMessageSender *sender = messageSender;
00388                 messageSender = 0;
00389                 sender->Quit();
00390         }
00391 
00392         Invoke();
00393         SetValue(0);
00394 }
00395 
00396 void 
00397 TransportButton::MouseStartPressing()
00398 {
00399         if (mouseDown)
00400                 return;
00401         
00402         mouseDown = true;
00403         if (!keyDown)
00404                 StartPressing();
00405 }
00406 
00407 void 
00408 TransportButton::MouseDonePressing()
00409 {
00410         if (!mouseDown)
00411                 return;
00412         
00413         mouseDown = false;
00414         if (!keyDown)
00415                 DonePressing();
00416 }
00417 
00418 void 
00419 TransportButton::ShortcutKeyDown()
00420 {
00421         if (!IsEnabled())
00422                 return;
00423 
00424         if (keyDown)
00425                 return;
00426         
00427         keyDown = true;
00428         if (!mouseDown)
00429                 StartPressing();
00430 }
00431 
00432 void 
00433 TransportButton::ShortcutKeyUp()
00434 {
00435         if (!keyDown)
00436                 return;
00437         
00438         keyDown = false;
00439         if (!mouseDown)
00440                 DonePressing();
00441 }
00442 
00443 
00444 void 
00445 TransportButton::MouseDown(BPoint)
00446 {
00447         if (!IsEnabled())
00448                 return;
00449 
00450         ASSERT(Window()->Flags() & B_ASYNCHRONOUS_CONTROLS);
00451         SetTracking(true);
00452         SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
00453         MouseStartPressing();
00454 }
00455 
00456 void 
00457 TransportButton::MouseMoved(BPoint point, uint32 code, const BMessage *)
00458 {
00459         if (IsTracking() && Bounds().Contains(point) != Value()) {
00460                 if (!Value())
00461                         MouseStartPressing();
00462                 else
00463                         MouseCancelPressing();
00464         }
00465 }
00466 
00467 void 
00468 TransportButton::MouseUp(BPoint point)
00469 {
00470         if (IsTracking()) {
00471                 if (Bounds().Contains(point))
00472                         MouseDonePressing();
00473                 else
00474                         MouseCancelPressing();
00475                 SetTracking(false);
00476         }
00477 }
00478 
00479 void 
00480 TransportButton::SetStartPressingMessage(BMessage *message)
00481 {
00482         delete startPressingMessage;
00483         startPressingMessage = message;
00484 }
00485 
00486 void 
00487 TransportButton::SetPressingMessage(BMessage *message)
00488 {
00489         delete pressingMessage;
00490         pressingMessage = message;
00491 }
00492 
00493 void 
00494 TransportButton::SetDonePressingMessage(BMessage *message)
00495 {
00496         delete donePressingMessage;
00497         donePressingMessage = message;
00498 }
00499 
00500 void 
00501 TransportButton::SetPressingPeriod(bigtime_t newTime)
00502 {
00503         pressingPeriod = newTime;
00504 }
00505 
00506 
00507 PlayPauseButton::PlayPauseButton(BRect frame, const char *name,
00508         const unsigned char *normalBits, const unsigned char *pressedBits,
00509         const unsigned char *disabledBits, const unsigned char *normalPlayingBits,
00510         const unsigned char *pressedPlayingBits, const unsigned char *normalPausedBits,
00511         const unsigned char *pressedPausedBits,
00512         BMessage *invokeMessage, uint32 key, uint32 modifiers, uint32 resizeFlags)
00513         :       TransportButton(frame, name, normalBits, pressedBits,
00514                         disabledBits, invokeMessage, 0,
00515                         0, 0, 0, key, modifiers, resizeFlags),
00516                 normalPlayingBits(normalPlayingBits),
00517                 pressedPlayingBits(pressedPlayingBits),
00518                 normalPausedBits(normalPausedBits),
00519                 pressedPausedBits(pressedPausedBits),
00520                 state(PlayPauseButton::kStopped),
00521                 lastPauseBlinkTime(0),
00522                 lastModeMask(0)
00523 {
00524 }
00525 
00526 void 
00527 PlayPauseButton::SetStopped()
00528 {
00529         if (state == kStopped || state == kAboutToPlay)
00530                 return;
00531         
00532         state = kStopped;
00533         Invalidate();
00534 }
00535 
00536 void 
00537 PlayPauseButton::SetPlaying()
00538 {
00539         if (state == kPlaying || state == kAboutToPause)
00540                 return;
00541         
00542         state = kPlaying;
00543         Invalidate();
00544 }
00545 
00546 const bigtime_t kPauseBlinkPeriod = 600000;
00547 
00548 void 
00549 PlayPauseButton::SetPaused()
00550 {
00551         if (state == kAboutToPlay)
00552                 return;
00553 
00554         // in paused state blink the LED on and off
00555         bigtime_t now = system_time();
00556         if (state == kPausedLedOn || state == kPausedLedOff) {
00557                 if (now - lastPauseBlinkTime < kPauseBlinkPeriod)
00558                         return;
00559                 
00560                 if (state == kPausedLedOn)
00561                         state = kPausedLedOff;
00562                 else
00563                         state = kPausedLedOn;
00564         } else
00565                 state = kPausedLedOn;
00566         
00567         lastPauseBlinkTime = now;
00568         Invalidate();
00569 }
00570 
00571 uint32 
00572 PlayPauseButton::ModeMask() const
00573 {
00574         if (!IsEnabled())
00575                 return kDisabledMask;
00576         
00577         uint32 result = 0;
00578 
00579         if (Value())
00580                 result = kPressedMask;
00581 
00582         if (state == kPlaying || state == kAboutToPlay)
00583                 result |= kPlayingMask;
00584         else if (state == kAboutToPause || state == kPausedLedOn)               
00585                 result |= kPausedMask;
00586         
00587         return result;
00588 }
00589 
00590 const unsigned char *
00591 PlayPauseButton::BitsForMask(uint32 mask) const
00592 {
00593         switch (mask) {
00594                 case kPlayingMask:
00595                         return normalPlayingBits;
00596                 case kPlayingMask | kPressedMask:
00597                         return pressedPlayingBits;
00598                 case kPausedMask:
00599                         return normalPausedBits;
00600                 case kPausedMask | kPressedMask:
00601                         return pressedPausedBits;
00602                 default:
00603                         return _inherited::BitsForMask(mask);
00604         }       
00605         TRESPASS();
00606         return 0;
00607 }
00608 
00609 
00610 void 
00611 PlayPauseButton::StartPressing()
00612 {
00613         if (state == kPlaying)
00614                 state = kAboutToPause;
00615         else
00616                 state = kAboutToPlay;
00617         
00618         _inherited::StartPressing();
00619 }
00620 
00621 void 
00622 PlayPauseButton::MouseCancelPressing()
00623 {
00624         if (state == kAboutToPause)
00625                 state = kPlaying;
00626         else
00627                 state = kStopped;
00628         
00629         _inherited::MouseCancelPressing();
00630 }
00631 
00632 void 
00633 PlayPauseButton::DonePressing()
00634 {
00635         if (state == kAboutToPause) {
00636                 state = kPausedLedOn;
00637                 lastPauseBlinkTime = system_time();
00638         } else if (state == kAboutToPlay)
00639                 state = kPlaying;
00640         
00641         _inherited::DonePressing();
00642 }
00643 
00644 

Generated on Tue Dec 20 10:14:37 2005 for vlc-0.8.4a by  doxygen 1.4.2