- Added LCD info view from 0.4.x
[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.5 2002/10/28 19:42:24 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(uint64 seek, uint64 size)
286 {
287         fSeekSlider->SetPosition((float)seek / (float)size);
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         fSkipBack->SetEnabled(enabled);
329         fPlayPause->SetEnabled(enabled);
330         fSkipForward->SetEnabled(enabled);
331         fStop->SetEnabled(enabled);
332         fMute->SetEnabled(enabled);
333         fVolumeSlider->SetEnabled(enabled);
334         fSeekSlider->SetEnabled(enabled);
335         fRewind->SetEnabled(enabled);
336         fForward->SetEnabled(enabled);
337 }
338
339 // SetAudioEnabled
340 void
341 MediaControlView::SetAudioEnabled(bool enabled)
342 {
343         fMute->SetEnabled(enabled);
344         fVolumeSlider->SetEnabled(enabled);
345 }
346
347 // GetSeekTo
348 uint32
349 MediaControlView::GetSeekTo() const
350 {
351         return fSeekSlider->Value();
352 }
353
354 // GetVolume
355 uint32
356 MediaControlView::GetVolume() const
357 {
358         return fVolumeSlider->Value();
359 }
360
361 // SetSkippable
362 void
363 MediaControlView::SetSkippable(bool backward, bool forward)
364 {
365         fSkipBack->SetEnabled(backward);
366         fSkipForward->SetEnabled(forward);
367 }
368
369 // SetMuted
370 void
371 MediaControlView::SetMuted(bool mute)
372 {
373         fVolumeSlider->SetMuted(mute);
374 }
375
376 // _LayoutControls
377 void
378 MediaControlView::_LayoutControls(BRect frame) const
379 {
380         // seek slider
381         BRect r(frame);
382         // calculate absolutly minimal width
383         float minWidth = fSkipBack->Bounds().Width();
384 //      minWidth += fRewind->Bounds().Width();
385         minWidth += fStop->Bounds().Width();
386         minWidth += fPlayPause->Bounds().Width();
387 //      minWidth += fForward->Bounds().Width();
388         minWidth += fSkipForward->Bounds().Width();
389         minWidth += fMute->Bounds().Width();
390         minWidth += VOLUME_MIN_WIDTH;
391         
392         // layout time slider and info view
393     float width, height;
394     fPositionInfo->GetBigPreferredSize( &width, &height );
395     float ratio = width / height;
396     width = r.Height() * ratio;
397     if (frame.Width() - minWidth - MIN_SPACE >= width
398               && frame.Height() >= height)
399     {
400         r.right = r.left + width;
401         fPositionInfo->SetMode(PositionInfoView::MODE_BIG);
402         _LayoutControl(fPositionInfo, r, true, true);
403         frame.left = r.right + MIN_SPACE;
404         r.left = frame.left;
405         r.right = frame.right;
406     //    r.bottom = r.top + r.Height() / 2.0 - MIN_SPACE / 2.0;
407         r.bottom = r.top + fSeekSlider->Bounds().Height();
408         _LayoutControl(fSeekSlider, r, true);
409     }
410     else
411     {
412         fPositionInfo->GetPreferredSize( &width, &height );
413         fPositionInfo->SetMode(PositionInfoView::MODE_SMALL);
414         fPositionInfo->ResizeTo(width, height);
415         r.bottom = r.top + r.Height() / 2.0 - MIN_SPACE / 2.0;
416         r.right = r.left + fPositionInfo->Bounds().Width();
417         _LayoutControl(fPositionInfo, r, true );
418         r.left = r.right + MIN_SPACE;
419         r.right = frame.right;
420         _LayoutControl(fSeekSlider, r, true);
421     }
422         float currentWidth = frame.Width();
423         float space = (currentWidth - minWidth) / 6.0;//8.0;
424         // apply weighting
425         space = MIN_SPACE + (space - MIN_SPACE) / VOLUME_SLIDER_LAYOUT_WEIGHT;
426         // layout controls with "space" inbetween
427         r.left = frame.left;
428         r.top = r.bottom + MIN_SPACE + 1.0;
429         r.bottom = frame.bottom;
430         // skip back
431         r.right = r.left + fSkipBack->Bounds().Width();
432         _LayoutControl(fSkipBack, r);
433         // rewind
434 //      r.left = r.right + space;
435 //      r.right = r.left + fRewind->Bounds().Width();
436 //      _LayoutControl(fRewind, r);
437         // stop
438         r.left = r.right + space;
439         r.right = r.left + fStop->Bounds().Width();
440         _LayoutControl(fStop, r);
441         // play/pause
442         r.left = r.right + space;
443         r.right = r.left + fPlayPause->Bounds().Width();
444         _LayoutControl(fPlayPause, r);
445         // forward
446 //      r.left = r.right + space;
447 //      r.right = r.left + fForward->Bounds().Width();
448 //      _LayoutControl(fForward, r);
449         // skip forward
450         r.left = r.right + space;
451         r.right = r.left + fSkipForward->Bounds().Width();
452         _LayoutControl(fSkipForward, r);
453         // speaker icon
454         r.left = r.right + space + space;
455         r.right = r.left + fMute->Bounds().Width();
456         _LayoutControl(fMute, r);
457         // volume slider
458         r.left = r.right + SPEAKER_SLIDER_DIST; // keep speaker icon and volume slider attached
459         r.right = frame.right;
460         _LayoutControl(fVolumeSlider, r, true);
461 }
462
463 // _MinFrame
464 BRect           
465 MediaControlView::_MinFrame() const
466 {
467         // add up width of controls along bottom (seek slider will likely adopt)
468         float minWidth = 2 * BORDER_INSET;
469         minWidth += fSkipBack->Bounds().Width() + MIN_SPACE;
470 //      minWidth += fRewind->Bounds().Width() + MIN_SPACE;
471         minWidth += fStop->Bounds().Width() + MIN_SPACE;
472         minWidth += fPlayPause->Bounds().Width() + MIN_SPACE;
473 //      minWidth += fForward->Bounds().Width() + MIN_SPACE;
474         minWidth += fSkipForward->Bounds().Width() + MIN_SPACE + MIN_SPACE;
475         minWidth += fMute->Bounds().Width() + SPEAKER_SLIDER_DIST;
476         minWidth += VOLUME_MIN_WIDTH;
477
478         // add up height of seek slider and heighest control on bottom
479         float minHeight = 2 * BORDER_INSET;
480         minHeight += fSeekSlider->Bounds().Height() + MIN_SPACE + MIN_SPACE / 2.0;
481         minHeight += fBottomControlHeight;
482         return BRect(0.0, 0.0, minWidth - 1.0, minHeight - 1.0);
483 }
484
485 // _LayoutControl
486 void
487 MediaControlView::_LayoutControl(BView* view, BRect frame,
488                                  bool resizeWidth, bool resizeHeight) const
489 {
490     if (!resizeHeight)
491             // center vertically
492             frame.top = (frame.top + frame.bottom) / 2.0 - view->Bounds().Height() / 2.0;
493         if (!resizeWidth)
494             //center horizontally
495                 frame.left = (frame.left + frame.right) / 2.0 - view->Bounds().Width() / 2.0;
496         view->MoveTo(frame.LeftTop());
497         float width = resizeWidth ? frame.Width() : view->Bounds().Width();
498         float height = resizeHeight ? frame.Height() : view->Bounds().Height();
499     if (resizeWidth || resizeHeight)
500         view->ResizeTo(width, height);
501 }
502
503
504
505 /*****************************************************************************
506  * SeekSlider
507  *****************************************************************************/
508 SeekSlider::SeekSlider(BRect frame, const char* name, MediaControlView *owner,
509                                            int32 minValue, int32 maxValue)
510         : BControl(frame, name, NULL, NULL, B_FOLLOW_NONE,
511                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
512           fOwner(owner),
513           fTracking(false),
514           fMinValue(minValue),
515           fMaxValue(maxValue)
516 {
517         BFont font(be_plain_font);
518         font.SetSize(9.0);
519         SetFont(&font);
520 }
521
522 SeekSlider::~SeekSlider()
523 {
524         _EndSeek();
525 }
526
527 /*****************************************************************************
528  * VolumeSlider::AttachedToWindow
529  *****************************************************************************/
530 void
531 SeekSlider::AttachedToWindow()
532 {
533         BControl::AttachedToWindow();
534         SetViewColor(B_TRANSPARENT_32_BIT);
535 }
536
537 /*****************************************************************************
538  * VolumeSlider::Draw
539  *****************************************************************************/
540 void
541 SeekSlider::Draw(BRect updateRect)
542 {
543         BRect r(Bounds());
544         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
545         float sliderStart = (r.left + knobWidth2);
546         float sliderEnd = (r.right - knobWidth2);
547         float knobPos = sliderStart
548                                         + floorf((sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
549                                         / (fMaxValue - fMinValue) + 0.5);
550         // draw both sides (the original from Be doesn't seem
551         // to make a difference for enabled/disabled state)
552 //      DrawBitmapAsync(fLeftSideBits, r.LeftTop());
553 //      DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
554         // colors for the slider area between the two bitmaps
555         rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
556         rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
557         rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
558         rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
559         rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
560         rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
561         rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
562         rgb_color green = kSeekGreen;
563         rgb_color greenShadow = kSeekGreenShadow;
564         rgb_color black = kBlack;
565         rgb_color dotGrey = midShadow;
566         rgb_color dotGreen = greenShadow;
567         // draw frame
568         _StrokeFrame(r, softShadow, softShadow, softLight, softLight);
569         r.InsetBy(1.0, 1.0);
570         _StrokeFrame(r, black, black, light, light);
571         if (IsEnabled())
572         {
573                 r.InsetBy(1.0, 1.0);
574                 // inner shadow
575                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
576                 r.top++;
577                 r.left++;
578                 _StrokeFrame(r, greenShadow, greenShadow, green, green);
579                 // inside area
580                 r.InsetBy(1.0, 1.0);
581                 SetHighColor(green);
582                 FillRect(r);
583                 // dots
584                 int32 dotCount = (int32)(r.Width() / 6.0);
585                 BPoint dotPos;
586                 dotPos.y = r.top + 2.0;
587                 SetHighColor(dotGreen);
588                 for (int32 i = 0; i < dotCount; i++)
589                 {
590                         dotPos.x = sliderStart + i * 6.0 + 5.0;
591                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 6.0));
592                 }
593                 // slider handle
594                 r.top -= 4.0;
595                 r.bottom += 3.0;
596                 r.left = knobPos - knobWidth2;
597                 r.right = knobPos + knobWidth2;
598                 // black outline
599                 float handleBottomSize = 2.0;
600                 float handleArrowSize = 6.0;
601                 BeginLineArray(10);
602                         // upper handle
603                         AddLine(BPoint(r.left, r.top + handleBottomSize),
604                                         BPoint(r.left, r.top), black);
605                         AddLine(BPoint(r.left + 1.0, r.top),
606                                         BPoint(r.right, r.top), black);
607                         AddLine(BPoint(r.right, r.top + 1.0),
608                                         BPoint(r.right, r.top + handleBottomSize), black);
609                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
610                                         BPoint(knobPos, r.top + handleArrowSize), black);
611                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
612                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), black);
613                         // lower handle
614                         AddLine(BPoint(r.left, r.bottom),
615                                         BPoint(r.left, r.bottom - handleBottomSize), black);
616                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
617                                         BPoint(knobPos, r.bottom - handleArrowSize), black);
618                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
619                                         BPoint(r.right, r.bottom - handleBottomSize), black);
620                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
621                                         BPoint(r.right, r.bottom), black);
622                         AddLine(BPoint(r.right - 1.0, r.bottom),
623                                         BPoint(r.left + 1.0, r.bottom), black);
624                 EndLineArray();
625                 // inner red light and shadow lines
626                 r.InsetBy(1.0, 1.0);
627                 handleBottomSize--;
628                 handleArrowSize -= 2.0;
629                 BeginLineArray(10);
630                         // upper handle
631                         AddLine(BPoint(r.left, r.top + handleBottomSize),
632                                         BPoint(r.left, r.top), kSeekRedLight);
633                         AddLine(BPoint(r.left + 1.0, r.top),
634                                         BPoint(r.right, r.top), kSeekRedLight);
635                         AddLine(BPoint(r.right, r.top + 1.0),
636                                         BPoint(r.right, r.top + handleBottomSize), kSeekRedShadow);
637                         AddLine(BPoint(r.right - 1.0, r.top + handleBottomSize + 1.0),
638                                         BPoint(knobPos, r.top + handleArrowSize), kSeekRedShadow);
639                         AddLine(BPoint(knobPos - 1.0, r.top + handleArrowSize - 1.0),
640                                         BPoint(r.left + 1.0, r.top + handleBottomSize + 1.0), kSeekRedLight);
641                         // lower handle
642                         AddLine(BPoint(r.left, r.bottom),
643                                         BPoint(r.left, r.bottom - handleBottomSize), kSeekRedLight);
644                         AddLine(BPoint(r.left + 1.0, r.bottom - handleBottomSize - 1.0),
645                                         BPoint(knobPos, r.bottom - handleArrowSize), kSeekRedLight);
646                         AddLine(BPoint(knobPos + 1.0, r.bottom - handleArrowSize + 1.0),
647                                         BPoint(r.right, r.bottom - handleBottomSize), kSeekRedShadow);
648                         AddLine(BPoint(r.right, r.bottom - handleBottomSize + 1.0),
649                                         BPoint(r.right, r.bottom), kSeekRedShadow);
650                         AddLine(BPoint(r.right - 1.0, r.bottom),
651                                         BPoint(r.left + 1.0, r.bottom), kSeekRedShadow);
652                 EndLineArray();
653                 // fill rest of handles with red
654                 SetHighColor(kSeekRed);
655                 r.InsetBy(1.0, 1.0);
656                 handleArrowSize -= 2.0;
657                 BPoint arrow[3];
658                 // upper handle arrow
659                 arrow[0].x = r.left;
660                 arrow[0].y = r.top;
661                 arrow[1].x = r.right;
662                 arrow[1].y = r.top;
663                 arrow[2].x = knobPos;
664                 arrow[2].y = r.top + handleArrowSize;
665                 FillPolygon(arrow, 3);
666                 // lower handle arrow
667                 arrow[0].x = r.left;
668                 arrow[0].y = r.bottom;
669                 arrow[1].x = r.right;
670                 arrow[1].y = r.bottom;
671                 arrow[2].x = knobPos;
672                 arrow[2].y = r.bottom - handleArrowSize;
673                 FillPolygon(arrow, 3);
674         }
675         else
676         {
677                 r.InsetBy(1.0, 1.0);
678                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
679                 r.InsetBy(1.0, 1.0);
680                 _StrokeFrame(r, darkShadow, darkShadow, darkShadow, darkShadow);
681                 r.InsetBy(1.0, 1.0);
682                 SetHighColor(darkShadow);
683                 SetLowColor(shadow);
684                 // stripes
685                 float width = floorf(StringWidth(kDisabledSeekMessage));
686                 float textPos = r.left + r.Width() / 2.0 - width / 2.0;
687                 pattern stripes = { 0xc7, 0x8f, 0x1f, 0x3e, 0x7c, 0xf8, 0xf1, 0xe3 };
688                 BRect stripesRect(r);
689                 stripesRect.right = textPos - 5.0;
690                 FillRect(stripesRect, stripes);
691                 stripesRect.left = textPos + width + 3.0;
692                 stripesRect.right = r.right;
693                 FillRect(stripesRect, stripes);
694                 // info text
695                 r.left = textPos - 4.0;
696                 r.right = textPos + width + 2.0;
697                 FillRect(r);
698                 SetHighColor(shadow);
699                 SetLowColor(darkShadow);
700                 font_height fh;
701                 GetFontHeight(&fh);
702                 DrawString(kDisabledSeekMessage, BPoint(textPos, r.top + ceilf(fh.ascent) - 1.0));
703         }
704 }
705
706 /*****************************************************************************
707  * SeekSlider::MouseDown
708  *****************************************************************************/
709 void
710 SeekSlider::MouseDown(BPoint where)
711 {
712         if (IsEnabled() && Bounds().Contains(where))
713         {
714                 SetValue(_ValueFor(where.x));
715                 fTracking = true;
716                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
717                 _BeginSeek();
718         }
719 }
720
721 /*****************************************************************************
722  * SeekSlider::MouseMoved
723  *****************************************************************************/
724 void
725 SeekSlider::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
726 {
727         if (fTracking)
728         {
729                 SetValue(_ValueFor(where.x));
730                 _Seek();
731         }
732 }
733
734 /*****************************************************************************
735  * SeekSlider::MouseUp
736  *****************************************************************************/
737 void
738 SeekSlider::MouseUp(BPoint where)
739 {
740         if (fTracking)
741         {
742                 fTracking = false;
743                 _EndSeek();
744         }
745 }
746
747 /*****************************************************************************
748  * SeekSlider::ResizeToPreferred
749  *****************************************************************************/
750 void
751 SeekSlider::ResizeToPreferred()
752 {
753         float width = 15.0 + StringWidth(kDisabledSeekMessage) + 15.0;
754         ResizeTo(width, 17.0);
755 }
756
757 /*****************************************************************************
758  * SeekSlider::SetPosition
759  *****************************************************************************/
760 void
761 SeekSlider::SetPosition(float position)
762 {
763         SetValue(fMinValue + (int32)floorf((fMaxValue - fMinValue) * position + 0.5));
764 }
765
766 /*****************************************************************************
767  * SeekSlider::_ValueFor
768  *****************************************************************************/
769 int32
770 SeekSlider::_ValueFor(float xPos) const
771 {
772         BRect r(Bounds());
773         float knobWidth2 = SEEK_SLIDER_KNOB_WIDTH / 2.0;
774         float sliderStart = (r.left + knobWidth2);
775         float sliderEnd = (r.right - knobWidth2);
776         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
777                                   / (sliderEnd - sliderStart - 1.0));
778         if (value < fMinValue)
779                 value = fMinValue;
780         if (value > fMaxValue)
781                 value = fMaxValue;
782         return value;
783 }
784
785 /*****************************************************************************
786  * SeekSlider::_StrokeFrame
787  *****************************************************************************/
788 void
789 SeekSlider::_StrokeFrame(BRect r, rgb_color left, rgb_color top,
790                                                  rgb_color right, rgb_color bottom)
791 {
792         BeginLineArray(4);
793                 AddLine(BPoint(r.left, r.bottom), BPoint(r.left, r.top), left);
794                 AddLine(BPoint(r.left + 1.0, r.top), BPoint(r.right, r.top), top);
795                 AddLine(BPoint(r.right, r.top + 1.0), BPoint(r.right, r.bottom), right);
796                 AddLine(BPoint(r.right - 1.0, r.bottom), BPoint(r.left + 1.0, r.bottom), bottom);
797         EndLineArray();
798 }
799
800 /*****************************************************************************
801  * SeekSlider::_BeginSeek
802  *****************************************************************************/
803 void
804 SeekSlider::_BeginSeek()
805 {
806         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
807         if (fOwner->fScrubSem >= B_OK)
808                 release_sem(fOwner->fScrubSem);
809 }
810
811 /*****************************************************************************
812  * SeekSlider::_Seek
813  *****************************************************************************/
814 void
815 SeekSlider::_Seek()
816 {
817         if (fOwner->fScrubSem >= B_OK)
818                 delete_sem(fOwner->fScrubSem);
819         fOwner->fScrubSem = create_sem(0, "Vlc::fScrubSem");
820         if (fOwner->fScrubSem >= B_OK)
821                 release_sem(fOwner->fScrubSem);
822 }
823
824 /*****************************************************************************
825  * SeekSlider::_EndSeek
826  *****************************************************************************/
827 void
828 SeekSlider::_EndSeek()
829 {
830         if (fOwner->fScrubSem >= B_OK)
831                 delete_sem(fOwner->fScrubSem);
832         fOwner->fScrubSem = B_ERROR;
833 }
834
835
836 /*****************************************************************************
837  * VolumeSlider
838  *****************************************************************************/
839 VolumeSlider::VolumeSlider(BRect frame, const char* name, int32 minValue, int32 maxValue,
840                                                    BMessage* message, BHandler* target)
841         : BControl(frame, name, NULL, message, B_FOLLOW_NONE,
842                            B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE),
843           fLeftSideBits(NULL),
844           fRightSideBits(NULL),
845           fKnobBits(NULL),
846           fTracking(false),
847           fMuted(false),
848           fMinValue(minValue),
849           fMaxValue(maxValue)
850 {
851         SetTarget(target);
852
853         // create bitmaps
854         BRect r(BPoint(0.0, 0.0), kVolumeSliderBitmapSize);
855         fLeftSideBits = new BBitmap(r, B_CMAP8);
856         fRightSideBits = new BBitmap(r, B_CMAP8);
857         r.Set(0.0, 0.0, kVolumeSliderKnobBitmapSize.x, kVolumeSliderKnobBitmapSize.y);
858         fKnobBits = new BBitmap(r, B_CMAP8);
859
860         _MakeBitmaps();
861 }
862
863 /*****************************************************************************
864  * VolumeSlider destructor
865  *****************************************************************************/
866 VolumeSlider::~VolumeSlider()
867 {
868         delete fLeftSideBits;
869         delete fRightSideBits;
870         delete fKnobBits;
871 }
872
873 /*****************************************************************************
874  * VolumeSlider::AttachedToWindow
875  *****************************************************************************/
876 void
877 VolumeSlider::AttachedToWindow()
878 {
879         BControl::AttachedToWindow();
880         SetViewColor(B_TRANSPARENT_32_BIT);
881 }
882
883 /*****************************************************************************
884  * VolumeSlider::SetValue
885  *****************************************************************************/
886 void
887 VolumeSlider::SetValue(int32 value)
888 {
889         if (value != Value())
890         {
891                 BControl::SetValue(value);
892                 Invoke();
893         }
894 }
895
896 /*****************************************************************************
897  * VolumeSlider::SetEnabled
898  *****************************************************************************/
899 void
900 VolumeSlider::SetEnabled(bool enable)
901 {
902         if (enable != IsEnabled())
903         {
904                 BControl::SetEnabled(enable);
905                 _MakeBitmaps();
906                 Invalidate();
907         }
908 }
909
910 /*****************************************************************************
911  * VolumeSlider::Draw
912  *****************************************************************************/
913 void
914 VolumeSlider::Draw(BRect updateRect)
915 {
916         if (IsValid())
917         {
918                 BRect r(Bounds());
919                 float sliderSideWidth = kVolumeSliderBitmapWidth;
920                 float sliderStart = (r.left + sliderSideWidth);
921                 float sliderEnd = (r.right - sliderSideWidth);
922                 float knobPos = sliderStart
923                                                 + (sliderEnd - sliderStart - 1.0) * (Value() - fMinValue)
924                                                 / (fMaxValue - fMinValue);
925                 // draw both sides (the original from Be doesn't seem
926                 // to make a difference for enabled/disabled state)
927                 DrawBitmapAsync(fLeftSideBits, r.LeftTop());
928                 DrawBitmapAsync(fRightSideBits, BPoint(sliderEnd + 1.0, r.top));
929                 // colors for the slider area between the two bitmaps
930                 rgb_color background = kBackground;//ui_color(B_PANEL_BACKGROUND_COLOR);
931                 rgb_color shadow = tint_color(background, B_DARKEN_2_TINT);
932                 rgb_color softShadow = tint_color(background, B_DARKEN_1_TINT);
933                 rgb_color darkShadow = tint_color(background, B_DARKEN_4_TINT);
934                 rgb_color midShadow = tint_color(background, B_DARKEN_3_TINT);
935                 rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT);
936                 rgb_color softLight = tint_color(background, B_LIGHTEN_1_TINT);
937                 rgb_color green = kGreen;
938                 rgb_color greenShadow = kGreenShadow;
939                 rgb_color black = kBlack;
940                 rgb_color dotGrey = midShadow;
941                 rgb_color dotGreen = greenShadow;
942                 // make dimmed version of colors if we're disabled
943                 if (!IsEnabled())
944                 {
945                         shadow = (rgb_color){ 200, 200, 200, 255 };
946                         softShadow = dimmed_color_cmap8(softShadow, background, DIM_LEVEL);
947                         darkShadow = dimmed_color_cmap8(darkShadow, background, DIM_LEVEL);
948                         midShadow = shadow;
949                         light = dimmed_color_cmap8(light, background, DIM_LEVEL);
950                         softLight = dimmed_color_cmap8(softLight, background, DIM_LEVEL);
951                         green = dimmed_color_cmap8(green, background, DIM_LEVEL);
952                         greenShadow = dimmed_color_cmap8(greenShadow, background, DIM_LEVEL);
953                         black = dimmed_color_cmap8(black, background, DIM_LEVEL);
954                         dotGreen = dotGrey;
955                 }
956                 else if (fMuted)
957                 {
958                         green = tint_color(kBackground, B_DARKEN_3_TINT);
959                         greenShadow = tint_color(kBackground, B_DARKEN_4_TINT);
960                         dotGreen = greenShadow;
961                 }
962                 // draw slider edges between bitmaps
963                 BeginLineArray(7);
964                         AddLine(BPoint(sliderStart, r.top),
965                                         BPoint(sliderEnd, r.top), softShadow);
966                         AddLine(BPoint(sliderStart, r.bottom),
967                                         BPoint(sliderEnd, r.bottom), softLight);
968                         r.InsetBy(0.0, 1.0);
969                         AddLine(BPoint(sliderStart, r.top),
970                                         BPoint(sliderEnd, r.top), black);
971                         AddLine(BPoint(sliderStart, r.bottom),
972                                         BPoint(sliderEnd, r.bottom), light);
973                         r.top++;
974                         AddLine(BPoint(sliderStart, r.top),
975                                         BPoint(knobPos, r.top), greenShadow);
976                         AddLine(BPoint(knobPos, r.top),
977                                         BPoint(sliderEnd, r.top), midShadow);
978                         r.top++;
979                         AddLine(BPoint(sliderStart, r.top),
980                                         BPoint(knobPos, r.top), greenShadow);
981                 EndLineArray();
982                 // fill rest inside of slider
983                 r.InsetBy(0.0, 1.0);
984                 r.left = sliderStart;
985                 r.right = knobPos;
986                 SetHighColor(green);
987                 FillRect(r, B_SOLID_HIGH);
988                 r.left = knobPos + 1.0;
989                 r.right = sliderEnd;
990                 r.top -= 1.0;
991                 SetHighColor(shadow);
992                 FillRect(r, B_SOLID_HIGH);
993                 // draw little dots inside
994                 int32 dotCount = (int32)((sliderEnd - sliderStart) / 5.0);
995                 BPoint dotPos;
996                 dotPos.y = r.top + 4.0;
997                 for (int32 i = 0; i < dotCount; i++)
998                 {
999                         dotPos.x = sliderStart + i * 5.0 + 4.0;
1000                         SetHighColor(dotPos.x < knobPos ? dotGreen : dotGrey);
1001                         StrokeLine(dotPos, BPoint(dotPos.x, dotPos.y + 1.0));
1002                 }
1003                 // draw knob
1004                 r.top -= 1.0;
1005                 SetDrawingMode(B_OP_OVER); // part of knob is transparent
1006                 DrawBitmapAsync(fKnobBits, BPoint(knobPos - kVolumeSliderKnobWidth / 2, r.top));
1007         }
1008         else
1009                 fprintf(stderr, "VolumeSlider::Draw() - Error: no valid bitmaps!");
1010 }
1011
1012 /*****************************************************************************
1013  * VolumeSlider::MouseDown
1014  *****************************************************************************/
1015 void
1016 VolumeSlider::MouseDown(BPoint where)
1017 {
1018         if (Bounds().Contains(where) && IsEnabled())
1019         {
1020                 fTracking = true;
1021                 SetValue(_ValueFor(where.x));
1022                 SetMouseEventMask(B_POINTER_EVENTS, B_LOCK_WINDOW_FOCUS);
1023         }
1024 }
1025
1026 /*****************************************************************************
1027  * VolumeSlider::MouseMoved
1028  *****************************************************************************/
1029 void
1030 VolumeSlider::MouseMoved(BPoint where, uint32 transit, const BMessage* dragMessage)
1031 {
1032         if (fTracking)
1033                 SetValue(_ValueFor(where.x));
1034 }
1035
1036 /*****************************************************************************
1037  * VolumeSlider::MouseUp
1038  *****************************************************************************/
1039 void
1040 VolumeSlider::MouseUp(BPoint where)
1041 {
1042         fTracking = false;
1043 }
1044
1045
1046 /*****************************************************************************
1047  * VolumeSlider::IsValid
1048  *****************************************************************************/
1049 bool
1050 VolumeSlider::IsValid() const
1051 {
1052         return (fLeftSideBits && fLeftSideBits->IsValid()
1053                         && fRightSideBits && fRightSideBits->IsValid()
1054                         && fKnobBits && fKnobBits->IsValid());
1055 }
1056
1057 /*****************************************************************************
1058  * VolumeSlider::SetMuted
1059  *****************************************************************************/
1060 void
1061 VolumeSlider::SetMuted(bool mute)
1062 {
1063         if (mute != fMuted)
1064         {
1065                 fMuted = mute;
1066                 _MakeBitmaps();
1067                 Invalidate();
1068         }
1069 }
1070
1071 /*****************************************************************************
1072  * VolumeSlider::_MakeBitmaps
1073  *****************************************************************************/
1074 void
1075 VolumeSlider::_MakeBitmaps()
1076 {
1077         if (IsValid())
1078         {
1079                 // left side of slider
1080                 memcpy(fLeftSideBits->Bits(), kVolumeSliderLeftBitmapBits,
1081                            fLeftSideBits->BitsLength());
1082                 // right side of slider
1083                 memcpy(fRightSideBits->Bits(), kVolumeSliderRightBits,
1084                            fRightSideBits->BitsLength());
1085                 // slider knob
1086                 int32 length = fKnobBits->BitsLength();
1087                 memcpy(fKnobBits->Bits(), kVolumeSliderKnobBits, length);
1088                 uint8* bits = (uint8*)fKnobBits->Bits();
1089                 // black was used in the knob to represent transparency
1090                 // use screen to get index for the "transarent" color used in the bitmap
1091                 BScreen screen(B_MAIN_SCREEN_ID);
1092                 uint8 blackIndex = screen.IndexForColor(kBlack);
1093                 // replace black index with transparent index
1094                 for (int32 i = 0; i < length; i++)
1095                         if (bits[i] == blackIndex)
1096                                 bits[i] = B_TRANSPARENT_MAGIC_CMAP8;
1097
1098                 if (!IsEnabled())
1099                 {
1100                         // make ghosted versions of the bitmaps
1101                         dim_bitmap(fLeftSideBits, kBackground, DIM_LEVEL);
1102                         dim_bitmap(fRightSideBits, kBackground, DIM_LEVEL);
1103                         dim_bitmap(fKnobBits, kBackground, DIM_LEVEL);
1104                 }
1105                 else if (fMuted)
1106                 {
1107                         // replace green color (and shadow) in left slider side
1108                         bits = (uint8*)fLeftSideBits->Bits();
1109                         length = fLeftSideBits->BitsLength();
1110                         uint8 greenIndex = screen.IndexForColor(kGreen);
1111                         uint8 greenShadowIndex = screen.IndexForColor(kGreenShadow);
1112                         rgb_color shadow = tint_color(kBackground, B_DARKEN_3_TINT);
1113                         rgb_color midShadow = tint_color(kBackground, B_DARKEN_4_TINT);
1114                         uint8 replaceIndex = screen.IndexForColor(shadow);
1115                         uint8 replaceShadowIndex = screen.IndexForColor(midShadow);
1116                         for (int32 i = 0; i < length; i++)
1117                         {
1118                                 if (bits[i] == greenIndex)
1119                                         bits[i] = replaceIndex;
1120                                 else if (bits[i] == greenShadowIndex)
1121                                         bits[i] = replaceShadowIndex;
1122                         }
1123                 }
1124         }
1125 }
1126
1127 /*****************************************************************************
1128  * VolumeSlider::_ValueFor
1129  *****************************************************************************/
1130 int32
1131 VolumeSlider::_ValueFor(float xPos) const
1132 {
1133         BRect r(Bounds());
1134         float sliderStart = (r.left + kVolumeSliderBitmapWidth);
1135         float sliderEnd = (r.right - kVolumeSliderBitmapWidth);
1136         int32 value =  fMinValue + (int32)(((xPos - sliderStart) * (fMaxValue - fMinValue))
1137                                   / (sliderEnd - sliderStart - 1.0));
1138         if (value < fMinValue)
1139                 value = fMinValue;
1140         if (value > fMaxValue)
1141                 value = fMaxValue;
1142         return value;
1143 }
1144
1145 /*****************************************************************************
1146  * PositionInfoView::PositionInfoView
1147  *****************************************************************************/
1148 PositionInfoView::PositionInfoView( BRect frame, const char* name,
1149                                     intf_thread_t * p_interface )
1150         : BView( frame, name, B_FOLLOW_NONE,
1151                          B_WILL_DRAW | B_PULSE_NEEDED | B_FULL_UPDATE_ON_RESIZE ),
1152           fMode( MODE_SMALL ),
1153           fCurrentFileIndex( -1 ),
1154           fCurrentFileSize( -1 ),
1155           fCurrentTitleIndex( -1 ),
1156           fCurrentTitleSize( -1 ),
1157           fCurrentChapterIndex( -1 ),
1158           fCurrentChapterSize( -1 ),
1159           fSeconds( -1 ),
1160           fTimeString( "-:--:--" ),
1161           fLastPulseUpdate( system_time() ),
1162           fStackedWidthCache( 0.0 ),
1163           fStackedHeightCache( 0.0 )
1164 {
1165     p_intf = p_interface;
1166
1167         SetViewColor( B_TRANSPARENT_32_BIT );
1168         SetLowColor( kBlack );
1169         SetHighColor( 0, 255, 0, 255 );
1170         SetFontSize( 11.0 );
1171 }
1172
1173 /*****************************************************************************
1174  * PositionInfoView::~PositionInfoView
1175  *****************************************************************************/
1176 PositionInfoView::~PositionInfoView()
1177 {
1178 }
1179
1180 /*****************************************************************************
1181  * PositionInfoView::Draw
1182  *****************************************************************************/
1183 void
1184 PositionInfoView::Draw( BRect updateRect )
1185 {
1186         rgb_color background = ui_color( B_PANEL_BACKGROUND_COLOR );
1187         rgb_color shadow = tint_color( background, B_DARKEN_1_TINT );
1188         rgb_color darkShadow = tint_color( background, B_DARKEN_4_TINT );
1189         rgb_color light = tint_color( background, B_LIGHTEN_MAX_TINT );
1190         rgb_color softLight = tint_color( background, B_LIGHTEN_1_TINT );
1191         // frame
1192         BRect r( Bounds() );
1193         BeginLineArray( 8 );
1194                 AddLine( BPoint( r.left, r.bottom ),
1195                                  BPoint( r.left, r.top ), shadow );
1196                 AddLine( BPoint( r.left + 1.0, r.top ),
1197                                  BPoint( r.right, r.top ), shadow );
1198                 AddLine( BPoint( r.right, r.top + 1.0 ),
1199                                  BPoint( r.right, r.bottom ), softLight );
1200                 AddLine( BPoint( r.right - 1.0, r.bottom ),
1201                                  BPoint( r.left + 1.0, r.bottom ), softLight );
1202                 r.InsetBy( 1.0, 1.0 );
1203                 AddLine( BPoint( r.left, r.bottom ),
1204                                  BPoint( r.left, r.top ), darkShadow );
1205                 AddLine( BPoint( r.left + 1.0, r.top ),
1206                                  BPoint( r.right, r.top ), darkShadow );
1207                 AddLine( BPoint( r.right, r.top + 1.0 ),
1208                                  BPoint( r.right, r.bottom ), light );
1209                 AddLine( BPoint( r.right - 1.0, r.bottom ),
1210                                  BPoint( r.left + 1.0, r.bottom ), light );
1211         EndLineArray();
1212         // background
1213         r.InsetBy( 1.0, 1.0 );
1214         FillRect( r, B_SOLID_LOW );
1215         // contents
1216         font_height fh;
1217         GetFontHeight( &fh );
1218         switch ( fMode )
1219         {
1220                 case MODE_SMALL:
1221                 {
1222                         float width = StringWidth( fTimeString.String() );
1223                         DrawString( fTimeString.String(),
1224                                                 BPoint( r.left + r.Width() / 2.0 - width / 2.0,
1225                                                                 r.top + r.Height() / 2.0 + fh.ascent / 2.0 - 1.0 ) );
1226                         break;
1227                 }
1228                 case MODE_BIG:
1229                 {
1230                         BFont font;
1231                         GetFont( &font );
1232                         BFont smallFont = font;
1233                         BFont bigFont = font;
1234                         BFont tinyFont = font;
1235                         smallFont.SetSize( r.Height() / 5.0 );
1236                         bigFont.SetSize( r.Height() / 3.0 );
1237                         tinyFont.SetSize( r.Height() / 7.0 );
1238                         float timeHeight = r.Height() / 2.5;
1239                         float height = ( r.Height() - timeHeight ) / 3.0;
1240                         SetFont( &tinyFont );
1241                         SetHighColor( 0, 180, 0, 255 );
1242                         DrawString( "File", BPoint( r.left + 3.0, r.top + height ) );
1243                         DrawString( "Title", BPoint( r.left + 3.0, r.top + 2.0 * height ) );
1244                         DrawString( "Chapter", BPoint( r.left + 3.0, r.top + 3.0 * height ) );
1245                         SetFont( &smallFont );
1246                         BString helper;
1247                         SetHighColor( 0, 255, 0, 255 );
1248                         // file
1249                         _MakeString( helper, fCurrentFileIndex, fCurrentFileSize );
1250                         float width = StringWidth( helper.String() );
1251                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + height ) );
1252                         // title
1253                         _MakeString( helper, fCurrentTitleIndex, fCurrentTitleSize );
1254                         width = StringWidth( helper.String() );
1255                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + 2.0 * height ) );
1256                         // chapter
1257                         _MakeString( helper, fCurrentChapterIndex, fCurrentChapterSize );
1258                         width = StringWidth( helper.String() );
1259                         DrawString( helper.String(), BPoint( r.right - 3.0 - width, r.top + 3.0 * height ) );
1260                         // time
1261                         SetFont( &bigFont );
1262                         width = StringWidth( fTimeString.String() );
1263                         DrawString( fTimeString.String(),
1264                                                 BPoint( r.left + r.Width() / 2.0 - width / 2.0,
1265                                                                 r.bottom - 3.0 ) );
1266                         break;
1267                 }
1268         }
1269 }
1270
1271 /*****************************************************************************
1272  * PositionInfoView::ResizeToPreferred
1273  *****************************************************************************/
1274 void
1275 PositionInfoView::ResizeToPreferred()
1276 {
1277         float width, height;
1278         GetPreferredSize( &width, &height );
1279         ResizeTo( width, height );
1280 }
1281
1282 /*****************************************************************************
1283  * PositionInfoView::GetPreferredSize
1284  *****************************************************************************/
1285 void
1286 PositionInfoView::GetPreferredSize( float* width, float* height )
1287 {
1288         if ( width && height )
1289         {
1290                 *width = 5.0 + ceilf( StringWidth( "0:00:00" ) ) + 5.0;
1291                 font_height fh;
1292                 GetFontHeight( &fh );
1293                 *height = 3.0 + ceilf( fh.ascent ) + 3.0;
1294                 fStackedWidthCache = *width * 1.2;
1295                 fStackedHeightCache = *height * 2.7;
1296         }
1297 }
1298
1299 /*****************************************************************************
1300  * PositionInfoView::Pulse
1301  *****************************************************************************/
1302 void
1303 PositionInfoView::Pulse()
1304 {
1305         // allow for Pulse frequency to be higher, MediaControlView needs it
1306         bigtime_t now = system_time();
1307         if ( now - fLastPulseUpdate > 900000 )
1308         {
1309                 int32 index, size;
1310                 p_intf->p_sys->p_vlc_wrapper->getPlaylistInfo( index, size );
1311                 SetFile( index, size );
1312                 p_intf->p_sys->p_vlc_wrapper->getTitleInfo( index, size );
1313                 SetTitle( index, size );
1314                 p_intf->p_sys->p_vlc_wrapper->getChapterInfo( index, size );
1315                 SetChapter( index, size );
1316                 SetTime( p_intf->p_sys->p_vlc_wrapper->getTimeAsString() );
1317                 fLastPulseUpdate = now;
1318         }
1319 }
1320
1321 /*****************************************************************************
1322  * PositionInfoView::GetBigPreferredSize
1323  *****************************************************************************/
1324 void
1325 PositionInfoView::GetBigPreferredSize( float* width, float* height )
1326 {
1327         if ( width && height )
1328         {
1329                 *width = fStackedWidthCache;
1330                 *height = fStackedHeightCache;
1331         }
1332 }
1333
1334 /*****************************************************************************
1335  * PositionInfoView::SetMode
1336  *****************************************************************************/
1337 void
1338 PositionInfoView::SetMode( uint32 mode )
1339 {
1340         if ( fMode != mode )
1341         {
1342                 fMode = mode;
1343                 _InvalidateContents();
1344         }
1345 }
1346
1347 /*****************************************************************************
1348  * PositionInfoView::SetFile
1349  *****************************************************************************/
1350 void
1351 PositionInfoView::SetFile( int32 index, int32 size )
1352 {
1353         if ( fCurrentFileIndex != index || fCurrentFileSize != size )
1354         {
1355                 fCurrentFileIndex = index;
1356                 fCurrentFileSize = size;
1357                 _InvalidateContents();
1358         }
1359 }
1360
1361 /*****************************************************************************
1362  * PositionInfoView::SetTitle
1363  *****************************************************************************/
1364 void
1365 PositionInfoView::SetTitle( int32 index, int32 size )
1366 {
1367         if ( fCurrentTitleIndex != index || fCurrentFileSize != size )
1368         {
1369                 fCurrentTitleIndex = index;
1370                 fCurrentTitleSize = size;
1371                 _InvalidateContents();
1372         }
1373 }
1374
1375 /*****************************************************************************
1376  * PositionInfoView::SetChapter
1377  *****************************************************************************/
1378 void
1379 PositionInfoView::SetChapter( int32 index, int32 size )
1380 {
1381         if ( fCurrentChapterIndex != index || fCurrentFileSize != size )
1382         {
1383                 fCurrentChapterIndex = index;
1384                 fCurrentChapterSize = size;
1385                 _InvalidateContents();
1386         }
1387 }
1388
1389 /*****************************************************************************
1390  * PositionInfoView::SetTime
1391  *****************************************************************************/
1392 void
1393 PositionInfoView::SetTime( int32 seconds )
1394 {
1395         if ( fSeconds != seconds )
1396         {
1397                 if ( seconds >= 0 )
1398                 {
1399                         int32 minutes = seconds / 60;
1400                         int32 hours = minutes / 60;
1401                         seconds -= minutes * 60 - hours * 60 * 60;
1402                         minutes -= hours * 60;
1403                         fTimeString.SetTo( "" );
1404                         fTimeString << hours << ":" << minutes << ":" << seconds;
1405                 }
1406                 else
1407                         fTimeString.SetTo( "-:--:--" );
1408
1409                 fSeconds = seconds;
1410                 _InvalidateContents();
1411         }
1412 }
1413
1414 /*****************************************************************************
1415  * PositionInfoView::SetTime
1416  *****************************************************************************/
1417 void
1418 PositionInfoView::SetTime( const char* string )
1419 {
1420         fTimeString.SetTo( string );
1421         _InvalidateContents();
1422 }
1423
1424 /*****************************************************************************
1425  * PositionInfoView::_InvalidateContents
1426  *****************************************************************************/
1427 void
1428 PositionInfoView::_InvalidateContents( uint32 which )
1429 {
1430         BRect r( Bounds() );
1431         r.InsetBy( 2.0, 2.0 );
1432         Invalidate( r );
1433 }
1434
1435 /*****************************************************************************
1436  * PositionInfoView::_InvalidateContents
1437  *****************************************************************************/
1438 void
1439 PositionInfoView::_MakeString( BString& into, int32 index, int32 maxIndex ) const
1440 {
1441         into = "";
1442         if ( index >= 0)
1443                 into << index;
1444         else
1445                 into << "-";
1446         into << "/";
1447         if ( maxIndex >= 0)
1448                 into << maxIndex;
1449         else
1450                 into << "-";
1451 }