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