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