]> git.sesse.net Git - vlc/blob - modules/gui/beos/MediaControlView.cpp
Attempt to port new BeOS features from the stable branch.
[vlc] / modules / gui / beos / MediaControlView.cpp
1 /*****************************************************************************
2  * MediaControlView.cpp: beos interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001 VideoLAN
5  * $Id: MediaControlView.cpp,v 1.3 2002/09/30 18:30:27 titer Exp $
6  *
7  * Authors: Tony Castley <tony@castley.net>
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /* System headers */
26 #include <InterfaceKit.h>
27 #include <AppKit.h>
28 #include <string.h>
29
30 /* VLC headers */
31 #include <vlc/vlc.h>
32 #include <vlc/intf.h>
33
34 /* BeOS interface headers */
35 #include "VlcWrapper.h"
36 #include "Bitmaps.h"
37 #include "DrawingTidbits.h"
38 #include "InterfaceWindow.h"
39 #include "MsgVals.h"
40 #include "TransportButton.h"
41 #include "ListViews.h"
42 #include "MediaControlView.h"
43
44 #define BORDER_INSET 6.0
45 #define MIN_SPACE 4.0
46 #define SPEAKER_SLIDER_DIST 6.0
47 #define VOLUME_MIN_WIDTH 70.0
48 #define DIM_LEVEL 0.4
49 #define VOLUME_SLIDER_LAYOUT_WEIGHT 2.0
50 #define SEEK_SLIDER_KNOB_WIDTH 8.0
51
52 // slider colors are hardcoded here, because that's just
53 // what they currently are within those bitmaps
54 const rgb_color kGreen = (rgb_color){ 152, 203, 152, 255 };
55 const rgb_color kGreenShadow = (rgb_color){ 102, 152, 102, 255 };
56 const rgb_color kBackground = (rgb_color){ 216, 216, 216, 255 };
57 const rgb_color kSeekGreen = (rgb_color){ 171, 221, 161, 255 };
58 const rgb_color kSeekGreenShadow = (rgb_color){ 144, 186, 136, 255 };
59 const rgb_color kSeekRed = (rgb_color){ 255, 0, 0, 255 };
60 const rgb_color kSeekRedLight = (rgb_color){ 255, 152, 152, 255 };
61 const rgb_color kSeekRedShadow = (rgb_color){ 178, 0, 0, 255 };
62
63 const char* kDisabledSeekMessage = "Drop files to play";
64
65 enum
66 {
67         MSG_REWIND                              = 'rwnd',
68         MSG_FORWARD                             = 'frwd',
69         MSG_SKIP_BACKWARDS              = 'skpb',
70         MSG_SKIP_FORWARD                = 'skpf',
71 };
72
73 // constructor
74 MediaControlView::MediaControlView(BRect frame)
75         : BBox(frame, NULL, B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS | B_PULSE_NEEDED,
76                    B_PLAIN_BORDER),
77       fScrubSem(B_ERROR),
78       fCurrentRate(DEFAULT_RATE),
79       fCurrentStatus(UNDEF_S),
80       fBottomControlHeight(0.0)
81 {
82         BRect frame(0.0, 0.0, 10.0, 10.0);
83         
84     // Seek Slider
85     fSeekSlider = new SeekSlider(frame, "seek slider", this, 0, 2047);
86     fSeekSlider->SetValue(0);
87     fSeekSlider->ResizeToPreferred();
88     AddChild( fSeekSlider );
89
90     // Buttons
91     // Skip Back
92     frame.SetRightBottom(kSkipButtonSize);
93         fBottomControlHeight = kRewindBitmapHeight - 1.0;
94     fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
95                                     kSkipBackBitmapBits,
96                                     kPressedSkipBackBitmapBits,
97                                     kDisabledSkipBackBitmapBits,
98                                     new BMessage(MSG_SKIP_BACKWARDS));
99     AddChild( fSkipBack );
100
101         // Play Pause
102     frame.SetRightBottom(kPlayButtonSize);
103         if (fBottomControlHeight < kPlayPauseBitmapHeight - 1.0)
104                 fBottomControlHeight = kPlayPauseBitmapHeight - 1.0;
105     fPlayPause = new PlayPauseButton(frame, B_EMPTY_STRING,
106                                      kPlayButtonBitmapBits,
107                                      kPressedPlayButtonBitmapBits,
108                                      kDisabledPlayButtonBitmapBits,
109                                      kPlayingPlayButtonBitmapBits,
110                                      kPressedPlayingPlayButtonBitmapBits,
111                                      kPausedPlayButtonBitmapBits,
112                                      kPressedPausedPlayButtonBitmapBits,
113                                      new BMessage(START_PLAYBACK));
114
115     AddChild( fPlayPause );
116
117     // Skip Foward
118     frame.SetRightBottom(kSkipButtonSize);
119     fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
120                                        kSkipForwardBitmapBits,
121                                        kPressedSkipForwardBitmapBits,
122                                        kDisabledSkipForwardBitmapBits,
123                                        new BMessage(MSG_SKIP_FORWARD));
124     AddChild( fSkipForward );
125
126         // Forward
127         fForward = new TransportButton(frame, B_EMPTY_STRING,
128                                                                    kForwardBitmapBits,
129                                                                    kPressedForwardBitmapBits,
130                                                                    kDisabledForwardBitmapBits,
131                                                                    new BMessage(MSG_FORWARD));
132 //      AddChild( fForward );
133
134         // Rewind
135         fRewind = new TransportButton(frame, B_EMPTY_STRING,
136                                                                   kRewindBitmapBits,
137                                                                   kPressedRewindBitmapBits,
138                                                                   kDisabledRewindBitmapBits,
139                                                                   new BMessage(MSG_REWIND));
140 //      AddChild( fRewind );
141
142     // Stop
143     frame.SetRightBottom(kStopButtonSize);
144         if (fBottomControlHeight < kStopBitmapHeight - 1.0)
145                 fBottomControlHeight = kStopBitmapHeight - 1.0;
146     fStop = new TransportButton(frame, B_EMPTY_STRING,
147                                 kStopButtonBitmapBits,
148                                 kPressedStopButtonBitmapBits,
149                                 kDisabledStopButtonBitmapBits,
150                                 new BMessage(STOP_PLAYBACK));
151         AddChild( fStop );
152
153         // Mute
154     frame.SetRightBottom(kSpeakerButtonSize);
155         if (fBottomControlHeight < kSpeakerIconBitmapHeight - 1.0)
156                 fBottomControlHeight = kSpeakerIconBitmapHeight - 1.0;
157     fMute = new TransportButton(frame, B_EMPTY_STRING,
158                                 kSpeakerIconBits,
159                                 kPressedSpeakerIconBits,
160                                 kSpeakerIconBits,
161                                 new BMessage(VOLUME_MUTE));
162
163         AddChild( fMute );
164
165     // Volume Slider
166         fVolumeSlider = new VolumeSlider(BRect(0.0, 0.0, VOLUME_MIN_WIDTH,
167                                                                                    kVolumeSliderBitmapHeight - 1.0),
168                                                                          "volume slider", 1, AOUT_VOLUME_MAX,
169                                                                          new BMessage(VOLUME_CHG));
170         fVolumeSlider->SetValue(AOUT_VOLUME_DEFAULT);
171         AddChild( fVolumeSlider );
172 }
173
174 // destructor
175 MediaControlView::~MediaControlView()
176 {
177 }
178
179 // AttachedToWindow
180 void
181 MediaControlView::AttachedToWindow()
182 {
183         // we are now a valid BHandler
184         fRewind->SetTarget(this);
185         fForward->SetTarget(this);
186         fSkipBack->SetTarget(this);
187         fSkipForward->SetTarget(this);
188         fVolumeSlider->SetTarget(Window());
189
190         BRect r(_MinFrame());
191         if (BMenuBar* menuBar = Window()->KeyMenuBar())
192                 r.bottom += menuBar->Bounds().Height();
193
194         Window()->SetSizeLimits(r.Width(), r.Width() * 2.0, r.Height(), r.Height() * 2.0);
195         if (!Window()->Bounds().Contains(r))
196                 Window()->ResizeTo(r.Width(), r.Height());
197         else
198                 FrameResized(Bounds().Width(), Bounds().Height());
199
200         // get pulse message every two frames
201         Window()->SetPulseRate(80000);
202 }
203
204 // FrameResized
205 void
206 MediaControlView::FrameResized(float width, float height)
207 {
208         BRect r(Bounds());
209         // make sure we don't leave dirty pixels
210         // (B_FULL_UPDATE_ON_RESIZE == annoying flicker -> this is smarter)
211         if (fOldBounds.Width() < r.Width())
212                 Invalidate(BRect(fOldBounds.right, fOldBounds.top + 1.0,
213                                                  fOldBounds.right, fOldBounds.bottom - 1.0));
214         else
215                 Invalidate(BRect(r.right, r.top + 1.0,
216                                                  r.right, r.bottom - 1.0));
217         if (fOldBounds.Height() < r.Height())
218                 Invalidate(BRect(fOldBounds.left + 1.0, fOldBounds.bottom,
219                                                  fOldBounds.right - 1.0, fOldBounds.bottom));
220         else
221                 Invalidate(BRect(r.left + 1.0, r.bottom,
222                                                  r.right - 1.0, r.bottom));
223         // remember for next time
224         fOldBounds = r;
225         // layout controls
226         r.InsetBy(BORDER_INSET, BORDER_INSET);
227         _LayoutControls(r);
228 }
229
230 // GetPreferredSize
231 void
232 MediaControlView::GetPreferredSize(float* width, float* height)
233 {
234         if (width && height)
235         {
236                 BRect r(_MinFrame());
237                 *width = r.Width();
238                 *height = r.Height();
239         }
240 }
241
242 // MessageReceived
243 void
244 MediaControlView::MessageReceived(BMessage* message)
245 {
246         switch (message->what)
247         {
248                 case MSG_REWIND:
249                         break;
250                 case MSG_FORWARD:
251                         break;
252                 case MSG_SKIP_BACKWARDS:
253                         Window()->PostMessage(NAVIGATE_PREV);
254                         break;
255                 case MSG_SKIP_FORWARD:
256                         Window()->PostMessage(NAVIGATE_NEXT);
257                         break;
258                 default:
259                     BBox::MessageReceived(message);
260                     break;
261         }
262 }
263
264 // Pulse
265 void
266 MediaControlView::Pulse()
267 {
268         InterfaceWindow* window = dynamic_cast<InterfaceWindow*>(Window());
269         if (window && window->IsStopped())
270                         fPlayPause->SetStopped();
271 }
272
273 // SetProgress
274 void
275 MediaControlView::SetProgress(uint64 seek, uint64 size)
276 {
277         fSeekSlider->SetPosition((float)seek / (float)size);
278 }
279
280 // SetStatus
281 void
282 MediaControlView::SetStatus(int status, int rate)
283 {
284         // we need to set the button status periodically
285         // (even if it is the same) to get a blinking button
286         fCurrentStatus = status;
287     switch( status )
288     {
289         case PLAYING_S:
290         case FORWARD_S:
291         case BACKWARD_S:
292         case START_S:
293             fPlayPause->SetPlaying();
294             break;
295         case PAUSE_S:
296             fPlayPause->SetPaused();
297             break;
298         case UNDEF_S:
299         case NOT_STARTED_S:
300         default:
301             fPlayPause->SetStopped();
302             break;
303     }
304         if (rate != fCurrentRate)
305         {
306                 fCurrentRate = rate;
307             if ( rate < DEFAULT_RATE )
308             {
309                 // TODO: ...
310             }
311         }
312 }
313
314 // SetEnabled
315 void
316 MediaControlView::SetEnabled(bool enabled)
317 {
318         fSkipBack->SetEnabled(enabled);
319         fPlayPause->SetEnabled(enabled);
320         fSkipForward->SetEnabled(enabled);
321         fStop->SetEnabled(enabled);
322         fMute->SetEnabled(enabled);
323         fVolumeSlider->SetEnabled(enabled);
324         fSeekSlider->SetEnabled(enabled);
325         fRewind->SetEnabled(enabled);
326         fForward->SetEnabled(enabled);
327 }
328
329 // SetAudioEnabled
330 void
331 MediaControlView::SetAudioEnabled(bool enabled)
332 {
333         fMute->SetEnabled(enabled);
334         fVolumeSlider->SetEnabled(enabled);
335 }
336
337 // GetSeekTo
338 uint32
339 MediaControlView::GetSeekTo() const
340 {
341         return fSeekSlider->Value();
342 }
343
344 // GetVolume
345 uint32
346 MediaControlView::GetVolume() const
347 {
348         return fVolumeSlider->Value();
349 }
350
351 // SetSkippable
352 void
353 MediaControlView::SetSkippable(bool backward, bool forward)
354 {
355         fSkipBack->SetEnabled(backward);
356         fSkipForward->SetEnabled(forward);
357 }
358
359 // SetMuted
360 void
361 MediaControlView::SetMuted(bool mute)
362 {
363         fVolumeSlider->SetMuted(mute);
364 }
365
366 // _LayoutControls
367 void
368 MediaControlView::_LayoutControls(BRect frame) const
369 {
370         // seek slider
371         BRect r(frame);
372         r.bottom = r.top + r.Height() / 2.0 - MIN_SPACE / 2.0;
373         _LayoutControl(fSeekSlider, r, true);
374         // calculate absolutly minimal width
375         float minWidth = fSkipBack->Bounds().Width();
376 //      minWidth += fRewind->Bounds().Width();
377         minWidth += fStop->Bounds().Width();
378         minWidth += fPlayPause->Bounds().Width();
379 //      minWidth += fForward->Bounds().Width();
380         minWidth += fSkipForward->Bounds().Width();
381         minWidth += fMute->Bounds().Width();
382         minWidth += VOLUME_MIN_WIDTH;
383         float currentWidth = frame.Width();
384         float space = (currentWidth - minWidth) / 6.0;//8.0;
385         // apply weighting
386         space = MIN_SPACE + (space - MIN_SPACE) / VOLUME_SLIDER_LAYOUT_WEIGHT;
387         // layout controls with "space" inbetween
388         r.top = r.bottom + MIN_SPACE + 1.0;
389         r.bottom = frame.bottom;
390         // skip back
391         r.right = r.left + fSkipBack->Bounds().Width();
392         _LayoutControl(fSkipBack, r);
393         // rewind
394 //      r.left = r.right + space;
395 //      r.right = r.left + fRewind->Bounds().Width();
396 //      _LayoutControl(fRewind, r);
397         // stop
398         r.left = r.right + space;
399         r.right = r.left + fStop->Bounds().Width();
400         _LayoutControl(fStop, r);
401         // play/pause
402         r.left = r.right + space;
403         r.right = r.left + fPlayPause->Bounds().Width();
404         _LayoutControl(fPlayPause, r);
405         // forward
406 //      r.left = r.right + space;
407 //      r.right = r.left + fForward->Bounds().Width();
408 //      _LayoutControl(fForward, r);
409         // skip forward
410         r.left = r.right + space;
411         r.right = r.left + fSkipForward->Bounds().Width();
412         _LayoutControl(fSkipForward, r);
413         // speaker icon
414         r.left = r.right + space + space;
415         r.right = r.left + fMute->Bounds().Width();
416         _LayoutControl(fMute, r);
417         // volume slider
418         r.left = r.right + SPEAKER_SLIDER_DIST; // keep speaker icon and volume slider attached
419         r.right = frame.right;
420         _LayoutControl(fVolumeSlider, r, true);
421 }
422
423 // _MinFrame
424 BRect           
425 MediaControlView::_MinFrame() const
426 {
427         // add up width of controls along bottom (seek slider will likely adopt)
428         float minWidth = 2 * BORDER_INSET;
429         minWidth += fSkipBack->Bounds().Width() + MIN_SPACE;
430 //      minWidth += fRewind->Bounds().Width() + MIN_SPACE;
431         minWidth += fStop->Bounds().Width() + MIN_SPACE;
432         minWidth += fPlayPause->Bounds().Width() + MIN_SPACE;
433 //      minWidth += fForward->Bounds().Width() + MIN_SPACE;
434         minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
435         minWidth += fMute->Bounds().Width() + SPEAKER_SLIDER_DIST;
436         minWidth += VOLUME_MIN_WIDTH;
437
438         // add up height of seek slider and heighest control on bottom
439         float minHeight = 2 * BORDER_INSET;
440         minHeight += fSeekSlider->Bounds().Height() + MIN_SPACE + MIN_SPACE / 2.0;
441         minHeight += fBottomControlHeight;
442         return BRect(0.0, 0.0, minWidth - 1.0, minHeight - 1.0);
443 }
444
445 // _LayoutControl
446 void
447 MediaControlView::_LayoutControl(BView* view, BRect frame, bool resize) const
448 {
449         // center vertically
450         frame.top = (frame.top + frame.bottom) / 2.0 - view->Bounds().Height() / 2.0;
451         if (!resize)
452                 frame.left = (frame.left + frame.right) / 2.0 - view->Bounds().Width() / 2.0;
453         view->MoveTo(frame.LeftTop());
454         if (resize)
455                 view->ResizeTo(frame.Width(), view->Bounds().Height());
456 }
457
458
459
460 /*****************************************************************************
461  * SeekSlider
462  *****************************************************************************/
463 SeekSlider::SeekSlider(BRect frame, const char* name, MediaControlView *owner,
464                                            int32 minValue, int32 maxValue)
465         : BControl(frame, name, NULL, NULL, B_FOLLOW_NONE,
466                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
467           fOwner(owner),
468           fTracking(false),
469           fMinValue(minValue),
470           fMaxValue(maxValue)
471 {
472         BFont font(be_plain_font);
473         font.SetSize(9.0);
474         SetFont(&font);
475 }
476
477 SeekSlider::~SeekSlider()
478 {
479         _EndSeek();
480 }
481
482 /*****************************************************************************
483  * VolumeSlider::AttachedToWindow
484  *****************************************************************************/
485 void
486 SeekSlider::AttachedToWindow()
487 {
488         BControl::AttachedToWindow();
489         SetViewColor(B_TRANSPARENT_32_BIT);
490 }
491
492 /*****************************************************************************
493  * VolumeSlider::Draw
494  *****************************************************************************/
495 void
496 SeekSlider::Draw(BRect updateRect)
497 {
498         BRect r(Bounds());
499         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
500         float sliderStart = (r.left + knobWidth2);
501         float sliderEnd = (r.right - knobWidth2);
502         float knobPos = sliderStart
503                                         + floorf((sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
504                                         / (fMaxValue - fMinValue) + 0.5);
505         // draw both sides (the original from Be doesn't seem
506         // to make a difference for enabled/disabled state)
507 //      DrawBitmapAsync(fLeftSideBits, r.LeftTop());
508 //      DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
509         // colors for the slider area between the two bitmaps
510         rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
511         rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
512         rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
513         rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
514         rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
515         rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
516         rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
517         rgb_color green = kSeekGreen;
518         rgb_color greenShadow = kSeekGreenShadow;
519         rgb_color black = kBlack;
520         rgb_color dotGrey = midShadow;
521         rgb_color dotGreen = greenShadow;
522         // draw frame
523         _StrokeFrame(r, softShadow, softShadow, softLight, softLight);
524         r.InsetBy(1.0, 1.0);
525         _StrokeFrame(r, black, black, light, light);
526         if (IsEnabled())
527         {
528                 r.InsetBy(1.0, 1.0);
529                 // inner shadow
530                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
531                 r.top++;
532                 r.left++;
533                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
534                 // inside area
535                 r.InsetBy(1.0, 1.0);
536                 SetHighColor(green);
537                 FillRect(r);
538                 // dots
539                 int32 dotCount = (int32)(r.Width() / 6.0);
540                 BPoint dotPos;
541                 dotPos.y = r.top + 2.0;
542                 SetHighColor(dotGreen);
543                 for (int32 i = 0; i < dotCount; i++)
544                 {
545                         dotPos.x = sliderStart + i * 6.0 + 5.0;
546                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 6.0));
547                 }
548                 // slider handle
549                 r.top -= 4.0;
550                 r.bottom += 3.0;
551                 r.left = knobPos - knobWidth2;
552                 r.right = knobPos + knobWidth2;
553                 // black outline
554                 float handleBottomSize = 2.0;
555                 float handleArrowSize = 6.0;
556                 BeginLineArray(10);
557                         // upper handle
558                         AddLine(BPoint(r.left, r.top + handleBottomSize),
559                                         BPoint(r.left, r.top), black);
560                         AddLine(BPoint(r.left + 1.0, r.top),
561                                         BPoint(r.right, r.top), black);
562                         AddLine(BPoint(r.right, r.top + 1.0),
563                                         BPoint(r.right, r.top + handleBottomSize), black);
564                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
565                                         BPoint(knobPos, r.top + handleArrowSize), black);
566                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
567                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), black);
568                         // lower handle
569                         AddLine(BPoint(r.left, r.bottom),
570                                         BPoint(r.left, r.bottom - handleBottomSize), black);
571                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
572                                         BPoint(knobPos, r.bottom - handleArrowSize), black);
573                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
574                                         BPoint(r.right, r.bottom - handleBottomSize), black);
575                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
576                                         BPoint(r.right, r.bottom), black);
577                         AddLine(BPoint(r.right - 1.0, r.bottom),
578                                         BPoint(r.left + 1.0, r.bottom), black);
579                 EndLineArray();
580                 // inner red light and shadow lines
581                 r.InsetBy(1.0, 1.0);
582                 handleBottomSize--;
583                 handleArrowSize -= 2.0;
584                 BeginLineArray(10);
585                         // upper handle
586                         AddLine(BPoint(r.left, r.top + handleBottomSize),
587                                         BPoint(r.left, r.top), kSeekRedLight);
588                         AddLine(BPoint(r.left + 1.0, r.top),
589                                         BPoint(r.right, r.top), kSeekRedLight);
590                         AddLine(BPoint(r.right, r.top + 1.0),
591                                         BPoint(r.right, r.top + handleBottomSize), kSeekRedShadow);
592                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
593                                         BPoint(knobPos, r.top + handleArrowSize), kSeekRedShadow);
594                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
595                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), kSeekRedLight);
596                         // lower handle
597                         AddLine(BPoint(r.left, r.bottom),
598                                         BPoint(r.left, r.bottom - handleBottomSize), kSeekRedLight);
599                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
600                                         BPoint(knobPos, r.bottom - handleArrowSize), kSeekRedLight);
601                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
602                                         BPoint(r.right, r.bottom - handleBottomSize), kSeekRedShadow);
603                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
604                                         BPoint(r.right, r.bottom), kSeekRedShadow);
605                         AddLine(BPoint(r.right - 1.0, r.bottom),
606                                         BPoint(r.left + 1.0, r.bottom), kSeekRedShadow);
607                 EndLineArray();
608                 // fill rest of handles with red
609                 SetHighColor(kSeekRed);
610                 r.InsetBy(1.0, 1.0);
611                 handleArrowSize -= 2.0;
612                 BPoint arrow[3];
613                 // upper handle arrow
614                 arrow[0].x = r.left;
615                 arrow[0].y = r.top;
616                 arrow[1].x = r.right;
617                 arrow[1].y = r.top;
618                 arrow[2].x = knobPos;
619                 arrow[2].y = r.top + handleArrowSize;
620                 FillPolygon(arrow, 3);
621                 // lower handle arrow
622                 arrow[0].x = r.left;
623                 arrow[0].y = r.bottom;
624                 arrow[1].x = r.right;
625                 arrow[1].y = r.bottom;
626                 arrow[2].x = knobPos;
627                 arrow[2].y = r.bottom - handleArrowSize;
628                 FillPolygon(arrow, 3);
629         }
630         else
631         {
632                 r.InsetBy(1.0, 1.0);
633                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
634                 r.InsetBy(1.0, 1.0);
635                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
636                 r.InsetBy(1.0, 1.0);
637                 SetHighColor(darkShadow);
638                 SetLowColor(shadow);
639                 // stripes
640                 float width = floorf(StringWidth(kDisabledSeekMessage));
641                 float textPos = r.left + r.Width() / 2.0 - width / 2.0;
642                 pattern stripes = { 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3 };
643                 BRect stripesRect(r);
644                 stripesRect.right = textPos - 5.0;
645                 FillRect(stripesRect, stripes);
646                 stripesRect.left = textPos + width + 3.0;
647                 stripesRect.right = r.right;
648                 FillRect(stripesRect, stripes);
649                 // info text
650                 r.left = textPos - 4.0;
651                 r.right = textPos + width + 2.0;
652                 FillRect(r);
653                 SetHighColor(shadow);
654                 SetLowColor(darkShadow);
655                 font_height fh;
656                 GetFontHeight(&fh);
657                 DrawString(kDisabledSeekMessage, BPoint(textPos, r.top + ceilf(fh.ascent) - 1.0));
658         }
659 }
660
661 /*****************************************************************************
662  * SeekSlider::MouseDown
663  *****************************************************************************/
664 void
665 SeekSlider::MouseDown(BPoint where)
666 {
667         if (IsEnabled() && Bounds().Contains(where))
668         {
669                 SetValue(_ValueFor(where.x));
670                 fTracking = true;
671                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
672                 _BeginSeek();
673         }
674 }
675
676 /*****************************************************************************
677  * SeekSlider::MouseMoved
678  *****************************************************************************/
679 void
680 SeekSlider::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
681 {
682         if (fTracking)
683         {
684                 SetValue(_ValueFor(where.x));
685                 _Seek();
686         }
687 }
688
689 /*****************************************************************************
690  * SeekSlider::MouseUp
691  *****************************************************************************/
692 void
693 SeekSlider::MouseUp(BPoint where)
694 {
695         if (fTracking)
696         {
697                 fTracking = false;
698                 _EndSeek();
699         }
700 }
701
702 /*****************************************************************************
703  * SeekSlider::ResizeToPreferred
704  *****************************************************************************/
705 void
706 SeekSlider::ResizeToPreferred()
707 {
708         float width = 15.0 + StringWidth(kDisabledSeekMessage) + 15.0;
709         ResizeTo(width, 17.0);
710 }
711
712 /*****************************************************************************
713  * SeekSlider::SetPosition
714  *****************************************************************************/
715 void
716 SeekSlider::SetPosition(float position)
717 {
718         SetValue(fMinValue + (int32)floorf((fMaxValue - fMinValue) * position + 0.5));
719 }
720
721 /*****************************************************************************
722  * SeekSlider::_ValueFor
723  *****************************************************************************/
724 int32
725 SeekSlider::_ValueFor(float xPos) const
726 {
727         BRect r(Bounds());
728         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
729         float sliderStart = (r.left + knobWidth2);
730         float sliderEnd = (r.right - knobWidth2);
731         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
732                                   / (sliderEnd - sliderStart - 1.0));
733         if (value < fMinValue)
734                 value = fMinValue;
735         if (value > fMaxValue)
736                 value = fMaxValue;
737         return value;
738 }
739
740 /*****************************************************************************
741  * SeekSlider::_StrokeFrame
742  *****************************************************************************/
743 void
744 SeekSlider::_StrokeFrame(BRect r, rgb_color left, rgb_color top,
745                                                  rgb_color right, rgb_color bottom)
746 {
747         BeginLineArray(4);
748                 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), left);
749                 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), top);
750                 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), right);
751                 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), bottom);
752         EndLineArray();
753 }
754
755 /*****************************************************************************
756  * SeekSlider::_BeginSeek
757  *****************************************************************************/
758 void
759 SeekSlider::_BeginSeek()
760 {
761         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
762         if (fOwner->fScrubSem >= B_OK)
763                 release_sem(fOwner->fScrubSem);
764 }
765
766 /*****************************************************************************
767  * SeekSlider::_Seek
768  *****************************************************************************/
769 void
770 SeekSlider::_Seek()
771 {
772         if (fOwner->fScrubSem >= B_OK)
773                 delete_sem(fOwner->fScrubSem);
774         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
775         if (fOwner->fScrubSem >= B_OK)
776                 release_sem(fOwner->fScrubSem);
777 }
778
779 /*****************************************************************************
780  * SeekSlider::_EndSeek
781  *****************************************************************************/
782 void
783 SeekSlider::_EndSeek()
784 {
785         if (fOwner->fScrubSem >= B_OK)
786                 delete_sem(fOwner->fScrubSem);
787         fOwner->fScrubSem = B_ERROR;
788 }
789
790
791 /*****************************************************************************
792  * VolumeSlider
793  *****************************************************************************/
794 VolumeSlider::VolumeSlider(BRect frame, const char* name, int32 minValue, int32 maxValue,
795                                                    BMessage* message, BHandler* target)
796         : BControl(frame, name, NULL, message, B_FOLLOW_NONE,
797                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
798           fLeftSideBits(NULL),
799           fRightSideBits(NULL),
800           fKnobBits(NULL),
801           fTracking(false),
802           fMuted(false),
803           fMinValue(minValue),
804           fMaxValue(maxValue)
805 {
806         SetTarget(target);
807
808         // create bitmaps
809         BRect r(BPoint(0.0, 0.0), kVolumeSliderBitmapSize);
810         fLeftSideBits = new BBitmap(r, B_CMAP8);
811         fRightSideBits = new BBitmap(r, B_CMAP8);
812         r.Set(0.0, 0.0, kVolumeSliderKnobBitmapSize.x, kVolumeSliderKnobBitmapSize.y);
813         fKnobBits = new BBitmap(r, B_CMAP8);
814
815         _MakeBitmaps();
816 }
817
818 /*****************************************************************************
819  * VolumeSlider destructor
820  *****************************************************************************/
821 VolumeSlider::~VolumeSlider()
822 {
823         delete fLeftSideBits;
824         delete fRightSideBits;
825         delete fKnobBits;
826 }
827
828 /*****************************************************************************
829  * VolumeSlider::AttachedToWindow
830  *****************************************************************************/
831 void
832 VolumeSlider::AttachedToWindow()
833 {
834         BControl::AttachedToWindow();
835         SetViewColor(B_TRANSPARENT_32_BIT);
836 }
837
838 /*****************************************************************************
839  * VolumeSlider::SetValue
840  *****************************************************************************/
841 void
842 VolumeSlider::SetValue(int32 value)
843 {
844         if (value != Value())
845         {
846                 BControl::SetValue(value);
847                 Invoke();
848         }
849 }
850
851 /*****************************************************************************
852  * VolumeSlider::SetEnabled
853  *****************************************************************************/
854 void
855 VolumeSlider::SetEnabled(bool enable)
856 {
857         if (enable != IsEnabled())
858         {
859                 BControl::SetEnabled(enable);
860                 _MakeBitmaps();
861                 Invalidate();
862         }
863 }
864
865 /*****************************************************************************
866  * VolumeSlider::Draw
867  *****************************************************************************/
868 void
869 VolumeSlider::Draw(BRect updateRect)
870 {
871         if (IsValid())
872         {
873                 BRect r(Bounds());
874                 float sliderSideWidth = kVolumeSliderBitmapWidth;
875                 float sliderStart = (r.left + sliderSideWidth);
876                 float sliderEnd = (r.right - sliderSideWidth);
877                 float knobPos = sliderStart
878                                                 + (sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
879                                                 / (fMaxValue - fMinValue);
880                 // draw both sides (the original from Be doesn't seem
881                 // to make a difference for enabled/disabled state)
882                 DrawBitmapAsync(fLeftSideBits, r.LeftTop());
883                 DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
884                 // colors for the slider area between the two bitmaps
885                 rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
886                 rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
887                 rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
888                 rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
889                 rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
890                 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
891                 rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
892                 rgb_color green = kGreen;
893                 rgb_color greenShadow = kGreenShadow;
894                 rgb_color black = kBlack;
895                 rgb_color dotGrey = midShadow;
896                 rgb_color dotGreen = greenShadow;
897                 // make dimmed version of colors if we're disabled
898                 if (!IsEnabled())
899                 {
900                         shadow = (rgb_color){ 200, 200, 200, 255 };
901                         softShadow = dimmed_color_cmap8(softShadow, background, DIM_LEVEL);
902                         darkShadow = dimmed_color_cmap8(darkShadow, background, DIM_LEVEL);
903                         midShadow = shadow;
904                         light = dimmed_color_cmap8(light, background, DIM_LEVEL);
905                         softLight = dimmed_color_cmap8(softLight, background, DIM_LEVEL);
906                         green = dimmed_color_cmap8(green, background, DIM_LEVEL);
907                         greenShadow = dimmed_color_cmap8(greenShadow, background, DIM_LEVEL);
908                         black = dimmed_color_cmap8(black, background, DIM_LEVEL);
909                         dotGreen = dotGrey;
910                 }
911                 else if (fMuted)
912                 {
913                         green = tint_color(kBackground, B_DARKEN_3_TINT);
914                         greenShadow = tint_color(kBackground, B_DARKEN_4_TINT);
915                         dotGreen = greenShadow;
916                 }
917                 // draw slider edges between bitmaps
918                 BeginLineArray(7);
919                         AddLine(BPoint(sliderStart, r.top),
920                                         BPoint(sliderEnd, r.top), softShadow);
921                         AddLine(BPoint(sliderStart, r.bottom),
922                                         BPoint(sliderEnd, r.bottom), softLight);
923                         r.InsetBy(0.0, 1.0);
924                         AddLine(BPoint(sliderStart, r.top),
925                                         BPoint(sliderEnd, r.top), black);
926                         AddLine(BPoint(sliderStart, r.bottom),
927                                         BPoint(sliderEnd, r.bottom), light);
928                         r.top++;
929                         AddLine(BPoint(sliderStart, r.top),
930                                         BPoint(knobPos, r.top), greenShadow);
931                         AddLine(BPoint(knobPos, r.top),
932                                         BPoint(sliderEnd, r.top), midShadow);
933                         r.top++;
934                         AddLine(BPoint(sliderStart, r.top),
935                                         BPoint(knobPos, r.top), greenShadow);
936                 EndLineArray();
937                 // fill rest inside of slider
938                 r.InsetBy(0.0, 1.0);
939                 r.left = sliderStart;
940                 r.right = knobPos;
941                 SetHighColor(green);
942                 FillRect(r, B_SOLID_HIGH);
943                 r.left = knobPos + 1.0;
944                 r.right = sliderEnd;
945                 r.top -= 1.0;
946                 SetHighColor(shadow);
947                 FillRect(r, B_SOLID_HIGH);
948                 // draw little dots inside
949                 int32 dotCount = (int32)((sliderEnd - sliderStart) / 5.0);
950                 BPoint dotPos;
951                 dotPos.y = r.top + 4.0;
952                 for (int32 i = 0; i < dotCount; i++)
953                 {
954                         dotPos.x = sliderStart + i * 5.0 + 4.0;
955                         SetHighColor(dotPos.x < knobPos ? dotGreen : dotGrey);
956                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 1.0));
957                 }
958                 // draw knob
959                 r.top -= 1.0;
960                 SetDrawingMode(B_OP_OVER); // part of knob is transparent
961                 DrawBitmapAsync(fKnobBits, BPoint(knobPos - kVolumeSliderKnobWidth / 2, r.top));
962         }
963         else
964                 fprintf(stderr, "VolumeSlider::Draw() - Error: no valid bitmaps!");
965 }
966
967 /*****************************************************************************
968  * VolumeSlider::MouseDown
969  *****************************************************************************/
970 void
971 VolumeSlider::MouseDown(BPoint where)
972 {
973         if (Bounds().Contains(where) && IsEnabled())
974         {
975                 fTracking = true;
976                 SetValue(_ValueFor(where.x));
977                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
978         }
979 }
980
981 /*****************************************************************************
982  * VolumeSlider::MouseMoved
983  *****************************************************************************/
984 void
985 VolumeSlider::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
986 {
987         if (fTracking)
988                 SetValue(_ValueFor(where.x));
989 }
990
991 /*****************************************************************************
992  * VolumeSlider::MouseUp
993  *****************************************************************************/
994 void
995 VolumeSlider::MouseUp(BPoint where)
996 {
997         fTracking = false;
998 }
999
1000
1001 /*****************************************************************************
1002  * VolumeSlider::IsValid
1003  *****************************************************************************/
1004 bool
1005 VolumeSlider::IsValid() const
1006 {
1007         return (fLeftSideBits && fLeftSideBits->IsValid()
1008                         && fRightSideBits && fRightSideBits->IsValid()
1009                         && fKnobBits && fKnobBits->IsValid());
1010 }
1011
1012 /*****************************************************************************
1013  * VolumeSlider::SetMuted
1014  *****************************************************************************/
1015 void
1016 VolumeSlider::SetMuted(bool mute)
1017 {
1018         if (mute != fMuted)
1019         {
1020                 fMuted = mute;
1021                 _MakeBitmaps();
1022                 Invalidate();
1023         }
1024 }
1025
1026 /*****************************************************************************
1027  * VolumeSlider::_MakeBitmaps
1028  *****************************************************************************/
1029 void
1030 VolumeSlider::_MakeBitmaps()
1031 {
1032         if (IsValid())
1033         {
1034                 // left side of slider
1035                 memcpy(fLeftSideBits->Bits(), kVolumeSliderLeftBitmapBits,
1036                            fLeftSideBits->BitsLength());
1037                 // right side of slider
1038                 memcpy(fRightSideBits->Bits(), kVolumeSliderRightBits,
1039                            fRightSideBits->BitsLength());
1040                 // slider knob
1041                 int32 length = fKnobBits->BitsLength();
1042                 memcpy(fKnobBits->Bits(), kVolumeSliderKnobBits, length);
1043                 uint8* bits = (uint8*)fKnobBits->Bits();
1044                 // black was used in the knob to represent transparency
1045                 // use screen to get index for the "transarent" color used in the bitmap
1046                 BScreen screen(B_MAIN_SCREEN_ID);
1047                 uint8 blackIndex = screen.IndexForColor(kBlack);
1048                 // replace black index with transparent index
1049                 for (int32 i = 0; i < length; i++)
1050                         if (bits[i] == blackIndex)
1051                                 bits[i] = B_TRANSPARENT_MAGIC_CMAP8;
1052
1053                 if (!IsEnabled())
1054                 {
1055                         // make ghosted versions of the bitmaps
1056                         dim_bitmap(fLeftSideBits, kBackground, DIM_LEVEL);
1057                         dim_bitmap(fRightSideBits, kBackground, DIM_LEVEL);
1058                         dim_bitmap(fKnobBits, kBackground, DIM_LEVEL);
1059                 }
1060                 else if (fMuted)
1061                 {
1062                         // replace green color (and shadow) in left slider side
1063                         bits = (uint8*)fLeftSideBits->Bits();
1064                         length = fLeftSideBits->BitsLength();
1065                         uint8 greenIndex = screen.IndexForColor(kGreen);
1066                         uint8 greenShadowIndex = screen.IndexForColor(kGreenShadow);
1067                         rgb_color shadow = tint_color(kBackground, B_DARKEN_3_TINT);
1068                         rgb_color midShadow = tint_color(kBackground, B_DARKEN_4_TINT);
1069                         uint8 replaceIndex = screen.IndexForColor(shadow);
1070                         uint8 replaceShadowIndex = screen.IndexForColor(midShadow);
1071                         for (int32 i = 0; i < length; i++)
1072                         {
1073                                 if (bits[i] == greenIndex)
1074                                         bits[i] = replaceIndex;
1075                                 else if (bits[i] == greenShadowIndex)
1076                                         bits[i] = replaceShadowIndex;
1077                         }
1078                 }
1079         }
1080 }
1081
1082 /*****************************************************************************
1083  * VolumeSlider::_ValueFor
1084  *****************************************************************************/
1085 int32
1086 VolumeSlider::_ValueFor(float xPos) const
1087 {
1088         BRect r(Bounds());
1089         float sliderStart = (r.left + kVolumeSliderBitmapWidth);
1090         float sliderEnd = (r.right - kVolumeSliderBitmapWidth);
1091         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
1092                                   / (sliderEnd - sliderStart - 1.0));
1093         if (value < fMinValue)
1094                 value = fMinValue;
1095         if (value > fMaxValue)
1096                 value = fMaxValue;
1097         return value;
1098 }
1099
1100
1101
1102
1103