]> git.sesse.net Git - vlc/blob - modules/gui/beos/MediaControlView.cpp
beos/*:
[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.21 2003/12/28 01:49:12 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 #include <string.h>
30
31 /* VLC headers */
32 #include <vlc/vlc.h>
33 #include <vlc/intf.h>
34 extern "C"
35 {
36   #include <audio_output.h>
37 }
38
39 /* BeOS interface headers */
40 #include "VlcWrapper.h"
41 #include "Bitmaps.h"
42 #include "DrawingTidbits.h"
43 #include "InterfaceWindow.h"
44 #include "MsgVals.h"
45 #include "TransportButton.h"
46 #include "ListViews.h"
47 #include "MediaControlView.h"
48
49 #define BORDER_INSET 6.0
50 #define MIN_SPACE 4.0
51 #define SPEAKER_SLIDER_DIST 6.0
52 #define VOLUME_MIN_WIDTH 70.0
53 #define DIM_LEVEL 0.4
54 #define VOLUME_SLIDER_LAYOUT_WEIGHT 2.0
55 #define SEEK_SLIDER_KNOB_WIDTH 8.0
56
57 // slider colors are hardcoded here, because that's just
58 // what they currently are within those bitmaps
59 const rgb_color kGreen = (rgb_color){ 152, 203, 152, 255 };
60 const rgb_color kGreenShadow = (rgb_color){ 102, 152, 102, 255 };
61 const rgb_color kBackground = (rgb_color){ 216, 216, 216, 255 };
62 const rgb_color kSeekGreen = (rgb_color){ 171, 221, 161, 255 };
63 const rgb_color kSeekGreenShadow = (rgb_color){ 144, 186, 136, 255 };
64 const rgb_color kSeekRed = (rgb_color){ 255, 0, 0, 255 };
65 const rgb_color kSeekRedLight = (rgb_color){ 255, 152, 152, 255 };
66 const rgb_color kSeekRedShadow = (rgb_color){ 178, 0, 0, 255 };
67
68 #define DISABLED_SEEK_MESSAGE _("Drop files to play")
69
70 enum
71 {
72         MSG_REWIND                              = 'rwnd',
73         MSG_FORWARD                             = 'frwd',
74         MSG_SKIP_BACKWARDS              = 'skpb',
75         MSG_SKIP_FORWARD                = 'skpf',
76 };
77
78 // constructor
79 MediaControlView::MediaControlView(BRect frame, intf_thread_t *p_interface)
80         : BBox(frame, NULL, B_FOLLOW_NONE, B_WILL_DRAW | B_FRAME_EVENTS | B_PULSE_NEEDED,
81                    B_PLAIN_BORDER),
82       fScrubSem(B_ERROR),
83       fCurrentRate(DEFAULT_RATE),
84       fCurrentStatus(UNDEF_S),
85       fBottomControlHeight(0.0),
86       fIsEnabled( true )
87 {
88     p_intf = p_interface;
89
90         BRect frame(0.0, 0.0, 10.0, 10.0);
91         
92     // Seek Slider
93     fSeekSlider = new SeekSlider( frame, "seek slider", this,
94                                   0, SEEKSLIDER_RANGE );
95     fSeekSlider->SetValue(0);
96     fSeekSlider->ResizeToPreferred();
97     AddChild( fSeekSlider );
98
99     // Buttons
100     // Skip Back
101     frame.SetRightBottom(kSkipButtonSize);
102         fBottomControlHeight = kRewindBitmapHeight - 1.0;
103     fSkipBack = new TransportButton(frame, B_EMPTY_STRING,
104                                     kSkipBackBitmapBits,
105                                     kPressedSkipBackBitmapBits,
106                                     kDisabledSkipBackBitmapBits,
107                                     new BMessage(MSG_SKIP_BACKWARDS));
108     AddChild( fSkipBack );
109
110         // Play Pause
111     frame.SetRightBottom(kPlayButtonSize);
112         if (fBottomControlHeight < kPlayPauseBitmapHeight - 1.0)
113                 fBottomControlHeight = kPlayPauseBitmapHeight - 1.0;
114     fPlayPause = new PlayPauseButton(frame, B_EMPTY_STRING,
115                                      kPlayButtonBitmapBits,
116                                      kPressedPlayButtonBitmapBits,
117                                      kDisabledPlayButtonBitmapBits,
118                                      kPlayingPlayButtonBitmapBits,
119                                      kPressedPlayingPlayButtonBitmapBits,
120                                      kPausedPlayButtonBitmapBits,
121                                      kPressedPausedPlayButtonBitmapBits,
122                                      new BMessage(START_PLAYBACK));
123
124     AddChild( fPlayPause );
125
126     // Skip Foward
127     frame.SetRightBottom(kSkipButtonSize);
128     fSkipForward = new TransportButton(frame, B_EMPTY_STRING,
129                                        kSkipForwardBitmapBits,
130                                        kPressedSkipForwardBitmapBits,
131                                        kDisabledSkipForwardBitmapBits,
132                                        new BMessage(MSG_SKIP_FORWARD));
133     AddChild( fSkipForward );
134
135         // Forward
136         fForward = new TransportButton(frame, B_EMPTY_STRING,
137                                                                    kForwardBitmapBits,
138                                                                    kPressedForwardBitmapBits,
139                                                                    kDisabledForwardBitmapBits,
140                                                                    new BMessage(MSG_FORWARD));
141 //      AddChild( fForward );
142
143         // Rewind
144         fRewind = new TransportButton(frame, B_EMPTY_STRING,
145                                                                   kRewindBitmapBits,
146                                                                   kPressedRewindBitmapBits,
147                                                                   kDisabledRewindBitmapBits,
148                                                                   new BMessage(MSG_REWIND));
149 //      AddChild( fRewind );
150
151     // Stop
152     frame.SetRightBottom(kStopButtonSize);
153         if (fBottomControlHeight < kStopBitmapHeight - 1.0)
154                 fBottomControlHeight = kStopBitmapHeight - 1.0;
155     fStop = new TransportButton(frame, B_EMPTY_STRING,
156                                 kStopButtonBitmapBits,
157                                 kPressedStopButtonBitmapBits,
158                                 kDisabledStopButtonBitmapBits,
159                                 new BMessage(STOP_PLAYBACK));
160         AddChild( fStop );
161
162         // Mute
163     frame.SetRightBottom(kSpeakerButtonSize);
164         if (fBottomControlHeight < kSpeakerIconBitmapHeight - 1.0)
165                 fBottomControlHeight = kSpeakerIconBitmapHeight - 1.0;
166     fMute = new TransportButton(frame, B_EMPTY_STRING,
167                                 kSpeakerIconBits,
168                                 kPressedSpeakerIconBits,
169                                 kSpeakerIconBits,
170                                 new BMessage(VOLUME_MUTE));
171
172         AddChild( fMute );
173
174     // Volume Slider
175         fVolumeSlider = new VolumeSlider(BRect(0.0, 0.0, VOLUME_MIN_WIDTH,
176                                                                                    kVolumeSliderBitmapHeight - 1.0),
177                                                                          "volume slider", 1, AOUT_VOLUME_MAX,
178                                                                          new BMessage(VOLUME_CHG));
179         fVolumeSlider->SetValue( config_GetInt( p_intf, "volume" ) );
180         AddChild( fVolumeSlider );
181         
182         // Position Info View
183     fPositionInfo = new PositionInfoView(BRect(0.0, 0.0, 10.0, 10.0), "led",
184                                          p_intf);
185     fPositionInfo->ResizeToPreferred();
186     AddChild( fPositionInfo );
187 }
188
189 // destructor
190 MediaControlView::~MediaControlView()
191 {
192 }
193
194 // AttachedToWindow
195 void
196 MediaControlView::AttachedToWindow()
197 {
198         // we are now a valid BHandler
199         fRewind->SetTarget(this);
200         fForward->SetTarget(this);
201         fSkipBack->SetTarget(this);
202         fSkipForward->SetTarget(this);
203         fVolumeSlider->SetTarget(Window());
204
205         BRect r(_MinFrame());
206         if (BMenuBar* menuBar = Window()->KeyMenuBar())
207                 r.bottom += menuBar->Bounds().Height();
208
209         Window()->SetSizeLimits(r.Width(), r.Width() * 1.8, r.Height(), r.Height() * 1.3);
210         if (!Window()->Bounds().Contains(r))
211                 Window()->ResizeTo(r.Width(), r.Height());
212         else
213                 FrameResized(Bounds().Width(), Bounds().Height());
214
215         // get pulse message every two frames
216         Window()->SetPulseRate(80000);
217 }
218
219 // FrameResized
220 void
221 MediaControlView::FrameResized(float width, float height)
222 {
223         BRect r(Bounds());
224         // make sure we don't leave dirty pixels
225         // (B_FULL_UPDATE_ON_RESIZE == annoying flicker -> this is smarter)
226         if (fOldBounds.Width() < r.Width())
227                 Invalidate(BRect(fOldBounds.right, fOldBounds.top + 1.0,
228                                                  fOldBounds.right, fOldBounds.bottom - 1.0));
229         else
230                 Invalidate(BRect(r.right, r.top + 1.0,
231                                                  r.right, r.bottom - 1.0));
232         if (fOldBounds.Height() < r.Height())
233                 Invalidate(BRect(fOldBounds.left + 1.0, fOldBounds.bottom,
234                                                  fOldBounds.right - 1.0, fOldBounds.bottom));
235         else
236                 Invalidate(BRect(r.left + 1.0, r.bottom,
237                                                  r.right - 1.0, r.bottom));
238         // remember for next time
239         fOldBounds = r;
240         // layout controls
241         r.InsetBy(BORDER_INSET, BORDER_INSET);
242         _LayoutControls(r);
243 }
244
245 // GetPreferredSize
246 void
247 MediaControlView::GetPreferredSize(float* width, float* height)
248 {
249         if (width && height)
250         {
251                 BRect r(_MinFrame());
252                 *width = r.Width();
253                 *height = r.Height();
254         }
255 }
256
257 // MessageReceived
258 void
259 MediaControlView::MessageReceived(BMessage* message)
260 {
261         switch (message->what)
262         {
263                 case MSG_REWIND:
264                         break;
265                 case MSG_FORWARD:
266                         break;
267                 case MSG_SKIP_BACKWARDS:
268                         Window()->PostMessage(NAVIGATE_PREV);
269                         break;
270                 case MSG_SKIP_FORWARD:
271                         Window()->PostMessage(NAVIGATE_NEXT);
272                         break;
273                 default:
274                     BBox::MessageReceived(message);
275                     break;
276         }
277 }
278
279 // Pulse
280 void
281 MediaControlView::Pulse()
282 {
283         InterfaceWindow* window = dynamic_cast<InterfaceWindow*>(Window());
284         if (window && window->IsStopped())
285                         fPlayPause->SetStopped();
286
287     unsigned short i_volume;
288     aout_VolumeGet( p_intf, (audio_volume_t*)&i_volume );
289     fVolumeSlider->SetValue( i_volume );
290 }
291
292 // SetProgress
293 void
294 MediaControlView::SetProgress( float position )
295 {
296         fSeekSlider->SetPosition( position );
297 }
298
299 // SetStatus
300 void
301 MediaControlView::SetStatus(int status, int rate)
302 {
303         // we need to set the button status periodically
304         // (even if it is the same) to get a blinking button
305         fCurrentStatus = status;
306     switch( status )
307     {
308         case PLAYING_S:
309         case FORWARD_S:
310         case BACKWARD_S:
311             fPlayPause->SetPlaying();
312             break;
313         case PAUSE_S:
314             fPlayPause->SetPaused();
315             break;
316         case UNDEF_S:
317         default:
318             fPlayPause->SetStopped();
319             break;
320     }
321         if (rate != fCurrentRate)
322         {
323                 fCurrentRate = rate;
324             if ( rate < DEFAULT_RATE )
325             {
326                 // TODO: ...
327             }
328         }
329 }
330
331 // SetEnabled
332 void
333 MediaControlView::SetEnabled(bool enabled)
334 {
335     if( ( enabled && fIsEnabled ) ||
336         ( !enabled && !fIsEnabled ) )
337     {
338         /* do not redraw if it is not necessary */
339         return;
340     }
341     
342         if( LockLooper() )
343         {
344                 fSkipBack->SetEnabled( enabled );
345                 fPlayPause->SetEnabled( enabled );
346                 fSkipForward->SetEnabled( enabled );
347                 fStop->SetEnabled( enabled );
348                 fMute->SetEnabled( enabled );
349                 fVolumeSlider->SetEnabled( enabled );
350                 fSeekSlider->SetEnabled( enabled );
351                 fRewind->SetEnabled( enabled );
352                 fForward->SetEnabled( enabled );
353                 UnlockLooper();
354                 fIsEnabled = enabled;
355         }
356 }
357
358 // SetAudioEnabled
359 void
360 MediaControlView::SetAudioEnabled(bool enabled)
361 {
362         fMute->SetEnabled(enabled);
363         fVolumeSlider->SetEnabled(enabled);
364 }
365
366 // GetSeekTo
367 uint32
368 MediaControlView::GetSeekTo() const
369 {
370         return fSeekSlider->Value();
371 }
372
373 // GetVolume
374 uint32
375 MediaControlView::GetVolume() const
376 {
377         return fVolumeSlider->Value();
378 }
379
380 // SetSkippable
381 void
382 MediaControlView::SetSkippable(bool backward, bool forward)
383 {
384         fSkipBack->SetEnabled(backward);
385         fSkipForward->SetEnabled(forward);
386 }
387
388 // SetMuted
389 void
390 MediaControlView::SetMuted(bool mute)
391 {
392         fVolumeSlider->SetMuted(mute);
393 }
394
395 // _LayoutControls
396 void
397 MediaControlView::_LayoutControls(BRect frame) const
398 {
399         // seek slider
400         BRect r(frame);
401         // calculate absolutly minimal width
402         float minWidth = fSkipBack->Bounds().Width();
403 //      minWidth += fRewind->Bounds().Width();
404         minWidth += fStop->Bounds().Width();
405         minWidth += fPlayPause->Bounds().Width();
406 //      minWidth += fForward->Bounds().Width();
407         minWidth += fSkipForward->Bounds().Width();
408         minWidth += fMute->Bounds().Width();
409         minWidth += VOLUME_MIN_WIDTH;
410         
411         // layout time slider and info view
412     float width, height;
413     fPositionInfo->GetBigPreferredSize( &width, &height );
414     float ratio = width / height;
415     width = r.Height() * ratio;
416     if (frame.Width() - minWidth - MIN_SPACE >= width
417               && frame.Height() >= height)
418     {
419         r.right = r.left + width;
420         fPositionInfo->SetMode(PositionInfoView::MODE_BIG);
421         _LayoutControl(fPositionInfo, r, true, true);
422         frame.left = r.right + MIN_SPACE;
423         r.left = frame.left;
424         r.right = frame.right;
425     //    r.bottom = r.top + r.Height() / 2.0 - MIN_SPACE / 2.0;
426         r.bottom = r.top + fSeekSlider->Bounds().Height();
427         _LayoutControl(fSeekSlider, r, true);
428     }
429     else
430     {
431         fPositionInfo->GetPreferredSize( &width, &height );
432         fPositionInfo->SetMode(PositionInfoView::MODE_SMALL);
433         fPositionInfo->ResizeTo(width, height);
434         r.bottom = r.top + r.Height() / 2.0 - MIN_SPACE / 2.0;
435         r.right = r.left + fPositionInfo->Bounds().Width();
436         _LayoutControl(fPositionInfo, r, true );
437         r.left = r.right + MIN_SPACE;
438         r.right = frame.right;
439         _LayoutControl(fSeekSlider, r, true);
440     }
441         float currentWidth = frame.Width();
442         float space = (currentWidth - minWidth) / 6.0;//8.0;
443         // apply weighting
444         space = MIN_SPACE + (space - MIN_SPACE) / VOLUME_SLIDER_LAYOUT_WEIGHT;
445         // layout controls with "space" inbetween
446         r.left = frame.left;
447         r.top = r.bottom + MIN_SPACE + 1.0;
448         r.bottom = frame.bottom;
449         // skip back
450         r.right = r.left + fSkipBack->Bounds().Width();
451         _LayoutControl(fSkipBack, r);
452         // rewind
453 //      r.left = r.right + space;
454 //      r.right = r.left + fRewind->Bounds().Width();
455 //      _LayoutControl(fRewind, r);
456         // stop
457         r.left = r.right + space;
458         r.right = r.left + fStop->Bounds().Width();
459         _LayoutControl(fStop, r);
460         // play/pause
461         r.left = r.right + space;
462         r.right = r.left + fPlayPause->Bounds().Width();
463         _LayoutControl(fPlayPause, r);
464         // forward
465 //      r.left = r.right + space;
466 //      r.right = r.left + fForward->Bounds().Width();
467 //      _LayoutControl(fForward, r);
468         // skip forward
469         r.left = r.right + space;
470         r.right = r.left + fSkipForward->Bounds().Width();
471         _LayoutControl(fSkipForward, r);
472         // speaker icon
473         r.left = r.right + space + space;
474         r.right = r.left + fMute->Bounds().Width();
475         _LayoutControl(fMute, r);
476         // volume slider
477         r.left = r.right + SPEAKER_SLIDER_DIST; // keep speaker icon and volume slider attached
478         r.right = frame.right;
479         _LayoutControl(fVolumeSlider, r, true);
480 }
481
482 // _MinFrame
483 BRect           
484 MediaControlView::_MinFrame() const
485 {
486         // add up width of controls along bottom (seek slider will likely adopt)
487         float minWidth = 2 * BORDER_INSET;
488         minWidth += fSkipBack->Bounds().Width() + MIN_SPACE;
489 //      minWidth += fRewind->Bounds().Width() + MIN_SPACE;
490         minWidth += fStop->Bounds().Width() + MIN_SPACE;
491         minWidth += fPlayPause->Bounds().Width() + MIN_SPACE;
492 //      minWidth += fForward->Bounds().Width() + MIN_SPACE;
493         minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
494         minWidth += fMute->Bounds().Width() + SPEAKER_SLIDER_DIST;
495         minWidth += VOLUME_MIN_WIDTH;
496
497         // add up height of seek slider and heighest control on bottom
498         float minHeight = 2 * BORDER_INSET;
499         minHeight += fSeekSlider->Bounds().Height() + MIN_SPACE + MIN_SPACE / 2.0;
500         minHeight += fBottomControlHeight;
501         return BRect(0.0, 0.0, minWidth - 1.0, minHeight - 1.0);
502 }
503
504 // _LayoutControl
505 void
506 MediaControlView::_LayoutControl(BView* view, BRect frame,
507                                  bool resizeWidth, bool resizeHeight) const
508 {
509     if (!resizeHeight)
510             // center vertically
511             frame.top = (frame.top + frame.bottom) / 2.0 - view->Bounds().Height() / 2.0;
512         if (!resizeWidth)
513             //center horizontally
514                 frame.left = (frame.left + frame.right) / 2.0 - view->Bounds().Width() / 2.0;
515         view->MoveTo(frame.LeftTop());
516         float width = resizeWidth ? frame.Width() : view->Bounds().Width();
517         float height = resizeHeight ? frame.Height() : view->Bounds().Height();
518     if (resizeWidth || resizeHeight)
519         view->ResizeTo(width, height);
520 }
521
522
523
524 /*****************************************************************************
525  * SeekSlider
526  *****************************************************************************/
527 SeekSlider::SeekSlider(BRect frame, const char* name, MediaControlView *owner,
528                                            int32 minValue, int32 maxValue)
529         : BControl(frame, name, NULL, NULL, B_FOLLOW_NONE,
530                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
531           fOwner(owner),
532           fTracking(false),
533           fMinValue(minValue),
534           fMaxValue(maxValue)
535 {
536         BFont font(be_plain_font);
537         font.SetSize(9.0);
538         SetFont(&font);
539 }
540
541 SeekSlider::~SeekSlider()
542 {
543         _EndSeek();
544 }
545
546 /*****************************************************************************
547  * VolumeSlider::AttachedToWindow
548  *****************************************************************************/
549 void
550 SeekSlider::AttachedToWindow()
551 {
552         BControl::AttachedToWindow();
553         SetViewColor(B_TRANSPARENT_32_BIT);
554 }
555
556 /*****************************************************************************
557  * VolumeSlider::Draw
558  *****************************************************************************/
559 void
560 SeekSlider::Draw(BRect updateRect)
561 {
562         BRect r(Bounds());
563         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
564         float sliderStart = (r.left + knobWidth2);
565         float sliderEnd = (r.right - knobWidth2);
566         float knobPos = sliderStart
567                                         + floorf((sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
568                                         / (fMaxValue - fMinValue) + 0.5);
569         // draw both sides (the original from Be doesn't seem
570         // to make a difference for enabled/disabled state)
571 //      DrawBitmapAsync(fLeftSideBits, r.LeftTop());
572 //      DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
573         // colors for the slider area between the two bitmaps
574         rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
575         rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
576         rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
577         rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
578         rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
579         rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
580         rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
581         rgb_color green = kSeekGreen;
582         rgb_color greenShadow = kSeekGreenShadow;
583         rgb_color black = kBlack;
584         rgb_color dotGrey = midShadow;
585         rgb_color dotGreen = greenShadow;
586         // draw frame
587         _StrokeFrame(r, softShadow, softShadow, softLight, softLight);
588         r.InsetBy(1.0, 1.0);
589         _StrokeFrame(r, black, black, light, light);
590         if (IsEnabled())
591         {
592                 r.InsetBy(1.0, 1.0);
593                 // inner shadow
594                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
595                 r.top++;
596                 r.left++;
597                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
598                 // inside area
599                 r.InsetBy(1.0, 1.0);
600                 SetHighColor(green);
601                 FillRect(r);
602                 // dots
603                 int32 dotCount = (int32)(r.Width() / 6.0);
604                 BPoint dotPos;
605                 dotPos.y = r.top + 2.0;
606                 SetHighColor(dotGreen);
607                 for (int32 i = 0; i < dotCount; i++)
608                 {
609                         dotPos.x = sliderStart + i * 6.0 + 5.0;
610                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 6.0));
611                 }
612                 // slider handle
613                 r.top -= 4.0;
614                 r.bottom += 3.0;
615                 r.left = knobPos - knobWidth2;
616                 r.right = knobPos + knobWidth2;
617                 // black outline
618                 float handleBottomSize = 2.0;
619                 float handleArrowSize = 6.0;
620                 BeginLineArray(10);
621                         // upper handle
622                         AddLine(BPoint(r.left, r.top + handleBottomSize),
623                                         BPoint(r.left, r.top), black);
624                         AddLine(BPoint(r.left + 1.0, r.top),
625                                         BPoint(r.right, r.top), black);
626                         AddLine(BPoint(r.right, r.top + 1.0),
627                                         BPoint(r.right, r.top + handleBottomSize), black);
628                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
629                                         BPoint(knobPos, r.top + handleArrowSize), black);
630                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
631                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), black);
632                         // lower handle
633                         AddLine(BPoint(r.left, r.bottom),
634                                         BPoint(r.left, r.bottom - handleBottomSize), black);
635                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
636                                         BPoint(knobPos, r.bottom - handleArrowSize), black);
637                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
638                                         BPoint(r.right, r.bottom - handleBottomSize), black);
639                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
640                                         BPoint(r.right, r.bottom), black);
641                         AddLine(BPoint(r.right - 1.0, r.bottom),
642                                         BPoint(r.left + 1.0, r.bottom), black);
643                 EndLineArray();
644                 // inner red light and shadow lines
645                 r.InsetBy(1.0, 1.0);
646                 handleBottomSize--;
647                 handleArrowSize -= 2.0;
648                 BeginLineArray(10);
649                         // upper handle
650                         AddLine(BPoint(r.left, r.top + handleBottomSize),
651                                         BPoint(r.left, r.top), kSeekRedLight);
652                         AddLine(BPoint(r.left + 1.0, r.top),
653                                         BPoint(r.right, r.top), kSeekRedLight);
654                         AddLine(BPoint(r.right, r.top + 1.0),
655                                         BPoint(r.right, r.top + handleBottomSize), kSeekRedShadow);
656                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
657                                         BPoint(knobPos, r.top + handleArrowSize), kSeekRedShadow);
658                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
659                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), kSeekRedLight);
660                         // lower handle
661                         AddLine(BPoint(r.left, r.bottom),
662                                         BPoint(r.left, r.bottom - handleBottomSize), kSeekRedLight);
663                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
664                                         BPoint(knobPos, r.bottom - handleArrowSize), kSeekRedLight);
665                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
666                                         BPoint(r.right, r.bottom - handleBottomSize), kSeekRedShadow);
667                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
668                                         BPoint(r.right, r.bottom), kSeekRedShadow);
669                         AddLine(BPoint(r.right - 1.0, r.bottom),
670                                         BPoint(r.left + 1.0, r.bottom), kSeekRedShadow);
671                 EndLineArray();
672                 // fill rest of handles with red
673                 SetHighColor(kSeekRed);
674                 r.InsetBy(1.0, 1.0);
675                 handleArrowSize -= 2.0;
676                 BPoint arrow[3];
677                 // upper handle arrow
678                 arrow[0].x = r.left;
679                 arrow[0].y = r.top;
680                 arrow[1].x = r.right;
681                 arrow[1].y = r.top;
682                 arrow[2].x = knobPos;
683                 arrow[2].y = r.top + handleArrowSize;
684                 FillPolygon(arrow, 3);
685                 // lower handle arrow
686                 arrow[0].x = r.left;
687                 arrow[0].y = r.bottom;
688                 arrow[1].x = r.right;
689                 arrow[1].y = r.bottom;
690                 arrow[2].x = knobPos;
691                 arrow[2].y = r.bottom - handleArrowSize;
692                 FillPolygon(arrow, 3);
693         }
694         else
695         {
696                 r.InsetBy(1.0, 1.0);
697                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
698                 r.InsetBy(1.0, 1.0);
699                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
700                 r.InsetBy(1.0, 1.0);
701                 SetHighColor(darkShadow);
702                 SetLowColor(shadow);
703                 // stripes
704                 float width = floorf(StringWidth(DISABLED_SEEK_MESSAGE));
705                 float textPos = r.left + r.Width() / 2.0 - width / 2.0;
706                 pattern stripes = {{ 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3 }};
707                 BRect stripesRect(r);
708                 stripesRect.right = textPos - 5.0;
709                 FillRect(stripesRect, stripes);
710                 stripesRect.left = textPos + width + 3.0;
711                 stripesRect.right = r.right;
712                 FillRect(stripesRect, stripes);
713                 // info text
714                 r.left = textPos - 4.0;
715                 r.right = textPos + width + 2.0;
716                 FillRect(r);
717                 SetHighColor(shadow);
718                 SetLowColor(darkShadow);
719                 font_height fh;
720                 GetFontHeight(&fh);
721                 DrawString(DISABLED_SEEK_MESSAGE, BPoint(textPos, r.top + ceilf(fh.ascent) - 1.0));
722         }
723 }
724
725 /*****************************************************************************
726  * SeekSlider::MouseDown
727  *****************************************************************************/
728 void
729 SeekSlider::MouseDown(BPoint where)
730 {
731         if (IsEnabled() && Bounds().Contains(where))
732         {
733                 SetValue(_ValueFor(where.x));
734                 fTracking = true;
735                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
736                 _BeginSeek();
737         }
738 }
739
740 /*****************************************************************************
741  * SeekSlider::MouseMoved
742  *****************************************************************************/
743 void
744 SeekSlider::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
745 {
746         if (fTracking)
747         {
748                 SetValue(_ValueFor(where.x));
749                 _Seek();
750         }
751 }
752
753 /*****************************************************************************
754  * SeekSlider::MouseUp
755  *****************************************************************************/
756 void
757 SeekSlider::MouseUp(BPoint where)
758 {
759         if (fTracking)
760         {
761                 fTracking = false;
762                 _EndSeek();
763         }
764 }
765
766 /*****************************************************************************
767  * SeekSlider::ResizeToPreferred
768  *****************************************************************************/
769 void
770 SeekSlider::ResizeToPreferred()
771 {
772         float width = 15.0 + StringWidth(DISABLED_SEEK_MESSAGE) + 15.0;
773         ResizeTo(width, 17.0);
774 }
775
776 /*****************************************************************************
777  * SeekSlider::SetPosition
778  *****************************************************************************/
779 void
780 SeekSlider::SetPosition(float position)
781 {
782         if ( LockLooper() )
783         {
784                 SetValue(fMinValue + (int32)floorf((fMaxValue - fMinValue) * position + 0.5));
785                 UnlockLooper();
786         }
787 }
788
789 /*****************************************************************************
790  * SeekSlider::_ValueFor
791  *****************************************************************************/
792 int32
793 SeekSlider::_ValueFor(float xPos) const
794 {
795         BRect r(Bounds());
796         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
797         float sliderStart = (r.left + knobWidth2);
798         float sliderEnd = (r.right - knobWidth2);
799         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
800                                   / (sliderEnd - sliderStart - 1.0));
801         if (value < fMinValue)
802                 value = fMinValue;
803         if (value > fMaxValue)
804                 value = fMaxValue;
805         return value;
806 }
807
808 /*****************************************************************************
809  * SeekSlider::_StrokeFrame
810  *****************************************************************************/
811 void
812 SeekSlider::_StrokeFrame(BRect r, rgb_color left, rgb_color top,
813                                                  rgb_color right, rgb_color bottom)
814 {
815         BeginLineArray(4);
816                 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), left);
817                 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), top);
818                 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), right);
819                 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), bottom);
820         EndLineArray();
821 }
822
823 /*****************************************************************************
824  * SeekSlider::_BeginSeek
825  *****************************************************************************/
826 void
827 SeekSlider::_BeginSeek()
828 {
829         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
830         if (fOwner->fScrubSem >= B_OK)
831                 release_sem(fOwner->fScrubSem);
832 }
833
834 /*****************************************************************************
835  * SeekSlider::_Seek
836  *****************************************************************************/
837 void
838 SeekSlider::_Seek()
839 {
840         if (fOwner->fScrubSem >= B_OK)
841                 delete_sem(fOwner->fScrubSem);
842         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
843         if (fOwner->fScrubSem >= B_OK)
844                 release_sem(fOwner->fScrubSem);
845 }
846
847 /*****************************************************************************
848  * SeekSlider::_EndSeek
849  *****************************************************************************/
850 void
851 SeekSlider::_EndSeek()
852 {
853         if (fOwner->fScrubSem >= B_OK)
854                 delete_sem(fOwner->fScrubSem);
855         fOwner->fScrubSem = B_ERROR;
856 }
857
858
859 /*****************************************************************************
860  * VolumeSlider
861  *****************************************************************************/
862 VolumeSlider::VolumeSlider(BRect frame, const char* name, int32 minValue, int32 maxValue,
863                                                    BMessage* message, BHandler* target)
864         : BControl(frame, name, NULL, message, B_FOLLOW_NONE,
865                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
866           fLeftSideBits(NULL),
867           fRightSideBits(NULL),
868           fKnobBits(NULL),
869           fTracking(false),
870           fMuted(false),
871           fMinValue(minValue),
872           fMaxValue(maxValue)
873 {
874         SetTarget(target);
875
876         // create bitmaps
877         BRect r(BPoint(0.0, 0.0), kVolumeSliderBitmapSize);
878         fLeftSideBits = new BBitmap(r, B_CMAP8);
879         fRightSideBits = new BBitmap(r, B_CMAP8);
880         r.Set(0.0, 0.0, kVolumeSliderKnobBitmapSize.x, kVolumeSliderKnobBitmapSize.y);
881         fKnobBits = new BBitmap(r, B_CMAP8);
882
883         _MakeBitmaps();
884 }
885
886 /*****************************************************************************
887  * VolumeSlider destructor
888  *****************************************************************************/
889 VolumeSlider::~VolumeSlider()
890 {
891         delete fLeftSideBits;
892         delete fRightSideBits;
893         delete fKnobBits;
894 }
895
896 /*****************************************************************************
897  * VolumeSlider::AttachedToWindow
898  *****************************************************************************/
899 void
900 VolumeSlider::AttachedToWindow()
901 {
902         BControl::AttachedToWindow();
903         SetViewColor(B_TRANSPARENT_32_BIT);
904 }
905
906 /*****************************************************************************
907  * VolumeSlider::SetValue
908  *****************************************************************************/
909 void
910 VolumeSlider::SetValue(int32 value)
911 {
912         if (value != Value())
913         {
914                 BControl::SetValue(value);
915                 Invoke();
916         }
917 }
918
919 /*****************************************************************************
920  * VolumeSlider::SetEnabled
921  *****************************************************************************/
922 void
923 VolumeSlider::SetEnabled(bool enable)
924 {
925         if (enable != IsEnabled())
926         {
927                 BControl::SetEnabled(enable);
928                 _MakeBitmaps();
929                 Invalidate();
930         }
931 }
932
933 /*****************************************************************************
934  * VolumeSlider::Draw
935  *****************************************************************************/
936 void
937 VolumeSlider::Draw(BRect updateRect)
938 {
939         if (IsValid())
940         {
941                 BRect r(Bounds());
942                 float sliderSideWidth = kVolumeSliderBitmapWidth;
943                 float sliderStart = (r.left + sliderSideWidth);
944                 float sliderEnd = (r.right - sliderSideWidth);
945                 float knobPos = sliderStart
946                                                 + (sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
947                                                 / (fMaxValue - fMinValue);
948                 // draw both sides (the original from Be doesn't seem
949                 // to make a difference for enabled/disabled state)
950                 DrawBitmapAsync(fLeftSideBits, r.LeftTop());
951                 DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
952                 // colors for the slider area between the two bitmaps
953                 rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
954                 rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
955                 rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
956                 rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
957                 rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
958                 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
959                 rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
960                 rgb_color green = kGreen;
961                 rgb_color greenShadow = kGreenShadow;
962                 rgb_color black = kBlack;
963                 rgb_color dotGrey = midShadow;
964                 rgb_color dotGreen = greenShadow;
965                 // make dimmed version of colors if we're disabled
966                 if (!IsEnabled())
967                 {
968                         shadow = (rgb_color){ 200, 200, 200, 255 };
969                         softShadow = dimmed_color_cmap8(softShadow, background, DIM_LEVEL);
970                         darkShadow = dimmed_color_cmap8(darkShadow, background, DIM_LEVEL);
971                         midShadow = shadow;
972                         light = dimmed_color_cmap8(light, background, DIM_LEVEL);
973                         softLight = dimmed_color_cmap8(softLight, background, DIM_LEVEL);
974                         green = dimmed_color_cmap8(green, background, DIM_LEVEL);
975                         greenShadow = dimmed_color_cmap8(greenShadow, background, DIM_LEVEL);
976                         black = dimmed_color_cmap8(black, background, DIM_LEVEL);
977                         dotGreen = dotGrey;
978                 }
979                 else if (fMuted)
980                 {
981                         green = tint_color(kBackground, B_DARKEN_3_TINT);
982                         greenShadow = tint_color(kBackground, B_DARKEN_4_TINT);
983                         dotGreen = greenShadow;
984                 }
985                 // draw slider edges between bitmaps
986                 BeginLineArray(7);
987                         AddLine(BPoint(sliderStart, r.top),
988                                         BPoint(sliderEnd, r.top), softShadow);
989                         AddLine(BPoint(sliderStart, r.bottom),
990                                         BPoint(sliderEnd, r.bottom), softLight);
991                         r.InsetBy(0.0, 1.0);
992                         AddLine(BPoint(sliderStart, r.top),
993                                         BPoint(sliderEnd, r.top), black);
994                         AddLine(BPoint(sliderStart, r.bottom),
995                                         BPoint(sliderEnd, r.bottom), light);
996                         r.top++;
997                         AddLine(BPoint(sliderStart, r.top),
998                                         BPoint(knobPos, r.top), greenShadow);
999                         AddLine(BPoint(knobPos, r.top),
1000                                         BPoint(sliderEnd, r.top), midShadow);
1001                         r.top++;
1002                         AddLine(BPoint(sliderStart, r.top),
1003                                         BPoint(knobPos, r.top), greenShadow);
1004                 EndLineArray();
1005                 // fill rest inside of slider
1006                 r.InsetBy(0.0, 1.0);
1007                 r.left = sliderStart;
1008                 r.right = knobPos;
1009                 SetHighColor(green);
1010                 FillRect(r, B_SOLID_HIGH);
1011                 r.left = knobPos + 1.0;
1012                 r.right = sliderEnd;
1013                 r.top -= 1.0;
1014                 SetHighColor(shadow);
1015                 FillRect(r, B_SOLID_HIGH);
1016                 // draw little dots inside
1017                 int32 dotCount = (int32)((sliderEnd - sliderStart) / 5.0);
1018                 BPoint dotPos;
1019                 dotPos.y = r.top + 4.0;
1020                 for (int32 i = 0; i < dotCount; i++)
1021                 {
1022                         dotPos.x = sliderStart + i * 5.0 + 4.0;
1023                         SetHighColor(dotPos.x < knobPos ? dotGreen : dotGrey);
1024                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 1.0));
1025                 }
1026                 // draw knob
1027                 r.top -= 1.0;
1028                 SetDrawingMode(B_OP_OVER); // part of knob is transparent
1029                 DrawBitmapAsync(fKnobBits, BPoint(knobPos - kVolumeSliderKnobWidth / 2, r.top));
1030         }
1031 }
1032
1033 /*****************************************************************************
1034  * VolumeSlider::MouseDown
1035  *****************************************************************************/
1036 void
1037 VolumeSlider::MouseDown(BPoint where)
1038 {
1039         if (Bounds().Contains(where) && IsEnabled())
1040         {
1041                 fTracking = true;
1042                 SetValue(_ValueFor(where.x));
1043                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
1044         }
1045 }
1046
1047 /*****************************************************************************
1048  * VolumeSlider::MouseMoved
1049  *****************************************************************************/
1050 void
1051 VolumeSlider::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
1052 {
1053         if (fTracking)
1054                 SetValue(_ValueFor(where.x));
1055 }
1056
1057 /*****************************************************************************
1058  * VolumeSlider::MouseUp
1059  *****************************************************************************/
1060 void
1061 VolumeSlider::MouseUp(BPoint where)
1062 {
1063         fTracking = false;
1064 }
1065
1066
1067 /*****************************************************************************
1068  * VolumeSlider::IsValid
1069  *****************************************************************************/
1070 bool
1071 VolumeSlider::IsValid() const
1072 {
1073         return (fLeftSideBits && fLeftSideBits->IsValid()
1074                         && fRightSideBits && fRightSideBits->IsValid()
1075                         && fKnobBits && fKnobBits->IsValid());
1076 }
1077
1078 /*****************************************************************************
1079  * VolumeSlider::SetMuted
1080  *****************************************************************************/
1081 void
1082 VolumeSlider::SetMuted(bool mute)
1083 {
1084         if (mute != fMuted)
1085         {
1086                 fMuted = mute;
1087                 _MakeBitmaps();
1088                 Invalidate();
1089         }
1090 }
1091
1092 /*****************************************************************************
1093  * VolumeSlider::_MakeBitmaps
1094  *****************************************************************************/
1095 void
1096 VolumeSlider::_MakeBitmaps()
1097 {
1098         if (IsValid())
1099         {
1100                 // left side of slider
1101                 memcpy(fLeftSideBits->Bits(), kVolumeSliderLeftBitmapBits,
1102                            fLeftSideBits->BitsLength());
1103                 // right side of slider
1104                 memcpy(fRightSideBits->Bits(), kVolumeSliderRightBits,
1105                            fRightSideBits->BitsLength());
1106                 // slider knob
1107                 int32 length = fKnobBits->BitsLength();
1108                 memcpy(fKnobBits->Bits(), kVolumeSliderKnobBits, length);
1109                 uint8* bits = (uint8*)fKnobBits->Bits();
1110                 // black was used in the knob to represent transparency
1111                 // use screen to get index for the "transarent" color used in the bitmap
1112                 BScreen screen(B_MAIN_SCREEN_ID);
1113                 uint8 blackIndex = screen.IndexForColor(kBlack);
1114                 // replace black index with transparent index
1115                 for (int32 i = 0; i < length; i++)
1116                         if (bits[i] == blackIndex)
1117                                 bits[i] = B_TRANSPARENT_MAGIC_CMAP8;
1118
1119                 if (!IsEnabled())
1120                 {
1121                         // make ghosted versions of the bitmaps
1122                         dim_bitmap(fLeftSideBits, kBackground, DIM_LEVEL);
1123                         dim_bitmap(fRightSideBits, kBackground, DIM_LEVEL);
1124                         dim_bitmap(fKnobBits, kBackground, DIM_LEVEL);
1125                 }
1126                 else if (fMuted)
1127                 {
1128                         // replace green color (and shadow) in left slider side
1129                         bits = (uint8*)fLeftSideBits->Bits();
1130                         length = fLeftSideBits->BitsLength();
1131                         uint8 greenIndex = screen.IndexForColor(kGreen);
1132                         uint8 greenShadowIndex = screen.IndexForColor(kGreenShadow);
1133                         rgb_color shadow = tint_color(kBackground, B_DARKEN_3_TINT);
1134                         rgb_color midShadow = tint_color(kBackground, B_DARKEN_4_TINT);
1135                         uint8 replaceIndex = screen.IndexForColor(shadow);
1136                         uint8 replaceShadowIndex = screen.IndexForColor(midShadow);
1137                         for (int32 i = 0; i < length; i++)
1138                         {
1139                                 if (bits[i] == greenIndex)
1140                                         bits[i] = replaceIndex;
1141                                 else if (bits[i] == greenShadowIndex)
1142                                         bits[i] = replaceShadowIndex;
1143                         }
1144                 }
1145         }
1146 }
1147
1148 /*****************************************************************************
1149  * VolumeSlider::_ValueFor
1150  *****************************************************************************/
1151 int32
1152 VolumeSlider::_ValueFor(float xPos) const
1153 {
1154         BRect r(Bounds());
1155         float sliderStart = (r.left + kVolumeSliderBitmapWidth);
1156         float sliderEnd = (r.right - kVolumeSliderBitmapWidth);
1157         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
1158                                   / (sliderEnd - sliderStart - 1.0));
1159         if (value < fMinValue)
1160                 value = fMinValue;
1161         if (value > fMaxValue)
1162                 value = fMaxValue;
1163         return value;
1164 }
1165
1166 /*****************************************************************************
1167  * PositionInfoView::PositionInfoView
1168  *****************************************************************************/
1169 PositionInfoView::PositionInfoView( BRect frame, const char* name,
1170                                     intf_thread_t * p_interface )
1171         : BView( frame, name, B_FOLLOW_NONE,
1172                          B_WILL_DRAW | B_PULSE_NEEDED | B_FULL_UPDATE_ON_RESIZE ),
1173           fMode( MODE_SMALL ),
1174           fCurrentFileIndex( -1 ),
1175           fCurrentFileSize( -1 ),
1176           fCurrentTitleIndex( -1 ),
1177           fCurrentTitleSize( -1 ),
1178           fCurrentChapterIndex( -1 ),
1179           fCurrentChapterSize( -1 ),
1180           fSeconds( -1 ),
1181           fTimeString( "-:--:--" ),
1182           fLastPulseUpdate( system_time() ),
1183           fStackedWidthCache( 0.0 ),
1184           fStackedHeightCache( 0.0 )
1185 {
1186     p_intf = p_interface;
1187
1188         SetViewColor( B_TRANSPARENT_32_BIT );
1189         SetLowColor( kBlack );
1190         SetHighColor( 0, 255, 0, 255 );
1191         SetFontSize( 11.0 );
1192 }
1193
1194 /*****************************************************************************
1195  * PositionInfoView::~PositionInfoView
1196  *****************************************************************************/
1197 PositionInfoView::~PositionInfoView()
1198 {
1199 }
1200
1201 /*****************************************************************************
1202  * PositionInfoView::Draw
1203  *****************************************************************************/
1204 void
1205 PositionInfoView::Draw( BRect updateRect )
1206 {
1207         rgb_color background = ui_color( B_PANEL_BACKGROUND_COLOR );
1208         rgb_color shadow = tint_color( background, B_DARKEN_1_TINT );
1209         rgb_color darkShadow = tint_color( background, B_DARKEN_4_TINT );
1210         rgb_color light = tint_color( background, B_LIGHTEN_MAX_TINT );
1211         rgb_color softLight = tint_color( background, B_LIGHTEN_1_TINT );
1212         // frame
1213         BRect r( Bounds() );
1214         BeginLineArray( 8 );
1215                 AddLine( BPoint( r.left, r.bottom ),
1216                                  BPoint( r.left, r.top ), shadow );
1217                 AddLine( BPoint( r.left + 1.0, r.top ),
1218                                  BPoint( r.right, r.top ), shadow );
1219                 AddLine( BPoint( r.right, r.top + 1.0 ),
1220                                  BPoint( r.right, r.bottom ), softLight );
1221                 AddLine( BPoint( r.right - 1.0, r.bottom ),
1222                                  BPoint( r.left + 1.0, r.bottom ), softLight );
1223                 r.InsetBy( 1.0, 1.0 );
1224                 AddLine( BPoint( r.left, r.bottom ),
1225                                  BPoint( r.left, r.top ), darkShadow );
1226                 AddLine( BPoint( r.left + 1.0, r.top ),
1227                                  BPoint( r.right, r.top ), darkShadow );
1228                 AddLine( BPoint( r.right, r.top + 1.0 ),
1229                                  BPoint( r.right, r.bottom ), light );
1230                 AddLine( BPoint( r.right - 1.0, r.bottom ),
1231                                  BPoint( r.left + 1.0, r.bottom ), light );
1232         EndLineArray();
1233         // background
1234         r.InsetBy( 1.0, 1.0 );
1235         FillRect( r, B_SOLID_LOW );
1236         // contents
1237         font_height fh;
1238         GetFontHeight( &fh );
1239         switch ( fMode )
1240         {
1241                 case MODE_SMALL:
1242                 {
1243                         float width = StringWidth( fTimeString.String() );
1244                         DrawString( fTimeString.String(),
1245                                                 BPoint( r.left + r.Width() / 2.0 - width / 2.0,
1246                                                                 r.top + r.Height() / 2.0 + fh.ascent / 2.0 - 1.0 ) );
1247                         break;
1248                 }
1249                 case MODE_BIG:
1250                 {
1251                         BFont font;
1252                         GetFont( &font );
1253                         BFont smallFont = font;
1254                         BFont bigFont = font;
1255                         BFont tinyFont = font;
1256                         smallFont.SetSize( r.Height() / 5.0 );
1257                         bigFont.SetSize( r.Height() / 3.0 );
1258                         tinyFont.SetSize( r.Height() / 7.0 );
1259                         float timeHeight = r.Height() / 2.5;
1260                         float height = ( r.Height() - timeHeight ) / 3.0;
1261                         SetFont( &tinyFont );
1262                         SetHighColor( 0, 180, 0, 255 );
1263                         DrawString( _("File"), BPoint( r.left + 3.0, r.top + height ) );
1264                         DrawString( _("Title"), BPoint( r.left + 3.0, r.top + 2.0 * height ) );
1265                         DrawString( _("Chapter"), BPoint( r.left + 3.0, r.top + 3.0 * height ) );
1266                         SetFont( &smallFont );
1267                         BString helper;
1268                         SetHighColor( 0, 255, 0, 255 );
1269                         // file
1270                         _MakeString( helper, fCurrentFileIndex, fCurrentFileSize );
1271                         float width = StringWidth( helper.String() );
1272                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + height ) );
1273                         // title
1274                         _MakeString( helper, fCurrentTitleIndex, fCurrentTitleSize );
1275                         width = StringWidth( helper.String() );
1276                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + 2.0 * height ) );
1277                         // chapter
1278                         _MakeString( helper, fCurrentChapterIndex, fCurrentChapterSize );
1279                         width = StringWidth( helper.String() );
1280                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + 3.0 * height ) );
1281                         // time
1282                         SetFont( &bigFont );
1283                         width = StringWidth( fTimeString.String() );
1284                         DrawString( fTimeString.String(),
1285                                                 BPoint( r.left + r.Width() / 2.0 - width / 2.0,
1286                                                                 r.bottom - 3.0 ) );
1287                         break;
1288                 }
1289         }
1290 }
1291
1292 /*****************************************************************************
1293  * PositionInfoView::ResizeToPreferred
1294  *****************************************************************************/
1295 void
1296 PositionInfoView::ResizeToPreferred()
1297 {
1298         float width, height;
1299         GetPreferredSize( &width, &height );
1300         ResizeTo( width, height );
1301 }
1302
1303 /*****************************************************************************
1304  * PositionInfoView::GetPreferredSize
1305  *****************************************************************************/
1306 void
1307 PositionInfoView::GetPreferredSize( float* width, float* height )
1308 {
1309         if ( width && height )
1310         {
1311                 *width = 5.0 + ceilf( StringWidth( "0:00:00" ) ) + 5.0;
1312                 font_height fh;
1313                 GetFontHeight( &fh );
1314                 *height = 3.0 + ceilf( fh.ascent ) + 3.0;
1315                 fStackedWidthCache = *width * 1.2;
1316                 fStackedHeightCache = *height * 2.7;
1317         }
1318 }
1319
1320 /*****************************************************************************
1321  * PositionInfoView::Pulse
1322  *****************************************************************************/
1323 void
1324 PositionInfoView::Pulse()
1325 {
1326         // allow for Pulse frequency to be higher, MediaControlView needs it
1327         bigtime_t now = system_time();
1328         if ( now - fLastPulseUpdate > 900000 )
1329         {
1330                 int32 index, size;
1331                 p_intf->p_sys->p_wrapper->GetPlaylistInfo( index, size );
1332                 SetFile( index + 1, size );
1333                 p_intf->p_sys->p_wrapper->TitleInfo( index, size );
1334                 SetTitle( index, size );
1335                 p_intf->p_sys->p_wrapper->ChapterInfo( index, size );
1336                 SetChapter( index, size );
1337                 SetTime( p_intf->p_sys->p_wrapper->GetTimeAsString() );
1338                 fLastPulseUpdate = now;
1339         }
1340 }
1341
1342 /*****************************************************************************
1343  * PositionInfoView::GetBigPreferredSize
1344  *****************************************************************************/
1345 void
1346 PositionInfoView::GetBigPreferredSize( float* width, float* height )
1347 {
1348         if ( width && height )
1349         {
1350                 *width = fStackedWidthCache;
1351                 *height = fStackedHeightCache;
1352         }
1353 }
1354
1355 /*****************************************************************************
1356  * PositionInfoView::SetMode
1357  *****************************************************************************/
1358 void
1359 PositionInfoView::SetMode( uint32 mode )
1360 {
1361         if ( fMode != mode )
1362         {
1363                 fMode = mode;
1364                 _InvalidateContents();
1365         }
1366 }
1367
1368 /*****************************************************************************
1369  * PositionInfoView::SetFile
1370  *****************************************************************************/
1371 void
1372 PositionInfoView::SetFile( int32 index, int32 size )
1373 {
1374         if ( fCurrentFileIndex != index || fCurrentFileSize != size )
1375         {
1376                 fCurrentFileIndex = index;
1377                 fCurrentFileSize = size;
1378                 _InvalidateContents();
1379         }
1380 }
1381
1382 /*****************************************************************************
1383  * PositionInfoView::SetTitle
1384  *****************************************************************************/
1385 void
1386 PositionInfoView::SetTitle( int32 index, int32 size )
1387 {
1388         if ( fCurrentTitleIndex != index || fCurrentFileSize != size )
1389         {
1390                 fCurrentTitleIndex = index;
1391                 fCurrentTitleSize = size;
1392                 _InvalidateContents();
1393         }
1394 }
1395
1396 /*****************************************************************************
1397  * PositionInfoView::SetChapter
1398  *****************************************************************************/
1399 void
1400 PositionInfoView::SetChapter( int32 index, int32 size )
1401 {
1402         if ( fCurrentChapterIndex != index || fCurrentFileSize != size )
1403         {
1404                 fCurrentChapterIndex = index;
1405                 fCurrentChapterSize = size;
1406                 _InvalidateContents();
1407         }
1408 }
1409
1410 /*****************************************************************************
1411  * PositionInfoView::SetTime
1412  *****************************************************************************/
1413 void
1414 PositionInfoView::SetTime( int32 seconds )
1415 {
1416         if ( fSeconds != seconds )
1417         {
1418                 if ( seconds >= 0 )
1419                 {
1420                         int32 minutes = seconds / 60;
1421                         int32 hours = minutes / 60;
1422                         seconds -= minutes * 60 - hours * 60 * 60;
1423                         minutes -= hours * 60;
1424                         fTimeString.SetTo( "" );
1425                         fTimeString << hours << ":" << minutes << ":" << seconds;
1426                 }
1427                 else
1428                         fTimeString.SetTo( "-:--:--" );
1429
1430                 fSeconds = seconds;
1431                 _InvalidateContents();
1432         }
1433 }
1434
1435 /*****************************************************************************
1436  * PositionInfoView::SetTime
1437  *****************************************************************************/
1438 void
1439 PositionInfoView::SetTime( const char* string )
1440 {
1441         fTimeString.SetTo( string );
1442         _InvalidateContents();
1443 }
1444
1445 /*****************************************************************************
1446  * PositionInfoView::_InvalidateContents
1447  *****************************************************************************/
1448 void
1449 PositionInfoView::_InvalidateContents( uint32 which )
1450 {
1451         BRect r( Bounds() );
1452         r.InsetBy( 2.0, 2.0 );
1453         Invalidate( r );
1454 }
1455
1456 /*****************************************************************************
1457  * PositionInfoView::_InvalidateContents
1458  *****************************************************************************/
1459 void
1460 PositionInfoView::_MakeString( BString& into, int32 index, int32 maxIndex ) const
1461 {
1462         into = "";
1463         if ( index >= 0 && maxIndex >= 0 )
1464                 into << index;
1465         else
1466                 into << "-";
1467         into << "/";
1468         if ( maxIndex >= 0 )
1469                 into << maxIndex;
1470         else
1471                 into << "-";
1472 }