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