Some clean-up. Playlist works again.
[vlc.git] / modules / gui / beos / InterfaceWindow.cpp
1 /*****************************************************************************
2  * InterfaceWindow.cpp: beos interface
3  *****************************************************************************
4  * Copyright (C) 1999, 2000, 2001 VideoLAN
5  * $Id: InterfaceWindow.cpp,v 1.3 2002/10/10 23:11:52 titer Exp $
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Tony Castley <tony@castley.net>
10  *          Richard Shepherd <richard@rshepherd.demon.co.uk>
11  *          Stephan Aßmus <stippi@yellowbites.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /* System headers */
29 #include <kernel/OS.h>
30 #include <InterfaceKit.h>
31 #include <AppKit.h>
32 #include <StorageKit.h>
33 #include <SupportKit.h>
34 #include <malloc.h>
35 #include <scsi.h>
36 #include <scsiprobe_driver.h>
37 #include <fs_info.h>
38 #include <string.h>
39
40 /* VLC headers */
41 #include <vlc/vlc.h>
42 #include <vlc/aout.h>
43 #include <vlc/intf.h>
44
45 /* BeOS interface headers */
46 #include "MsgVals.h"
47 #include "MediaControlView.h"
48 #include "PlayListWindow.h"
49 #include "VlcWrapper.h"
50 #include "InterfaceWindow.h"
51
52 #define INTERFACE_UPDATE_TIMEOUT 80000 // 2 frames if at 25 fps
53
54
55 /*****************************************************************************
56  * InterfaceWindow
57  *****************************************************************************/
58
59 InterfaceWindow::InterfaceWindow( BRect frame, const char *name,
60                                                                   intf_thread_t  *p_interface )
61         : BWindow( frame, name, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
62                            B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS ),
63           p_intf( p_interface ),
64           fInputThread( NULL ),
65           fFilePanel( NULL ),
66           fLastUpdateTime( system_time() ),
67           fSettings( new BMessage( 'sett' ) )
68 {
69     p_intf = p_interface;
70     playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
71                                                        FIND_ANYWHERE );
72     fPlaylistIsEmpty = (p_playlist->i_size < 0);
73     
74     fPlaylistWindow = new PlayListWindow( BRect( 20.0, 20.0, 170.0, 320.0 ),
75                                                                                   "Playlist",
76                                                                                   p_playlist,
77                                                                                   this,
78                                                                                   p_intf );
79     
80         // set the title bar
81         SetName( "interface" );
82         SetTitle( VOUT_TITLE );
83
84         // the media control view
85         p_mediaControl = new MediaControlView( BRect( 0.0, 0.0, 250.0, 50.0 ) );
86         p_mediaControl->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
87         p_mediaControl->SetEnabled( !fPlaylistIsEmpty );
88
89         float width, height;
90         p_mediaControl->GetPreferredSize( &width, &height );
91
92         // set up the main menu
93         fMenuBar = new BMenuBar( BRect(0.0, 0.0, width, 15.0), "main menu",
94                                                          B_FOLLOW_NONE, B_ITEMS_IN_ROW, false );
95
96         // make menu bar resize to correct height
97         float menuWidth, menuHeight;
98         fMenuBar->GetPreferredSize( &menuWidth, &menuHeight );
99         fMenuBar->ResizeTo( width, menuHeight );        // don't change! it's a workarround!
100         // take care of proper size for ourself
101         height += fMenuBar->Bounds().Height();
102         ResizeTo( width, height );
103
104         p_mediaControl->MoveTo( fMenuBar->Bounds().LeftBottom() + BPoint(0.0, 1.0) );
105         AddChild( fMenuBar );
106         AddChild( p_mediaControl );
107
108         // Add the file Menu
109         BMenu* fileMenu = new BMenu( "File" );
110         fMenuBar->AddItem( fileMenu );
111         fileMenu->AddItem( new BMenuItem( "Open File" B_UTF8_ELLIPSIS,
112                                                                           new BMessage( OPEN_FILE ), 'O') );
113         
114         fileMenu->AddItem( new CDMenu( "Open Disc" ) );
115         
116         fileMenu->AddSeparatorItem();
117         fileMenu->AddItem( new BMenuItem( "Play List" B_UTF8_ELLIPSIS,
118                                                                           new BMessage( OPEN_PLAYLIST ), 'P') );
119         
120         fileMenu->AddSeparatorItem();
121         BMenuItem* item = new BMenuItem( "About" B_UTF8_ELLIPSIS,
122                                                                          new BMessage( B_ABOUT_REQUESTED ), 'A');
123         item->SetTarget( be_app );
124         fileMenu->AddItem( item );
125         fileMenu->AddItem( new BMenuItem( "Quit", new BMessage( B_QUIT_REQUESTED ), 'Q') );
126
127         fLanguageMenu = new LanguageMenu("Language", AUDIO_ES, p_intf);
128         fSubtitlesMenu = new LanguageMenu("Subtitles", SPU_ES, p_intf);
129
130         /* Add the Audio menu */
131         fAudioMenu = new BMenu( "Audio" );
132         fMenuBar->AddItem ( fAudioMenu );
133         fAudioMenu->AddItem( fLanguageMenu );
134         fAudioMenu->AddItem( fSubtitlesMenu );
135
136         fPrevTitleMI = new BMenuItem( "Prev Title", new BMessage( PREV_TITLE ) );
137         fNextTitleMI = new BMenuItem( "Next Title", new BMessage( NEXT_TITLE ) );
138         fPrevChapterMI = new BMenuItem( "Prev Chapter", new BMessage( PREV_CHAPTER ) );
139         fNextChapterMI = new BMenuItem( "Next Chapter", new BMessage( NEXT_CHAPTER ) );
140
141         /* Add the Navigation menu */
142         fNavigationMenu = new BMenu( "Navigation" );
143         fMenuBar->AddItem( fNavigationMenu );
144         fNavigationMenu->AddItem( fPrevTitleMI );
145         fNavigationMenu->AddItem( fNextTitleMI );
146         fNavigationMenu->AddItem( fTitleMenu = new TitleMenu( "Go to Title", p_intf ) );
147         fNavigationMenu->AddSeparatorItem();
148         fNavigationMenu->AddItem( fPrevChapterMI );
149         fNavigationMenu->AddItem( fNextChapterMI );
150         fNavigationMenu->AddItem( fChapterMenu = new ChapterMenu( "Go to Chapter", p_intf ) );
151
152         /* Add the Speed menu */
153         fSpeedMenu = new BMenu( "Speed" );
154         fSpeedMenu->SetRadioMode( true );
155         fSpeedMenu->AddItem( fSlowerMI = new BMenuItem( "Slower", new BMessage( SLOWER_PLAY ) ) );
156         fNormalMI = new BMenuItem( "Normal", new BMessage( NORMAL_PLAY ) );
157         fNormalMI->SetMarked(true); // default to normal speed
158         fSpeedMenu->AddItem( fNormalMI );
159         fSpeedMenu->AddItem( fFasterMI = new BMenuItem( "Faster", new BMessage( FASTER_PLAY) ) );
160         fSpeedMenu->SetTargetForItems( this );
161         fMenuBar->AddItem( fSpeedMenu );
162
163                                                                                 
164         /* Add the Config menu */
165 //      BMenu* configMenu = new BMenu( "Config" );
166 //      menu_bar->AddItem( configMenu );
167 //      fOnTopMI = new BMenuItem( "Always on Top",
168 //                                                        new BMessage( TOGGLE_ON_TOP ) )
169 //      configMenu->AddItem(  );
170 //      fOnTopMI->SetMarked(false);                                                                     
171
172         // prepare fow showing
173         _SetMenusEnabled(false);
174
175         _RestoreSettings();
176
177         Show();
178 }
179
180 InterfaceWindow::~InterfaceWindow()
181 {
182         if (fPlaylistWindow)
183                 fPlaylistWindow->ReallyQuit();
184         delete fSettings;
185 }
186
187 /*****************************************************************************
188  * InterfaceWindow::FrameResized
189  *****************************************************************************/
190 void
191 InterfaceWindow::FrameResized(float width, float height)
192 {
193         BRect r(Bounds());
194         fMenuBar->MoveTo(r.LeftTop());
195         fMenuBar->ResizeTo(r.Width(), fMenuBar->Bounds().Height());
196         r.top += fMenuBar->Bounds().Height() + 1.0;
197         p_mediaControl->MoveTo(r.LeftTop());
198         p_mediaControl->ResizeTo(r.Width(), r.Height());
199 }
200
201 /*****************************************************************************
202  * InterfaceWindow::MessageReceived
203  *****************************************************************************/
204 void InterfaceWindow::MessageReceived( BMessage * p_message )
205 {
206         int playback_status;      // remember playback state
207         playback_status = p_intf->p_sys->p_vlc_wrapper->inputGetStatus();
208
209         switch( p_message->what )
210         {
211                 case B_ABOUT_REQUESTED:
212                 {
213                         BAlert* alert = new BAlert( VOUT_TITLE,
214                                                                                 "BeOS " VOUT_TITLE "\n\n<www.videolan.org>", "Ok");
215                         alert->Go();
216                         break;
217                 }
218                 case TOGGLE_ON_TOP:
219                         break;
220                         
221                 case OPEN_FILE:
222                         if( fFilePanel )
223                         {
224                                 fFilePanel->Show();
225                                 break;
226                         }
227                         fFilePanel = new BFilePanel();
228                         fFilePanel->SetTarget( this );
229                         fFilePanel->Show();
230                         break;
231         
232                 case OPEN_PLAYLIST:
233                         if (fPlaylistWindow->Lock())
234                         {
235                                 if (fPlaylistWindow->IsHidden())
236                                         fPlaylistWindow->Show();
237                                 else
238                                         fPlaylistWindow->Activate();
239                                 fPlaylistWindow->Unlock();
240                         }
241                         break;
242                 case OPEN_DVD:
243                         {
244                                 const char *psz_device;
245                                 BString type( "dvd" );
246                                 if( p_message->FindString( "device", &psz_device ) == B_OK )
247                                 {
248                                         BString device( psz_device );
249                                         p_intf->p_sys->p_vlc_wrapper->openDisc( type, device, 0, 0 );
250                                 }
251                                 _UpdatePlaylist();
252                         }
253                         break;
254         
255                 case STOP_PLAYBACK:
256                         // this currently stops playback not nicely
257                         if (playback_status > UNDEF_S)
258                         {
259                                 p_intf->p_sys->p_vlc_wrapper->volume_mute();
260                                 snooze( 400000 );
261                                 p_intf->p_sys->p_vlc_wrapper->playlistStop();
262                                 p_mediaControl->SetStatus(NOT_STARTED_S, DEFAULT_RATE);
263                         }
264                         break;
265         
266                 case START_PLAYBACK:
267                         /*  starts playing in normal mode */
268         
269                 case PAUSE_PLAYBACK:
270                         /* toggle between pause and play */
271                         if (playback_status > UNDEF_S)
272                         {
273                                 /* pause if currently playing */
274                                 if ( playback_status == PLAYING_S )
275                                 {
276                                         p_intf->p_sys->p_vlc_wrapper->volume_mute();
277                                         snooze( 400000 );
278                                         p_intf->p_sys->p_vlc_wrapper->playlistPause();
279                                 }
280                                 else
281                                 {
282                                         p_intf->p_sys->p_vlc_wrapper->volume_restore();
283                                         p_intf->p_sys->p_vlc_wrapper->playlistPlay();
284                                 }
285                         }
286                         else
287                         {
288                                 /* Play a new file */
289                                 p_intf->p_sys->p_vlc_wrapper->playlistPlay();
290                         }       
291                         break;
292         
293                 case FASTER_PLAY:
294                         /* cycle the fast playback modes */
295                         if (playback_status > UNDEF_S)
296                         {
297                                 p_intf->p_sys->p_vlc_wrapper->volume_mute();
298                                 snooze( 400000 );
299                                 p_intf->p_sys->p_vlc_wrapper->playFaster();
300                         }
301                         break;
302         
303                 case SLOWER_PLAY:
304                         /*  cycle the slow playback modes */
305                         if (playback_status > UNDEF_S)
306                         {
307                                 p_intf->p_sys->p_vlc_wrapper->volume_mute();
308                                 snooze( 400000 );
309                                 p_intf->p_sys->p_vlc_wrapper->playSlower();
310                         }
311                         break;
312         
313                 case NORMAL_PLAY:
314                         /*  restore speed to normal if already playing */
315                         if (playback_status > UNDEF_S)
316                         {
317                                 p_intf->p_sys->p_vlc_wrapper->volume_restore();
318                                 p_intf->p_sys->p_vlc_wrapper->playlistPlay();
319                         }
320                         break;
321         
322                 case SEEK_PLAYBACK:
323                         /* handled by semaphores */
324                         break;
325                 // volume related messages
326                 case VOLUME_CHG:
327                         /* adjust the volume */
328                         if (playback_status > UNDEF_S)
329                         {
330                                 p_intf->p_sys->p_vlc_wrapper->set_volume( p_mediaControl->GetVolume() );
331                                 p_mediaControl->SetMuted( p_intf->p_sys->p_vlc_wrapper->is_muted() );
332                         }
333                         break;
334         
335                 case VOLUME_MUTE:
336                         // toggle muting
337                         p_intf->p_sys->p_vlc_wrapper->toggle_mute();
338                         p_mediaControl->SetMuted( p_intf->p_sys->p_vlc_wrapper->is_muted() );
339                         break;
340         
341                 case SELECT_CHANNEL:
342                         if ( playback_status > UNDEF_S )
343                         {
344                                 int32 channel;
345                                 if ( p_message->FindInt32( "channel", &channel ) == B_OK )
346                                 {
347                                         p_intf->p_sys->p_vlc_wrapper->toggleLanguage( channel );
348                                         // vlc seems to remember the volume for every channel,
349                                         // but I would assume that to be somewhat annoying to the user
350                                         // the next call will also unmute the volume, which is probably
351                                         // desired as well, because if the user selects another language,
352                                         // he probably wants to hear the change as well
353                                         snooze( 400000 );       // we have to wait a bit, or the change will be reverted
354                                         p_intf->p_sys->p_vlc_wrapper->set_volume( p_mediaControl->GetVolume() );
355                                 }
356                         }
357                         break;
358         
359                 case SELECT_SUBTITLE:
360                         if ( playback_status > UNDEF_S )
361                         {
362                                 int32 subtitle;
363                                 if ( p_message->FindInt32( "subtitle", &subtitle ) == B_OK )
364                                         p_intf->p_sys->p_vlc_wrapper->toggleSubtitle( subtitle );
365                         }
366                         break;
367         
368                 // specific navigation messages
369                 case PREV_TITLE:
370                 {
371                         int             i_id;
372             i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_id - 1;
373
374             /* Disallow area 0 since it is used for video_ts.vob */
375             if( i_id > 0 )
376             {
377                 p_intf->p_sys->p_vlc_wrapper->toggleTitle(i_id);
378             }
379                         break;
380                 }
381                 case NEXT_TITLE:
382                 {
383                         int             i_id;
384
385             i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_id + 1;
386
387             if( i_id < p_intf->p_sys->p_input->stream.i_area_nb )
388             {
389                 p_intf->p_sys->p_vlc_wrapper->toggleTitle(i_id);
390             }
391                         break;
392                 }
393                 case TOGGLE_TITLE:
394                         if ( playback_status > UNDEF_S )
395                         {
396                                 int32 index;
397                                 if ( p_message->FindInt32( "index", &index ) == B_OK )
398                                         p_intf->p_sys->p_vlc_wrapper->toggleTitle( index );
399                         }
400                         break;
401                 case PREV_CHAPTER:
402                 {
403                         int             i_id;
404
405             i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_part - 1;
406
407             if( i_id >= 0 )
408             {
409                 p_intf->p_sys->p_vlc_wrapper->toggleChapter(i_id);
410             }
411                         break;
412                 }
413                 case NEXT_CHAPTER:
414                 {
415                         int             i_id;
416
417             i_id = p_intf->p_sys->p_input->stream.p_selected_area->i_part + 1;
418
419             if( i_id >= 0 )
420             {
421                 p_intf->p_sys->p_vlc_wrapper->toggleChapter(i_id);
422             }
423                         break;
424                 }
425                 case TOGGLE_CHAPTER:
426                         if ( playback_status > UNDEF_S )
427                         {
428                                 int32 index;
429                                 if ( p_message->FindInt32( "index", &index ) == B_OK )
430                                         p_intf->p_sys->p_vlc_wrapper->toggleChapter( index );
431                         }
432                         break;
433                 case PREV_FILE:
434                         p_intf->p_sys->p_vlc_wrapper->playlistPrev();
435                         break;
436                 case NEXT_FILE:
437                         p_intf->p_sys->p_vlc_wrapper->playlistNext();
438                         break;
439                 // general next/prev functionality (skips to whatever makes most sense)
440                 case NAVIGATE_PREV:
441                         p_intf->p_sys->p_vlc_wrapper->navigatePrev();
442                         break;
443                 case NAVIGATE_NEXT:
444                         p_intf->p_sys->p_vlc_wrapper->navigateNext();
445                         break;
446                 // drag'n'drop and system messages
447                 case B_REFS_RECEIVED:
448                 case B_SIMPLE_DATA:
449                         {
450                                 // figure out if user wants files replaced or added
451                                 bool replace = false;
452                                 if ( p_message->WasDropped() )
453                                         replace = !( modifiers() & B_SHIFT_KEY );
454                                 // build list of files to be played from message contents
455                                 entry_ref ref;
456                                 BList files;
457                                 for ( int i = 0; p_message->FindRef( "refs", i, &ref ) == B_OK; i++ )
458                                 {
459                                         BPath path( &ref );
460                                         if ( path.InitCheck() == B_OK )
461                                                 // the BString objects will be deleted
462                                                 // by the wrapper function further down
463                                                 files.AddItem( new BString( (char*)path.Path() ) );
464                                 }
465                                 // give the list to VLC
466                                 p_intf->p_sys->p_vlc_wrapper->openFiles(&files, replace);
467                                 _UpdatePlaylist();
468                         }
469                         break;
470         
471                 default:
472                         BWindow::MessageReceived( p_message );
473                         break;
474         }
475
476 }
477
478 /*****************************************************************************
479  * InterfaceWindow::QuitRequested
480  *****************************************************************************/
481 bool InterfaceWindow::QuitRequested()
482 {
483         if (p_intf->p_sys->p_input)
484         {
485                 p_intf->p_sys->p_vlc_wrapper->volume_mute();
486                 snooze( 400000 );
487                 p_intf->p_sys->p_vlc_wrapper->playlistStop();
488                 p_mediaControl->SetStatus(NOT_STARTED_S, DEFAULT_RATE);
489         }
490         
491         p_intf->b_die = 1;
492
493         _StoreSettings();
494
495         return( true );
496 }
497
498 /*****************************************************************************
499  * InterfaceWindow::updateInterface
500  *****************************************************************************/
501 void InterfaceWindow::updateInterface()
502 {
503         input_thread_t* input = p_intf->p_sys->p_input;
504         if ( input )
505         {
506                 if ( acquire_sem( p_mediaControl->fScrubSem ) == B_OK )
507                 {
508                     p_intf->p_sys->p_vlc_wrapper->setTimeAsFloat(p_mediaControl->GetSeekTo());
509                 }
510                 else if ( Lock() )
511                 {
512                         bool hasTitles = input->stream.i_area_nb > 1;
513                         bool hasChapters = input->stream.p_selected_area->i_part_nb > 1;
514                         p_mediaControl->SetStatus( input->stream.control.i_status, 
515                                                                            input->stream.control.i_rate );
516                         p_mediaControl->SetProgress( input->stream.p_selected_area->i_tell,
517                                                                                  input->stream.p_selected_area->i_size );
518                         _SetMenusEnabled( true, hasChapters, hasTitles );
519
520                         _UpdateSpeedMenu( input->stream.control.i_rate );
521
522                         // enable/disable skip buttons
523                         bool canSkipPrev;
524                         bool canSkipNext;
525                         p_intf->p_sys->p_vlc_wrapper->getNavCapabilities( &canSkipPrev, &canSkipNext );
526                         p_mediaControl->SetSkippable( canSkipPrev, canSkipNext );
527
528                         if ( p_intf->p_sys->p_vlc_wrapper->has_audio() )
529                         {
530                                 p_mediaControl->SetAudioEnabled( true );
531                                 p_mediaControl->SetMuted( p_intf->p_sys->p_vlc_wrapper->is_muted() );
532                         } else
533                                 p_mediaControl->SetAudioEnabled( false );
534
535                         if ( input != fInputThread )
536                         {
537                                 fInputThread = input;
538                                 _InputStreamChanged();
539                         }
540
541                         Unlock();
542                 }
543                 // update playlist as well
544                 if ( fPlaylistWindow->Lock() )
545                 {
546                         fPlaylistWindow->UpdatePlaylist();
547                         fPlaylistWindow->Unlock();
548                 }
549         }
550         else
551                 _SetMenusEnabled(false);
552
553         playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
554                                                        FIND_ANYWHERE );
555         if ( fPlaylistIsEmpty != ( p_playlist->i_size < 0) )
556         {
557                 if ( Lock() )
558                 {
559                         fPlaylistIsEmpty = !fPlaylistIsEmpty;
560                         p_mediaControl->SetEnabled( !fPlaylistIsEmpty );
561                         Unlock();
562                 }
563         }
564         if ( input != fInputThread )
565                 fInputThread = input;
566
567         fLastUpdateTime = system_time();
568 }
569
570 /*****************************************************************************
571  * InterfaceWindow::IsStopped
572  *****************************************************************************/
573 bool
574 InterfaceWindow::IsStopped() const
575 {
576         return (system_time() - fLastUpdateTime > INTERFACE_UPDATE_TIMEOUT);
577 }
578
579 /*****************************************************************************
580  * InterfaceWindow::_UpdatePlaylist
581  *****************************************************************************/
582 void
583 InterfaceWindow::_UpdatePlaylist()
584 {
585         if ( fPlaylistWindow->Lock() )
586         {
587                 fPlaylistWindow->UpdatePlaylist( true );
588                 fPlaylistWindow->Unlock();
589                 playlist_t *p_playlist = (playlist_t *)vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
590                                                        FIND_ANYWHERE );
591                 fPlaylistIsEmpty = p_playlist->i_size < 1;
592                 p_mediaControl->SetEnabled( !fPlaylistIsEmpty );
593         }
594 }
595
596 /*****************************************************************************
597  * InterfaceWindow::_SetMenusEnabled
598  *****************************************************************************/
599 void
600 InterfaceWindow::_SetMenusEnabled(bool hasFile, bool hasChapters, bool hasTitles)
601 {
602         if (!hasFile)
603         {
604                 hasChapters = false;
605                 hasTitles = false;
606         }
607         if (Lock())
608         {
609                 if (fNextChapterMI->IsEnabled() != hasChapters)
610                         fNextChapterMI->SetEnabled(hasChapters);
611                 if (fPrevChapterMI->IsEnabled() != hasChapters)
612                         fPrevChapterMI->SetEnabled(hasChapters);
613                 if (fChapterMenu->IsEnabled() != hasChapters)
614                         fChapterMenu->SetEnabled(hasChapters);
615                 if (fNextTitleMI->IsEnabled() != hasTitles)
616                         fNextTitleMI->SetEnabled(hasTitles);
617                 if (fPrevTitleMI->IsEnabled() != hasTitles)
618                         fPrevTitleMI->SetEnabled(hasTitles);
619                 if (fTitleMenu->IsEnabled() != hasTitles)
620                         fTitleMenu->SetEnabled(hasTitles);
621                 if (fAudioMenu->IsEnabled() != hasFile)
622                         fAudioMenu->SetEnabled(hasFile);
623                 if (fNavigationMenu->IsEnabled() != hasFile)
624                         fNavigationMenu->SetEnabled(hasFile);
625                 if (fLanguageMenu->IsEnabled() != hasFile)
626                         fLanguageMenu->SetEnabled(hasFile);
627                 if (fSubtitlesMenu->IsEnabled() != hasFile)
628                         fSubtitlesMenu->SetEnabled(hasFile);
629                 if (fSpeedMenu->IsEnabled() != hasFile)
630                         fSpeedMenu->SetEnabled(hasFile);
631                 Unlock();
632         }
633 }
634
635 /*****************************************************************************
636  * InterfaceWindow::_UpdateSpeedMenu
637  *****************************************************************************/
638 void
639 InterfaceWindow::_UpdateSpeedMenu( int rate )
640 {
641         if ( rate == DEFAULT_RATE )
642         {
643                 if ( !fNormalMI->IsMarked() )
644                         fNormalMI->SetMarked( true );
645         }
646         else if ( rate < DEFAULT_RATE )
647         {
648                 if ( !fFasterMI->IsMarked() )
649                         fFasterMI->SetMarked( true );
650         }
651         else
652         {
653                 if ( !fSlowerMI->IsMarked() )
654                         fSlowerMI->SetMarked( true );
655         }
656 }
657
658 /*****************************************************************************
659  * InterfaceWindow::_InputStreamChanged
660  *****************************************************************************/
661 void
662 InterfaceWindow::_InputStreamChanged()
663 {
664 //printf("InterfaceWindow::_InputStreamChanged()\n");
665         // TODO: move more stuff from updateInterface() here!
666         snooze( 400000 );
667         p_intf->p_sys->p_vlc_wrapper->set_volume( p_mediaControl->GetVolume() );
668 }
669
670 /*****************************************************************************
671  * InterfaceWindow::_LoadSettings
672  *****************************************************************************/
673 status_t
674 InterfaceWindow::_LoadSettings( BMessage* message, const char* fileName, const char* folder )
675 {
676         status_t ret = B_BAD_VALUE;
677         if ( message )
678         {
679                 BPath path;
680                 if ( ( ret = find_directory( B_USER_SETTINGS_DIRECTORY, &path ) ) == B_OK )
681                 {
682                         // passing folder is optional
683                         if ( folder )
684                                 ret = path.Append( folder );
685                         if ( ret == B_OK && ( ret = path.Append( fileName ) ) == B_OK )
686                         {
687                                 BFile file( path.Path(), B_READ_ONLY );
688                                 if ( ( ret = file.InitCheck() ) == B_OK )
689                                 {
690                                         ret = message->Unflatten( &file );
691                                         file.Unset();
692                                 }
693                         }
694                 }
695         }
696         return ret;
697 }
698
699 /*****************************************************************************
700  * InterfaceWindow::_SaveSettings
701  *****************************************************************************/
702 status_t
703 InterfaceWindow::_SaveSettings( BMessage* message, const char* fileName, const char* folder )
704 {
705         status_t ret = B_BAD_VALUE;
706         if ( message )
707         {
708                 BPath path;
709                 if ( ( ret = find_directory( B_USER_SETTINGS_DIRECTORY, &path ) ) == B_OK )
710                 {
711                         // passing folder is optional
712                         if ( folder && ( ret = path.Append( folder ) ) == B_OK )
713                                 ret = create_directory( path.Path(), 0777 );
714                         if ( ret == B_OK && ( ret = path.Append( fileName ) ) == B_OK )
715                         {
716                                 BFile file( path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE );
717                                 if ( ( ret = file.InitCheck() ) == B_OK )
718                                 {
719                                         ret = message->Flatten( &file );
720                                         file.Unset();
721                                 }
722                         }
723                 }
724         }
725         return ret;
726 }
727
728 /*****************************************************************************
729  * InterfaceWindow::_RestoreSettings
730  *****************************************************************************/
731 bool
732 make_sure_frame_is_on_screen( BRect& frame )
733 {
734         BScreen screen( B_MAIN_SCREEN_ID );
735         if (frame.IsValid() && screen.IsValid()) {
736                 if (!screen.Frame().Contains(frame)) {
737                         // make sure frame fits in the screen
738                         if (frame.Width() > screen.Frame().Width())
739                                 frame.right -= frame.Width() - screen.Frame().Width() + 10.0;
740                         if (frame.Height() > screen.Frame().Height())
741                                 frame.bottom -= frame.Height() - screen.Frame().Height() + 30.0;
742                         // frame is now at the most the size of the screen
743                         if (frame.right > screen.Frame().right)
744                                 frame.OffsetBy(-(frame.right - screen.Frame().right), 0.0);
745                         if (frame.bottom > screen.Frame().bottom)
746                                 frame.OffsetBy(0.0, -(frame.bottom - screen.Frame().bottom));
747                         if (frame.left < screen.Frame().left)
748                                 frame.OffsetBy((screen.Frame().left - frame.left), 0.0);
749                         if (frame.top < screen.Frame().top)
750                                 frame.OffsetBy(0.0, (screen.Frame().top - frame.top));
751                 }
752                 return true;
753         }
754         return false;
755 }
756
757 void
758 make_sure_frame_is_within_limits( BRect& frame, float minWidth, float minHeight,
759                                                                   float maxWidth, float maxHeight )
760 {
761         if ( frame.Width() < minWidth )
762                 frame.right = frame.left + minWidth;
763         if ( frame.Height() < minHeight )
764                 frame.bottom = frame.top + minHeight;
765         if ( frame.Width() > maxWidth )
766                 frame.right = frame.left + maxWidth;
767         if ( frame.Height() > maxHeight )
768                 frame.bottom = frame.top + maxHeight;
769 }
770
771 /*****************************************************************************
772  * InterfaceWindow::_RestoreSettings
773  *****************************************************************************/
774 void
775 InterfaceWindow::_RestoreSettings()
776 {
777         if ( _LoadSettings( fSettings, "interface_settings", "VideoLAN Client" ) == B_OK )
778         {
779                 BRect mainFrame;
780                 if ( fSettings->FindRect( "main frame", &mainFrame ) == B_OK )
781                 {
782                         // sanity checks: make sure window is not too big/small
783                         // and that it's not off-screen
784                         float minWidth, maxWidth, minHeight, maxHeight;
785                         GetSizeLimits( &minWidth, &maxWidth, &minHeight, &maxHeight );
786
787                         make_sure_frame_is_within_limits( mainFrame,
788                                                                                           minWidth, minHeight, maxWidth, maxHeight );
789                         make_sure_frame_is_on_screen( mainFrame );
790
791
792                         MoveTo( mainFrame.LeftTop() );
793                         ResizeTo( mainFrame.Width(), mainFrame.Height() );
794                 }
795                 if ( fPlaylistWindow->Lock() )
796                 {
797                         BRect playlistFrame;
798                         if (fSettings->FindRect( "playlist frame", &playlistFrame ) == B_OK )
799                         {
800                                 // sanity checks: make sure window is not too big/small
801                                 // and that it's not off-screen
802                                 float minWidth, maxWidth, minHeight, maxHeight;
803                                 fPlaylistWindow->GetSizeLimits( &minWidth, &maxWidth, &minHeight, &maxHeight );
804
805                                 make_sure_frame_is_within_limits( playlistFrame,
806                                                                                                   minWidth, minHeight, maxWidth, maxHeight );
807                                 make_sure_frame_is_on_screen( playlistFrame );
808
809                                 fPlaylistWindow->MoveTo( playlistFrame.LeftTop() );
810                                 fPlaylistWindow->ResizeTo( playlistFrame.Width(), playlistFrame.Height() );
811                         }
812                         
813                         bool showing;
814                         if ( fSettings->FindBool( "playlist showing", &showing ) == B_OK )
815                         {
816                                 if ( showing )
817                                 {
818                                         if ( fPlaylistWindow->IsHidden() )
819                                                 fPlaylistWindow->Show();
820                                 }
821                                 else
822                                 {
823                                         if ( !fPlaylistWindow->IsHidden() )
824                                                 fPlaylistWindow->Hide();
825                                 }
826                         }
827
828                         fPlaylistWindow->Unlock();
829                 }
830         }
831 }
832
833 /*****************************************************************************
834  * InterfaceWindow::_StoreSettings
835  *****************************************************************************/
836 void
837 InterfaceWindow::_StoreSettings()
838 {
839         if ( fSettings->ReplaceRect( "main frame", Frame() ) != B_OK )
840                 fSettings->AddRect( "main frame", Frame() );
841         if ( fPlaylistWindow->Lock() )
842         {
843                 if (fSettings->ReplaceRect( "playlist frame", fPlaylistWindow->Frame() ) != B_OK)
844                         fSettings->AddRect( "playlist frame", fPlaylistWindow->Frame() );
845                 if (fSettings->ReplaceBool( "playlist showing", !fPlaylistWindow->IsHidden() ) != B_OK)
846                         fSettings->AddBool( "playlist showing", !fPlaylistWindow->IsHidden() );
847                 fPlaylistWindow->Unlock();
848         }
849         _SaveSettings( fSettings, "interface_settings", "VideoLAN Client" );
850 }
851
852 /*****************************************************************************
853  * CDMenu::CDMenu
854  *****************************************************************************/
855 CDMenu::CDMenu(const char *name)
856           : BMenu(name)
857 {
858 }
859
860 /*****************************************************************************
861  * CDMenu::~CDMenu
862  *****************************************************************************/
863 CDMenu::~CDMenu()
864 {
865 }
866
867 /*****************************************************************************
868  * CDMenu::AttachedToWindow
869  *****************************************************************************/
870 void CDMenu::AttachedToWindow(void)
871 {
872         // remove all items
873         while (BMenuItem* item = RemoveItem(0L))
874                 delete item;
875         GetCD("/dev/disk");
876         BMenu::AttachedToWindow();
877 }
878
879 /*****************************************************************************
880  * CDMenu::GetCD
881  *****************************************************************************/
882 int CDMenu::GetCD( const char *directory )
883 {
884         BVolumeRoster *volRoster;
885         BVolume    *vol;
886         BDirectory      *dir;
887         int                status;
888         int                mounted;   
889         char              name[B_FILE_NAME_LENGTH]; 
890         fs_info    info;
891         dev_t            dev;
892         
893         volRoster = new BVolumeRoster();
894         vol = new BVolume();
895         dir = new BDirectory();
896         status = volRoster->GetNextVolume(vol);
897         status = vol->GetRootDirectory(dir);
898         while (status ==  B_NO_ERROR)
899         {
900                 mounted = vol->GetName(name);   
901                 if ((mounted == B_OK) && /* Disk is currently Mounted */
902                         (vol->IsReadOnly()) ) /* Disk is read-only */
903                 {
904                         dev = vol->Device();
905                         fs_stat_dev(dev, &info);
906                         
907                         device_geometry g;
908                         int i_dev;
909                         i_dev = open( info.device_name, O_RDONLY );
910                    
911                         if( i_dev >= 0 )
912                         {
913                                 if( ioctl(i_dev, B_GET_GEOMETRY, &g, sizeof(g)) >= 0 )
914                                 {
915                                         if( g.device_type == B_CD ) //ensure the drive is a CD-ROM
916                                         {
917                                                 BMessage *msg;
918                                                 msg = new BMessage( OPEN_DVD );
919                                                 msg->AddString( "device", info.device_name );
920                                                 BMenuItem *menu_item;
921                                                 menu_item = new BMenuItem( name, msg );
922                                                 AddItem( menu_item );
923                                         }
924                                         close(i_dev);
925                                 }
926                         }
927                 }
928                 vol->Unset();
929                 status = volRoster->GetNextVolume(vol);
930         }
931         return 0;
932 }
933
934 /*****************************************************************************
935  * LanguageMenu::LanguageMenu
936  *****************************************************************************/
937 LanguageMenu::LanguageMenu(const char *name, int menu_kind, 
938                                                         intf_thread_t  *p_interface)
939         :BMenu(name)
940 {
941         kind = menu_kind;
942         p_intf = p_interface;
943 }
944
945 /*****************************************************************************
946  * LanguageMenu::~LanguageMenu
947  *****************************************************************************/
948 LanguageMenu::~LanguageMenu()
949 {
950 }
951
952 /*****************************************************************************
953  * LanguageMenu::AttachedToWindow
954  *****************************************************************************/
955 void LanguageMenu::AttachedToWindow()
956 {
957         // remove all items
958         while ( BMenuItem* item = RemoveItem( 0L ) )
959                 delete item;
960
961         SetRadioMode( true );
962         _GetChannels();
963         BMenu::AttachedToWindow();
964 }
965
966 /*****************************************************************************
967  * LanguageMenu::_GetChannels
968  *****************************************************************************/
969 void LanguageMenu::_GetChannels()
970 {
971         char  *psz_name;
972         bool   b_active;
973         BMessage *msg;
974         BMenuItem *menu_item;
975         int     i;
976         es_descriptor_t *p_es  = NULL;
977
978         // Insert the "None" item if in subtitle mode
979         if( kind != AUDIO_ES ) //subtitle
980         {
981                 msg = new BMessage( SELECT_SUBTITLE );
982                 msg->AddInt32( "subtitle", -1 );
983                 menu_item = new BMenuItem( "None", msg );
984                 AddItem( menu_item );
985                 menu_item->SetMarked( true );
986         }
987
988         input_thread_t* input = p_intf->p_sys->p_input;
989         if ( input )
990         {
991                 vlc_mutex_lock( &input->stream.stream_lock );
992                 for( i = 0; i < input->stream.i_selected_es_number; i++ )
993                 {
994                         if( kind == input->stream.pp_selected_es[i]->i_cat )
995                                 p_es = input->stream.pp_selected_es[i];
996                 }
997         
998                 int32 addedItems = 0;
999                 bool emptyItemAdded = false;
1000                 uint32 what = kind == AUDIO_ES ? SELECT_CHANNEL : SELECT_SUBTITLE;
1001                 const char* fieldName = kind == AUDIO_ES ? "channel" : "subtitle";
1002         
1003                 for ( i = 0; i < input->stream.i_es_number; i++ )
1004                 {
1005                         if ( kind == input->stream.pp_es[i]->i_cat )
1006                         {
1007                                 bool addItem = true;
1008                                 psz_name = input->stream.pp_es[i]->psz_desc;
1009                                 // workarround for irritating empty strings
1010                                 if ( strcmp(psz_name, "") == 0 )
1011                                 {
1012 //                                      if ( kind != AUDIO_ES ) // don't add empty subtitle items, they don't work anyways
1013 //                                              addItem = false;
1014 //                                      else
1015 //                                      {
1016                                                 if (!emptyItemAdded)
1017                                                 {
1018                                                         psz_name = "<default>";
1019                                                         emptyItemAdded = true;
1020                                                 }
1021                                                 else
1022                                                         psz_name = "<unkown>";
1023 //                                      }
1024                                 }
1025                                 if ( addItem )
1026                                 {
1027                                         addedItems++;
1028                                         msg = new BMessage( what );
1029                                         msg->AddInt32( fieldName, i );
1030                                         menu_item = new BMenuItem( psz_name, msg );
1031                                         AddItem( menu_item );
1032                                         b_active = ( p_es == input->stream.pp_es[i] );
1033                                         menu_item->SetMarked( b_active );
1034                                 }
1035                         }
1036                 }
1037                 vlc_mutex_unlock( &input->stream.stream_lock );
1038         
1039                 // enhance readability and separate first item from rest
1040                 if ( ( emptyItemAdded || kind != AUDIO_ES ) && addedItems > 1 )
1041                          AddItem( new BSeparatorItem(), 1 );
1042         }
1043 }
1044
1045
1046
1047 /*****************************************************************************
1048  * TitleMenu::TitleMenu
1049  *****************************************************************************/
1050 TitleMenu::TitleMenu( const char *name, intf_thread_t  *p_interface )
1051         : BMenu(name),
1052         p_intf( p_interface )
1053 {
1054 }
1055
1056 /*****************************************************************************
1057  * TitleMenu::~TitleMenu
1058  *****************************************************************************/
1059 TitleMenu::~TitleMenu()
1060 {
1061 }
1062
1063 /*****************************************************************************
1064  * TitleMenu::AttachedToWindow
1065  *****************************************************************************/
1066 void TitleMenu::AttachedToWindow()
1067 {
1068         // make title menu empty
1069         while ( BMenuItem* item = RemoveItem( 0L ) )
1070                 delete item;
1071
1072         input_thread_t* input = p_intf->p_sys->p_input;
1073         if ( input )
1074         {
1075                 // lock stream access
1076                 vlc_mutex_lock( &input->stream.stream_lock );
1077                 // populate menu according to current stream
1078                 int32 numTitles = input->stream.i_area_nb;
1079                 if ( numTitles > 1 )
1080                 {
1081                         // disallow title 0!
1082                         for ( int32 i = 1; i < numTitles; i++ )
1083                         {
1084                                 BMessage* message = new BMessage( TOGGLE_TITLE );
1085                                 message->AddInt32( "index", i );
1086                                 BString helper( "" );
1087                                 helper << i;
1088                                 BMenuItem* item = new BMenuItem( helper.String(), message );
1089                                 item->SetMarked( input->stream.p_selected_area->i_id == i );
1090                                 AddItem( item );
1091                         }
1092                 }
1093                 // done messing with stream
1094                 vlc_mutex_unlock( &input->stream.stream_lock );
1095         }
1096         BMenu::AttachedToWindow();
1097 }
1098
1099
1100 /*****************************************************************************
1101  * ChapterMenu::ChapterMenu
1102  *****************************************************************************/
1103 ChapterMenu::ChapterMenu( const char *name, intf_thread_t  *p_interface )
1104         : BMenu(name),
1105         p_intf( p_interface )
1106 {
1107 }
1108
1109 /*****************************************************************************
1110  * ChapterMenu::~ChapterMenu
1111  *****************************************************************************/
1112 ChapterMenu::~ChapterMenu()
1113 {
1114 }
1115
1116 /*****************************************************************************
1117  * ChapterMenu::AttachedToWindow
1118  *****************************************************************************/
1119 void ChapterMenu::AttachedToWindow()
1120 {
1121         // make title menu empty
1122         while ( BMenuItem* item = RemoveItem( 0L ) )
1123                 delete item;
1124
1125         input_thread_t* input = p_intf->p_sys->p_input;
1126         if ( input )
1127         {
1128                 // lock stream access
1129                 vlc_mutex_lock( &input->stream.stream_lock );
1130                 // populate menu according to current stream
1131                 int32 numChapters = input->stream.p_selected_area->i_part_nb;
1132                 if ( numChapters > 1 )
1133                 {
1134                         for ( int32 i = 0; i < numChapters; i++ )
1135                         {
1136                                 BMessage* message = new BMessage( TOGGLE_CHAPTER );
1137                                 message->AddInt32( "index", i );
1138                                 BString helper( "" );
1139                                 helper << i + 1;
1140                                 BMenuItem* item = new BMenuItem( helper.String(), message );
1141                                 item->SetMarked( input->stream.p_selected_area->i_part == i );
1142                                 AddItem( item );
1143                         }
1144                 }
1145                 // done messing with stream
1146                 vlc_mutex_unlock( &input->stream.stream_lock );
1147         }
1148         BMenu::AttachedToWindow();
1149 }
1150