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