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