35987880113acfd9eafa9ababefb25c97a3c52bf
[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.38 2003/05/17 15:20:46 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 "VlcWrapper.h"
47 #include "MsgVals.h"
48 #include "MediaControlView.h"
49 #include "PlayListWindow.h"
50 #include "PreferencesWindow.h"
51 #include "MessagesWindow.h"
52 #include "InterfaceWindow.h"
53
54 #define INTERFACE_UPDATE_TIMEOUT 80000 // 2 frames if at 25 fps
55 #define INTERFACE_LOCKING_TIMEOUT 5000
56
57 // make_sure_frame_is_on_screen
58 bool
59 make_sure_frame_is_on_screen( BRect& frame )
60 {
61         BScreen screen( B_MAIN_SCREEN_ID );
62         if (frame.IsValid() && screen.IsValid()) {
63                 if (!screen.Frame().Contains(frame)) {
64                         // make sure frame fits in the screen
65                         if (frame.Width() > screen.Frame().Width())
66                                 frame.right -= frame.Width() - screen.Frame().Width() + 10.0;
67                         if (frame.Height() > screen.Frame().Height())
68                                 frame.bottom -= frame.Height() - screen.Frame().Height() + 30.0;
69                         // frame is now at the most the size of the screen
70                         if (frame.right > screen.Frame().right)
71                                 frame.OffsetBy(-(frame.right - screen.Frame().right), 0.0);
72                         if (frame.bottom > screen.Frame().bottom)
73                                 frame.OffsetBy(0.0, -(frame.bottom - screen.Frame().bottom));
74                         if (frame.left < screen.Frame().left)
75                                 frame.OffsetBy((screen.Frame().left - frame.left), 0.0);
76                         if (frame.top < screen.Frame().top)
77                                 frame.OffsetBy(0.0, (screen.Frame().top - frame.top));
78                 }
79                 return true;
80         }
81         return false;
82 }
83
84 // make_sure_frame_is_within_limits
85 void
86 make_sure_frame_is_within_limits( BRect& frame, float minWidth, float minHeight,
87                                   float maxWidth, float maxHeight )
88 {
89     if ( frame.Width() < minWidth )
90         frame.right = frame.left + minWidth;
91     if ( frame.Height() < minHeight )
92         frame.bottom = frame.top + minHeight;
93     if ( frame.Width() > maxWidth )
94         frame.right = frame.left + maxWidth;
95     if ( frame.Height() > maxHeight )
96         frame.bottom = frame.top + maxHeight;
97 }
98
99 // get_volume_info
100 bool
101 get_volume_info( BVolume& volume, BString& volumeName, bool& isCDROM, BString& deviceName )
102 {
103         bool success = false;
104         isCDROM = false;
105         deviceName = "";
106         volumeName = "";
107         char name[B_FILE_NAME_LENGTH];
108         if ( volume.GetName( name ) >= B_OK )   // disk is currently mounted
109         {
110                 volumeName = name;
111                 dev_t dev = volume.Device();
112                 fs_info info;
113                 if ( fs_stat_dev( dev, &info ) == B_OK )
114                 {
115                         success = true;
116                         deviceName = info.device_name;
117                         if ( volume.IsReadOnly() )
118                         {
119                                 int i_dev = open( info.device_name, O_RDONLY );
120                                 if ( i_dev >= 0 )
121                                 {
122                                         device_geometry g;
123                                         if ( ioctl( i_dev, B_GET_GEOMETRY, &g, sizeof( g ) ) >= 0 )
124                                                 isCDROM = ( g.device_type == B_CD );
125                                         close( i_dev );
126                                 }
127                         }
128                 }
129         }
130         return success;
131 }
132
133 // collect_folder_contents
134 void
135 collect_folder_contents( BDirectory& dir, BList& list, bool& deep, bool& asked, BEntry& entry )
136 {
137         while ( dir.GetNextEntry( &entry, true ) == B_OK )
138         {
139                 if ( !entry.IsDirectory() )
140                 {
141                         BPath path;
142                         // since the directory will give us the entries in reverse order,
143                         // we put them each at the same index, effectively reversing the
144                         // items while adding them
145                         if ( entry.GetPath( &path ) == B_OK )
146                         {
147                                 BString* string = new BString( path.Path() );
148                                 if ( !list.AddItem( string, 0 ) )
149                                         delete string;  // at least don't leak
150                         }
151                 }
152                 else
153                 {
154                         if ( !asked )
155                         {
156                                 // ask user if we should parse sub-folders as well
157                                 BAlert* alert = new BAlert( "sub-folders?",
158                                                                                         _("Open files from all sub-folders as well?"),
159                                                                                         _("No"), _("Yes"), NULL, B_WIDTH_AS_USUAL,
160                                                                                         B_IDEA_ALERT );
161                                 int32 buttonIndex = alert->Go();
162                                 deep = buttonIndex == 1;
163                                 asked = true;
164                                 // never delete BAlerts!!
165                         }
166                         if ( deep )
167                         {
168                                 BDirectory subDir( &entry );
169                                 if ( subDir.InitCheck() == B_OK )
170                                         collect_folder_contents( subDir, list,
171                                                                                          deep, asked, entry );
172                         }
173                 }
174         }
175 }
176
177
178 /*****************************************************************************
179  * InterfaceWindow
180  *****************************************************************************/
181
182 InterfaceWindow::InterfaceWindow( BRect frame, const char* name,
183                                   intf_thread_t* p_interface )
184     : BWindow( frame, name, B_TITLED_WINDOW_LOOK, B_NORMAL_WINDOW_FEEL,
185                B_NOT_ZOOMABLE | B_WILL_ACCEPT_FIRST_CLICK | B_ASYNCHRONOUS_CONTROLS ),
186       p_intf( p_interface ),
187       fFilePanel( NULL ),
188       fLastUpdateTime( system_time() ),
189           fSettings( new BMessage( 'sett' ) ),
190           p_wrapper( p_intf->p_sys->p_wrapper )
191 {
192     fPlaylistIsEmpty = !( p_wrapper->PlaylistSize() > 0 );
193     
194     BScreen screen;
195     BRect screen_rect = screen.Frame();
196     BRect window_rect;
197     window_rect.Set( ( screen_rect.right - PREFS_WINDOW_WIDTH ) / 2,
198                      ( screen_rect.bottom - PREFS_WINDOW_HEIGHT ) / 2,
199                      ( screen_rect.right + PREFS_WINDOW_WIDTH ) / 2,
200                      ( screen_rect.bottom + PREFS_WINDOW_HEIGHT ) / 2 );
201     fPreferencesWindow = new PreferencesWindow( p_intf, window_rect, _("Preferences") );
202     window_rect.Set( screen_rect.right - 500,
203                      screen_rect.top + 50,
204                      screen_rect.right - 150,
205                      screen_rect.top + 250 );
206     fPlaylistWindow = new PlayListWindow( window_rect, _("Playlist"), this, p_intf );
207     window_rect.Set( screen_rect.right - 550,
208                      screen_rect.top + 300,
209                      screen_rect.right - 150,
210                      screen_rect.top + 500 );
211     fMessagesWindow = new MessagesWindow( p_intf, window_rect, _("Messages") );
212
213     // the media control view
214     p_mediaControl = new MediaControlView( BRect( 0.0, 0.0, 250.0, 50.0 ),
215                                            p_intf );
216     p_mediaControl->SetViewColor( ui_color( B_PANEL_BACKGROUND_COLOR ) );
217
218     float width, height;
219     p_mediaControl->GetPreferredSize( &width, &height );
220
221     // set up the main menu
222     fMenuBar = new BMenuBar( BRect(0.0, 0.0, width, 15.0), "main menu",
223                              B_FOLLOW_NONE, B_ITEMS_IN_ROW, false );
224
225     // make menu bar resize to correct height
226     float menuWidth, menuHeight;
227     fMenuBar->GetPreferredSize( &menuWidth, &menuHeight );
228     fMenuBar->ResizeTo( width, menuHeight );    // don't change! it's a workarround!
229     // take care of proper size for ourself
230     height += fMenuBar->Bounds().Height();
231     ResizeTo( width, height );
232
233     p_mediaControl->MoveTo( fMenuBar->Bounds().LeftBottom() + BPoint(0.0, 1.0) );
234     AddChild( fMenuBar );
235     AddChild( p_mediaControl );
236
237     // Add the file Menu
238     BMenu* fileMenu = new BMenu( _("File") );
239     fMenuBar->AddItem( fileMenu );
240     fileMenu->AddItem( new BMenuItem( _AddEllipsis(_("Open File")),
241                                       new BMessage( OPEN_FILE ), 'O') );
242     
243     fileMenu->AddItem( new CDMenu( _("Open Disc") ) );
244
245     fileMenu->AddItem( new BMenuItem( _AddEllipsis(_("Open Subtitles")),
246                                       new BMessage( LOAD_SUBFILE ) ) );
247     
248     fileMenu->AddSeparatorItem();
249     BMenuItem* item = new BMenuItem( _AddEllipsis(_("About")),
250                                      new BMessage( B_ABOUT_REQUESTED ), 'A');
251     item->SetTarget( be_app );
252     fileMenu->AddItem( item );
253     fileMenu->AddItem( new BMenuItem( _("Quit"), new BMessage( B_QUIT_REQUESTED ), 'Q') );
254
255     fLanguageMenu = new LanguageMenu( _("Language"), AUDIO_ES, p_wrapper);
256     fSubtitlesMenu = new LanguageMenu( _("Subtitles"), SPU_ES, p_wrapper);
257
258     /* Add the Audio menu */
259     fAudioMenu = new BMenu( _("Audio") );
260     fMenuBar->AddItem ( fAudioMenu );
261     fAudioMenu->AddItem( fLanguageMenu );
262     fAudioMenu->AddItem( fSubtitlesMenu );
263
264     fPrevTitleMI = new BMenuItem( _("Prev Title"), new BMessage( PREV_TITLE ) );
265     fNextTitleMI = new BMenuItem( _("Next Title"), new BMessage( NEXT_TITLE ) );
266     fPrevChapterMI = new BMenuItem( _("Prev Chapter"), new BMessage( PREV_CHAPTER ) );
267     fNextChapterMI = new BMenuItem( _("Next Chapter"), new BMessage( NEXT_CHAPTER ) );
268     fGotoMenuMI = new BMenuItem( _("Goto Menu"), new BMessage( NAVIGATE_MENU ) );
269
270     /* Add the Navigation menu */
271     fNavigationMenu = new BMenu( _("Navigation") );
272     fMenuBar->AddItem( fNavigationMenu );
273     fNavigationMenu->AddItem( fGotoMenuMI );
274     fNavigationMenu->AddSeparatorItem();
275     fNavigationMenu->AddItem( fPrevTitleMI );
276     fNavigationMenu->AddItem( fNextTitleMI );
277     fNavigationMenu->AddItem( fTitleMenu = new TitleMenu( _("Go to Title"), p_intf ) );
278     fNavigationMenu->AddSeparatorItem();
279     fNavigationMenu->AddItem( fPrevChapterMI );
280     fNavigationMenu->AddItem( fNextChapterMI );
281     fNavigationMenu->AddItem( fChapterMenu = new ChapterMenu( _("Go to Chapter"), p_intf ) );
282
283     /* Add the Speed menu */
284     fSpeedMenu = new BMenu( _("Speed") );
285     fSpeedMenu->SetRadioMode( true );
286     fSpeedMenu->AddItem( fSlowerMI = new BMenuItem( _("Slower"), new BMessage( SLOWER_PLAY ) ) );
287     fNormalMI = new BMenuItem( _("Normal"), new BMessage( NORMAL_PLAY ) );
288     fNormalMI->SetMarked(true); // default to normal speed
289     fSpeedMenu->AddItem( fNormalMI );
290     fSpeedMenu->AddItem( fFasterMI = new BMenuItem( _("Faster"), new BMessage( FASTER_PLAY) ) );
291     fSpeedMenu->SetTargetForItems( this );
292     fMenuBar->AddItem( fSpeedMenu );
293
294     /* Add the Show menu */
295     fShowMenu = new BMenu( _("Window") );
296     fShowMenu->AddItem( new BMenuItem( _AddEllipsis(_("Play List")),
297                                        new BMessage( OPEN_PLAYLIST ), 'P') );
298     fShowMenu->AddItem( new BMenuItem( _AddEllipsis(_("Messages")),
299                                        new BMessage( OPEN_MESSAGES ), 'M' ) );
300     fShowMenu->AddItem( new BMenuItem( _AddEllipsis(_("Preferences")),
301                                        new BMessage( OPEN_PREFERENCES ), 'S' ) );
302     fMenuBar->AddItem( fShowMenu );                            
303
304     /* Prepare fow showing */
305     _SetMenusEnabled( false );
306     p_mediaControl->SetEnabled( false );
307
308         _RestoreSettings();    
309
310     Show();
311 }
312
313 InterfaceWindow::~InterfaceWindow()
314 {
315     if( fPlaylistWindow )
316         fPlaylistWindow->ReallyQuit();
317     fPlaylistWindow = NULL;
318     if( fMessagesWindow )
319         fMessagesWindow->ReallyQuit();
320     fMessagesWindow = NULL;
321     if( fPreferencesWindow )
322         fPreferencesWindow->ReallyQuit();
323     fPreferencesWindow = NULL;
324         delete fFilePanel;
325         delete fSettings;
326 }
327
328 /*****************************************************************************
329  * InterfaceWindow::FrameResized
330  *****************************************************************************/
331 void
332 InterfaceWindow::FrameResized(float width, float height)
333 {
334     BRect r(Bounds());
335     fMenuBar->MoveTo(r.LeftTop());
336     fMenuBar->ResizeTo(r.Width(), fMenuBar->Bounds().Height());
337     r.top += fMenuBar->Bounds().Height() + 1.0;
338     p_mediaControl->MoveTo(r.LeftTop());
339     p_mediaControl->ResizeTo(r.Width(), r.Height());
340 }
341
342 /*****************************************************************************
343  * InterfaceWindow::MessageReceived
344  *****************************************************************************/
345 void InterfaceWindow::MessageReceived( BMessage * p_message )
346 {
347     int playback_status;      // remember playback state
348     playback_status = p_wrapper->InputStatus();
349
350     switch( p_message->what )
351     {
352         case B_ABOUT_REQUESTED:
353         {
354             BAlert* alert = new BAlert( "VLC " PACKAGE_VERSION,
355                                         "VLC " PACKAGE_VERSION " for BeOS"
356                                         "\n\n<www.videolan.org>", _("OK"));
357             alert->Go();
358             break;
359         }
360         case TOGGLE_ON_TOP:
361             break;
362             
363         case OPEN_FILE:
364                 _ShowFilePanel( B_REFS_RECEIVED, _("VideoLAN Client: Open Media Files") );
365             break;
366
367         case LOAD_SUBFILE:
368                 _ShowFilePanel( SUBFILE_RECEIVED, _("VideoLAN Client: Open Subtitle File") );
369             break;
370
371         case OPEN_PLAYLIST:
372             if (fPlaylistWindow->Lock())
373             {
374                 if (fPlaylistWindow->IsHidden())
375                     fPlaylistWindow->Show();
376                 else
377                     fPlaylistWindow->Activate();
378                 fPlaylistWindow->Unlock();
379             }
380             break;
381         case OPEN_DVD:
382             {
383                 const char *psz_device;
384                 BString type( "dvd" );
385                 if( p_message->FindString( "device", &psz_device ) == B_OK )
386                 {
387                     BString device( psz_device );
388                     p_wrapper->OpenDisc( type, device, 0, 0 );
389                 }
390                 _UpdatePlaylist();
391             }
392             break;
393         
394         case SUBFILE_RECEIVED:
395         {
396             entry_ref ref;
397             if( p_message->FindRef( "refs", 0, &ref ) == B_OK )
398             {
399                 BPath path( &ref );
400                 if ( path.InitCheck() == B_OK )
401                     p_wrapper->LoadSubFile( path.Path() );
402             }
403             break;
404         }
405     
406         case STOP_PLAYBACK:
407             // this currently stops playback not nicely
408             if (playback_status > UNDEF_S)
409             {
410                 p_wrapper->PlaylistStop();
411                 p_mediaControl->SetStatus(NOT_STARTED_S, DEFAULT_RATE);
412             }
413             break;
414     
415         case START_PLAYBACK:
416             /*  starts playing in normal mode */
417     
418         case PAUSE_PLAYBACK:
419             /* toggle between pause and play */
420             if (playback_status > UNDEF_S)
421             {
422                 /* pause if currently playing */
423                 if ( playback_status == PLAYING_S )
424                 {
425                     p_wrapper->PlaylistPause();
426                 }
427                 else
428                 {
429                     p_wrapper->PlaylistPlay();
430                 }
431             }
432             else
433             {
434                 /* Play a new file */
435                 p_wrapper->PlaylistPlay();
436             }    
437             break;
438     
439         case FASTER_PLAY:
440             /* cycle the fast playback modes */
441             if (playback_status > UNDEF_S)
442             {
443                 p_wrapper->InputFaster();
444             }
445             break;
446     
447         case SLOWER_PLAY:
448             /*  cycle the slow playback modes */
449             if (playback_status > UNDEF_S)
450             {
451                 p_wrapper->InputSlower();
452             }
453             break;
454     
455         case NORMAL_PLAY:
456             /*  restore speed to normal if already playing */
457             if (playback_status > UNDEF_S)
458             {
459                 p_wrapper->PlaylistPlay();
460             }
461             break;
462     
463         case SEEK_PLAYBACK:
464             /* handled by semaphores */
465             break;
466         // volume related messages
467         case VOLUME_CHG:
468             /* adjust the volume */
469             if (playback_status > UNDEF_S)
470             {
471                 p_wrapper->SetVolume( p_mediaControl->GetVolume() );
472                 p_mediaControl->SetMuted( p_wrapper->IsMuted() );
473             }
474             break;
475     
476         case VOLUME_MUTE:
477             // toggle muting
478             if( p_wrapper->IsMuted() )
479                 p_wrapper->VolumeRestore();
480             else
481                 p_wrapper->VolumeMute();
482             p_mediaControl->SetMuted( p_wrapper->IsMuted() );
483             break;
484     
485         case SELECT_CHANNEL:
486             if ( playback_status > UNDEF_S )
487             {
488                 int32 channel;
489                 if ( p_message->FindInt32( "channel", &channel ) == B_OK )
490                 {
491                     p_wrapper->ToggleLanguage( channel );
492                 }
493             }
494             break;
495     
496         case SELECT_SUBTITLE:
497             if ( playback_status > UNDEF_S )
498             {
499                 int32 subtitle;
500                 if ( p_message->FindInt32( "subtitle", &subtitle ) == B_OK )
501                      p_wrapper->ToggleSubtitle( subtitle );
502             }
503             break;
504     
505         // specific navigation messages
506         case PREV_TITLE:
507         {
508             p_wrapper->PrevTitle();
509             break;
510         }
511         case NEXT_TITLE:
512         {
513             p_wrapper->NextTitle();
514             break;
515         }
516         case NAVIGATE_MENU:
517                 p_wrapper->ToggleTitle( 0 );
518                 break;
519         case TOGGLE_TITLE:
520             if ( playback_status > UNDEF_S )
521             {
522                 int32 index;
523                 if( p_message->FindInt32( "index", &index ) == B_OK )
524                     p_wrapper->ToggleTitle( index );
525             }
526             break;
527         case PREV_CHAPTER:
528         {
529             p_wrapper->PrevChapter();
530             break;
531         }
532         case NEXT_CHAPTER:
533         {
534             p_wrapper->NextChapter();
535             break;
536         }
537         case TOGGLE_CHAPTER:
538             if ( playback_status > UNDEF_S )
539             {
540                 int32 index;
541                 if( p_message->FindInt32( "index", &index ) == B_OK )
542                     p_wrapper->ToggleChapter( index );
543             }
544             break;
545         case PREV_FILE:
546             p_wrapper->PlaylistPrev();
547             break;
548         case NEXT_FILE:
549             p_wrapper->PlaylistNext();
550             break;
551         // general next/prev functionality (skips to whatever makes most sense)
552         case NAVIGATE_PREV:
553             p_wrapper->NavigatePrev();
554             break;
555         case NAVIGATE_NEXT:
556             p_wrapper->NavigateNext();
557             break;
558         // drag'n'drop and system messages
559         case MSG_SOUNDPLAY:
560                 // convert soundplay drag'n'drop message (containing paths)
561                 // to normal message (containing refs)
562                 {
563                         const char* path;
564                         for ( int32 i = 0; p_message->FindString( "path", i, &path ) == B_OK; i++ )
565                         {
566                                 entry_ref ref;
567                                 if ( get_ref_for_path( path, &ref ) == B_OK )
568                                         p_message->AddRef( "refs", &ref );
569                         }
570                 }
571                 // fall through
572         case B_REFS_RECEIVED:
573         case B_SIMPLE_DATA:
574             {
575                 /* file(s) opened by the File menu -> append to the playlist;
576                  * file(s) opened by drag & drop -> replace playlist;
577                  * file(s) opened by 'shift' + drag & drop -> append */
578                 bool replace = false;
579                 bool reverse = false;
580                 if ( p_message->WasDropped() )
581                 {
582                     replace = !( modifiers() & B_SHIFT_KEY );
583                     reverse = true;
584                 }
585                     
586                 // build list of files to be played from message contents
587                 entry_ref ref;
588                 BList files;
589                 
590                 // if we should parse sub-folders as well
591                         bool askedAlready = false;
592                         bool parseSubFolders = askedAlready;
593                         // traverse refs in reverse order
594                         int32 count;
595                         type_code dummy;
596                         if ( p_message->GetInfo( "refs", &dummy, &count ) == B_OK && count > 0 )
597                         {
598                                 int32 i = reverse ? count - 1 : 0;
599                                 int32 increment = reverse ? -1 : 1;
600                         for ( ; p_message->FindRef( "refs", i, &ref ) == B_OK; i += increment )
601                         {
602                             BPath path( &ref );
603                             if ( path.InitCheck() == B_OK )
604                             {
605                                 bool add = true;
606                                 // has the user dropped a folder?
607                                 BDirectory dir( &ref );
608                                 if ( dir.InitCheck() == B_OK)
609                                 {
610                                         // has the user dropped a dvd disk icon?
611                                                                 if ( dir.IsRootDirectory() )
612                                                                 {
613                                                                         BVolumeRoster volRoster;
614                                                                         BVolume vol;
615                                                                         BDirectory volumeRoot;
616                                                                         status_t status = volRoster.GetNextVolume( &vol );
617                                                                         while ( status == B_NO_ERROR )
618                                                                         {
619                                                                                 if ( vol.GetRootDirectory( &volumeRoot ) == B_OK
620                                                                                          && dir == volumeRoot )
621                                                                                 {
622                                                                                         BString volumeName;
623                                                                                         BString deviceName;
624                                                                                         bool isCDROM;
625                                                                                         if ( get_volume_info( vol, volumeName, isCDROM, deviceName )
626                                                                                                  && isCDROM )
627                                                                                         {
628                                                                                                 BMessage msg( OPEN_DVD );
629                                                                                                 msg.AddString( "device", deviceName.String() );
630                                                                                                 PostMessage( &msg );
631                                                                                                 add = false;
632                                                                                         }
633                                                                                         break;
634                                                                                 }
635                                                                                 else
636                                                                                 {
637                                                                                         vol.Unset();
638                                                                                         status = volRoster.GetNextVolume( &vol );
639                                                                                 }
640                                                                         }
641                                                                 }
642                                         if ( add )
643                                         {
644                                                 add = false;
645                                                 dir.Rewind();   // defensive programming
646                                                 BEntry entry;
647                                                                         collect_folder_contents( dir, files,
648                                                                                                                          parseSubFolders,
649                                                                                                                          askedAlready,
650                                                                                                                          entry );
651                                         }
652                                 }
653                                 if ( add )
654                                 {
655                                         BString* string = new BString( path.Path() );
656                                         if ( !files.AddItem( string, 0 ) )
657                                                 delete string;  // at least don't leak
658                                 }
659                             }
660                         }
661                         // give the list to VLC
662                         // BString objects allocated here will be deleted there
663                         int32 index;
664                         if ( p_message->FindInt32("drop index", &index) != B_OK )
665                                 index = -1;
666                         p_wrapper->OpenFiles( &files, replace, index );
667                         _UpdatePlaylist();
668                         }
669             }
670             break;
671
672         case OPEN_PREFERENCES:
673         {
674             if( fPreferencesWindow->Lock() )
675             {
676                 if (fPreferencesWindow->IsHidden())
677                     fPreferencesWindow->Show();
678                 else
679                     fPreferencesWindow->Activate();
680                 fPreferencesWindow->Unlock();
681             }
682             break;
683         }
684
685         case OPEN_MESSAGES:
686         {
687             if( fMessagesWindow->Lock() )
688             {
689                 if (fMessagesWindow->IsHidden())
690                     fMessagesWindow->Show();
691                 else
692                     fMessagesWindow->Activate();
693                 fMessagesWindow->Unlock();
694             }
695             break;
696         }
697         case MSG_UPDATE:
698                 UpdateInterface();
699                 break;
700         default:
701             BWindow::MessageReceived( p_message );
702             break;
703     }
704
705 }
706
707 /*****************************************************************************
708  * InterfaceWindow::QuitRequested
709  *****************************************************************************/
710 bool InterfaceWindow::QuitRequested()
711 {
712     p_wrapper->PlaylistStop();
713     p_mediaControl->SetStatus(NOT_STARTED_S, DEFAULT_RATE);
714
715         _StoreSettings();
716    
717     p_intf->b_die = 1;
718
719     return( true );
720 }
721
722 /*****************************************************************************
723  * InterfaceWindow::UpdateInterface
724  *****************************************************************************/
725 void InterfaceWindow::UpdateInterface()
726 {
727     if( p_wrapper->HasInput() )
728     {
729         if ( acquire_sem( p_mediaControl->fScrubSem ) == B_OK )
730         {
731             p_wrapper->SetTimeAsFloat( p_mediaControl->GetSeekTo() );
732         }
733         else if ( LockWithTimeout( INTERFACE_LOCKING_TIMEOUT ) == B_OK )
734         {
735             p_mediaControl->SetEnabled( true );
736             bool hasTitles = p_wrapper->HasTitles();
737             bool hasChapters = p_wrapper->HasChapters();
738             p_mediaControl->SetStatus( p_wrapper->InputStatus(), 
739                                        p_wrapper->InputRate() );
740             p_mediaControl->SetProgress( p_wrapper->GetTimeAsFloat() );
741             _SetMenusEnabled( true, hasChapters, hasTitles );
742
743             _UpdateSpeedMenu( p_wrapper->InputRate() );
744
745             // enable/disable skip buttons
746             bool canSkipPrev;
747             bool canSkipNext;
748             p_wrapper->GetNavCapabilities( &canSkipPrev, &canSkipNext );
749             p_mediaControl->SetSkippable( canSkipPrev, canSkipNext );
750
751             if ( p_wrapper->HasInput() )
752             {
753                 p_mediaControl->SetAudioEnabled( true );
754                 p_mediaControl->SetMuted( p_wrapper->IsMuted() );
755             } else
756                 p_mediaControl->SetAudioEnabled( false );
757
758             Unlock();
759         }
760         // update playlist as well
761         if ( fPlaylistWindow->LockWithTimeout( INTERFACE_LOCKING_TIMEOUT ) == B_OK )
762         {
763             fPlaylistWindow->UpdatePlaylist();
764             fPlaylistWindow->Unlock();
765         }
766     }
767     else
768     {
769                 if ( LockWithTimeout(INTERFACE_LOCKING_TIMEOUT) == B_OK )
770                 {
771                 _SetMenusEnabled( false );
772                 if( !( p_wrapper->PlaylistSize() > 0 ) )
773                     p_mediaControl->SetEnabled( false );
774                 else
775                 {
776                     p_mediaControl->SetProgress( 0 );
777                     // enable/disable skip buttons
778                     bool canSkipPrev;
779                     bool canSkipNext;
780                     p_wrapper->GetNavCapabilities( &canSkipPrev, &canSkipNext );
781                     p_mediaControl->SetSkippable( canSkipPrev, canSkipNext );
782                         }
783             Unlock();
784         }
785     }
786
787     fLastUpdateTime = system_time();
788 }
789
790 /*****************************************************************************
791  * InterfaceWindow::IsStopped
792  *****************************************************************************/
793 bool
794 InterfaceWindow::IsStopped() const
795 {
796     return (system_time() - fLastUpdateTime > INTERFACE_UPDATE_TIMEOUT);
797 }
798
799 /*****************************************************************************
800  * InterfaceWindow::_UpdatePlaylist
801  *****************************************************************************/
802 void
803 InterfaceWindow::_UpdatePlaylist()
804 {
805     if ( fPlaylistWindow->Lock() )
806     {
807         fPlaylistWindow->UpdatePlaylist( true );
808         fPlaylistWindow->Unlock();
809         p_mediaControl->SetEnabled( p_wrapper->PlaylistSize() );
810     }
811 }
812
813 /*****************************************************************************
814  * InterfaceWindow::_SetMenusEnabled
815  *****************************************************************************/
816 void
817 InterfaceWindow::_SetMenusEnabled(bool hasFile, bool hasChapters, bool hasTitles)
818 {
819     if (!hasFile)
820     {
821         hasChapters = false;
822         hasTitles = false;
823     }
824     if ( LockWithTimeout( INTERFACE_LOCKING_TIMEOUT ) == B_OK)
825     {
826         if ( fNextChapterMI->IsEnabled() != hasChapters )
827              fNextChapterMI->SetEnabled( hasChapters );
828         if ( fPrevChapterMI->IsEnabled() != hasChapters )
829              fPrevChapterMI->SetEnabled( hasChapters );
830         if ( fChapterMenu->IsEnabled() != hasChapters )
831              fChapterMenu->SetEnabled( hasChapters );
832         if ( fNextTitleMI->IsEnabled() != hasTitles )
833              fNextTitleMI->SetEnabled( hasTitles );
834         if ( fPrevTitleMI->IsEnabled() != hasTitles )
835              fPrevTitleMI->SetEnabled( hasTitles );
836         if ( fTitleMenu->IsEnabled() != hasTitles )
837              fTitleMenu->SetEnabled( hasTitles );
838         if ( fAudioMenu->IsEnabled() != hasFile )
839              fAudioMenu->SetEnabled( hasFile );
840         if ( fNavigationMenu->IsEnabled() != hasFile )
841              fNavigationMenu->SetEnabled( hasFile );
842         if ( fLanguageMenu->IsEnabled() != hasFile )
843              fLanguageMenu->SetEnabled( hasFile );
844         if ( fSubtitlesMenu->IsEnabled() != hasFile )
845              fSubtitlesMenu->SetEnabled( hasFile );
846         if ( fSpeedMenu->IsEnabled() != hasFile )
847              fSpeedMenu->SetEnabled( hasFile );
848         // "goto menu" menu item
849         bool hasMenu = config_GetInt( p_intf, "beos-dvdmenus" ) ?
850                            hasTitles : false;
851         if ( fGotoMenuMI->IsEnabled() != hasMenu )
852              fGotoMenuMI->SetEnabled( hasMenu );
853         Unlock();
854     }
855 }
856
857 /*****************************************************************************
858  * InterfaceWindow::_UpdateSpeedMenu
859  *****************************************************************************/
860 void
861 InterfaceWindow::_UpdateSpeedMenu( int rate )
862 {
863     if ( rate == DEFAULT_RATE )
864     {
865         if ( !fNormalMI->IsMarked() )
866             fNormalMI->SetMarked( true );
867     }
868     else if ( rate < DEFAULT_RATE )
869     {
870         if ( !fFasterMI->IsMarked() )
871             fFasterMI->SetMarked( true );
872     }
873     else
874     {
875         if ( !fSlowerMI->IsMarked() )
876             fSlowerMI->SetMarked( true );
877     }
878 }
879
880 /*****************************************************************************
881  * InterfaceWindow::_InputStreamChanged
882  *****************************************************************************/
883 void
884 InterfaceWindow::_InputStreamChanged()
885 {
886     // TODO: move more stuff from updateInterface() here!
887     snooze( 400000 );
888     p_wrapper->SetVolume( p_mediaControl->GetVolume() );
889 }
890
891 /*****************************************************************************
892  * InterfaceWindow::_ShowFilePanel
893  *****************************************************************************/
894 void
895 InterfaceWindow::_ShowFilePanel( uint32 command, const char* windowTitle )
896 {
897         if( !fFilePanel )
898         {
899                 fFilePanel = new BFilePanel( B_OPEN_PANEL, NULL, NULL,
900                                                                          B_FILE_NODE | B_DIRECTORY_NODE );
901                 fFilePanel->SetTarget( this );
902         }
903         fFilePanel->Window()->SetTitle( windowTitle );
904         BMessage message( command );
905         fFilePanel->SetMessage( &message );
906         if ( !fFilePanel->IsShowing() )
907         {
908                 fFilePanel->Refresh();
909                 fFilePanel->Show();
910         }
911 }
912
913 // set_window_pos
914 void
915 set_window_pos( BWindow* window, BRect frame )
916 {
917         // sanity checks: make sure window is not too big/small
918         // and that it's not off-screen
919         float minWidth, maxWidth, minHeight, maxHeight;
920         window->GetSizeLimits( &minWidth, &maxWidth, &minHeight, &maxHeight );
921
922         make_sure_frame_is_within_limits( frame,
923                                                                           minWidth, minHeight, maxWidth, maxHeight );
924         if ( make_sure_frame_is_on_screen( frame ) )
925         {
926                 window->MoveTo( frame.LeftTop() );
927                 window->ResizeTo( frame.Width(), frame.Height() );
928         }
929 }
930
931 // set_window_pos
932 void
933 launch_window( BWindow* window, bool showing )
934 {
935         if ( window->Lock() )
936         {
937                 if ( showing )
938                 {
939                         if ( window->IsHidden() )
940                                 window->Show();
941                 }
942                 else
943                 {
944                         if ( !window->IsHidden() )
945                                 window->Hide();
946                 }
947                 window->Unlock();
948         }
949 }
950
951 /*****************************************************************************
952  * InterfaceWindow::_RestoreSettings
953  *****************************************************************************/
954 void
955 InterfaceWindow::_RestoreSettings()
956 {
957         if ( load_settings( fSettings, "interface_settings", "VideoLAN Client" ) == B_OK )
958         {
959                 BRect frame;
960                 if ( fSettings->FindRect( "main frame", &frame ) == B_OK )
961                         set_window_pos( this, frame );
962                 if (fSettings->FindRect( "playlist frame", &frame ) == B_OK )
963                         set_window_pos( fPlaylistWindow, frame );
964                 if (fSettings->FindRect( "messages frame", &frame ) == B_OK )
965                         set_window_pos( fMessagesWindow, frame );
966                 if (fSettings->FindRect( "settings frame", &frame ) == B_OK )
967                 {
968                     /* FIXME: Preferences horizontal resizing doesn't work
969                        correctly now */
970                     frame.right = frame.left + fPreferencesWindow->Frame().Width();
971                         set_window_pos( fPreferencesWindow, frame );
972                 }
973                 
974                 bool showing;
975                 if ( fSettings->FindBool( "playlist showing", &showing ) == B_OK )
976                         launch_window( fPlaylistWindow, showing );
977                 if ( fSettings->FindBool( "messages showing", &showing ) == B_OK )
978                         launch_window( fMessagesWindow, showing );
979                 if ( fSettings->FindBool( "settings showing", &showing ) == B_OK )
980                         launch_window( fPreferencesWindow, showing );
981
982                 uint32 displayMode;
983                 if ( fSettings->FindInt32( "playlist display mode", (int32*)&displayMode ) == B_OK )
984                         fPlaylistWindow->SetDisplayMode( displayMode );
985         }
986 }
987
988 /*****************************************************************************
989  * InterfaceWindow::_StoreSettings
990  *****************************************************************************/
991 void
992 InterfaceWindow::_StoreSettings()
993 {
994         if ( fSettings->ReplaceRect( "main frame", Frame() ) != B_OK )
995                 fSettings->AddRect( "main frame", Frame() );
996         if ( fPlaylistWindow->Lock() )
997         {
998                 if (fSettings->ReplaceRect( "playlist frame", fPlaylistWindow->Frame() ) != B_OK)
999                         fSettings->AddRect( "playlist frame", fPlaylistWindow->Frame() );
1000                 if (fSettings->ReplaceBool( "playlist showing", !fPlaylistWindow->IsHidden() ) != B_OK)
1001                         fSettings->AddBool( "playlist showing", !fPlaylistWindow->IsHidden() );
1002                 fPlaylistWindow->Unlock();
1003         }
1004         if ( fMessagesWindow->Lock() )
1005         {
1006                 if (fSettings->ReplaceRect( "messages frame", fMessagesWindow->Frame() ) != B_OK)
1007                         fSettings->AddRect( "messages frame", fMessagesWindow->Frame() );
1008                 if (fSettings->ReplaceBool( "messages showing", !fMessagesWindow->IsHidden() ) != B_OK)
1009                         fSettings->AddBool( "messages showing", !fMessagesWindow->IsHidden() );
1010                 fMessagesWindow->Unlock();
1011         }
1012         if ( fPreferencesWindow->Lock() )
1013         {
1014                 if (fSettings->ReplaceRect( "settings frame", fPreferencesWindow->Frame() ) != B_OK)
1015                         fSettings->AddRect( "settings frame", fPreferencesWindow->Frame() );
1016                 if (fSettings->ReplaceBool( "settings showing", !fPreferencesWindow->IsHidden() ) != B_OK)
1017                         fSettings->AddBool( "settings showing", !fPreferencesWindow->IsHidden() );
1018                 fPreferencesWindow->Unlock();
1019         }
1020         uint32 displayMode = fPlaylistWindow->DisplayMode();
1021         if (fSettings->ReplaceInt32( "playlist display mode", displayMode ) != B_OK )
1022                 fSettings->AddInt32( "playlist display mode", displayMode );
1023
1024         save_settings( fSettings, "interface_settings", "VideoLAN Client" );
1025 }
1026
1027
1028 /*****************************************************************************
1029  * CDMenu::CDMenu
1030  *****************************************************************************/
1031 CDMenu::CDMenu(const char *name)
1032       : BMenu(name)
1033 {
1034 }
1035
1036 /*****************************************************************************
1037  * CDMenu::~CDMenu
1038  *****************************************************************************/
1039 CDMenu::~CDMenu()
1040 {
1041 }
1042
1043 /*****************************************************************************
1044  * CDMenu::AttachedToWindow
1045  *****************************************************************************/
1046 void CDMenu::AttachedToWindow(void)
1047 {
1048     // remove all items
1049     while ( BMenuItem* item = RemoveItem( 0L ) )
1050         delete item;
1051     GetCD( "/dev/disk" );
1052     BMenu::AttachedToWindow();
1053 }
1054
1055 /*****************************************************************************
1056  * CDMenu::GetCD
1057  *****************************************************************************/
1058 int CDMenu::GetCD( const char *directory )
1059 {
1060         BVolumeRoster volRoster;
1061         BVolume vol;
1062         BDirectory dir;
1063         status_t status = volRoster.GetNextVolume( &vol );
1064         while ( status ==  B_NO_ERROR )
1065         {
1066                 BString deviceName;
1067                 BString volumeName;
1068                 bool isCDROM;
1069                 if ( get_volume_info( vol, volumeName, isCDROM, deviceName )
1070                          && isCDROM )
1071                 {
1072                         BMessage* msg = new BMessage( OPEN_DVD );
1073                         msg->AddString( "device", deviceName.String() );
1074                         BMenuItem* item = new BMenuItem( volumeName.String(), msg );
1075                         AddItem( item );
1076                 }
1077                 vol.Unset();
1078                 status = volRoster.GetNextVolume( &vol );
1079         }
1080         return 0;
1081 }
1082
1083 /*****************************************************************************
1084  * LanguageMenu::LanguageMenu
1085  *****************************************************************************/
1086 LanguageMenu::LanguageMenu( const char *name, int menu_kind, 
1087                             VlcWrapper *p_wrapper )
1088     :BMenu(name)
1089 {
1090     kind = menu_kind;
1091     this->p_wrapper = p_wrapper;
1092 }
1093
1094 /*****************************************************************************
1095  * LanguageMenu::~LanguageMenu
1096  *****************************************************************************/
1097 LanguageMenu::~LanguageMenu()
1098 {
1099 }
1100
1101 /*****************************************************************************
1102  * LanguageMenu::AttachedToWindow
1103  *****************************************************************************/
1104 void LanguageMenu::AttachedToWindow()
1105 {
1106     // remove all items
1107     while ( BMenuItem* item = RemoveItem( 0L ) )
1108         delete item;
1109
1110     SetRadioMode( true );
1111         if ( BList *list = p_wrapper->GetChannels( kind ) )
1112         {
1113             for ( int32 i = 0; BMenuItem* item = (BMenuItem*)list->ItemAt( i ); i++ )
1114                 AddItem( item );
1115             
1116             if ( list->CountItems() > 1 )
1117                 AddItem( new BSeparatorItem(), 1 );
1118         }
1119     BMenu::AttachedToWindow();
1120 }
1121
1122 /*****************************************************************************
1123  * TitleMenu::TitleMenu
1124  *****************************************************************************/
1125 TitleMenu::TitleMenu( const char *name, intf_thread_t  *p_interface )
1126     : BMenu(name),
1127     p_intf( p_interface )
1128 {
1129 }
1130
1131 /*****************************************************************************
1132  * TitleMenu::~TitleMenu
1133  *****************************************************************************/
1134 TitleMenu::~TitleMenu()
1135 {
1136 }
1137
1138 /*****************************************************************************
1139  * TitleMenu::AttachedToWindow
1140  *****************************************************************************/
1141 void TitleMenu::AttachedToWindow()
1142 {
1143     while( BMenuItem* item = RemoveItem( 0L ) )
1144         delete item;
1145
1146     if ( BList *list = p_intf->p_sys->p_wrapper->GetTitles() )
1147         {    
1148                 for( int i = 0; BMenuItem* item = (BMenuItem*)list->ItemAt( i ); i++ )
1149                 AddItem( item );
1150         }
1151     BMenu::AttachedToWindow();
1152 }
1153
1154
1155 /*****************************************************************************
1156  * ChapterMenu::ChapterMenu
1157  *****************************************************************************/
1158 ChapterMenu::ChapterMenu( const char *name, intf_thread_t  *p_interface )
1159     : BMenu(name),
1160     p_intf( p_interface )
1161 {
1162 }
1163
1164 /*****************************************************************************
1165  * ChapterMenu::~ChapterMenu
1166  *****************************************************************************/
1167 ChapterMenu::~ChapterMenu()
1168 {
1169 }
1170
1171 /*****************************************************************************
1172  * ChapterMenu::AttachedToWindow
1173  *****************************************************************************/
1174 void ChapterMenu::AttachedToWindow()
1175 {
1176     while( BMenuItem* item = RemoveItem( 0L ) )
1177         delete item;
1178
1179     if ( BList* list = p_intf->p_sys->p_wrapper->GetChapters() )
1180         {    
1181             for( int i = 0; BMenuItem* item = (BMenuItem*)list->ItemAt( i ); i++ )
1182                 AddItem( item );
1183         }
1184     
1185     BMenu::AttachedToWindow();
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196 /*****************************************************************************
1197  * load_settings
1198  *****************************************************************************/
1199 status_t
1200 load_settings( BMessage* message, const char* fileName, const char* folder )
1201 {
1202         status_t ret = B_BAD_VALUE;
1203         if ( message )
1204         {
1205                 BPath path;
1206                 if ( ( ret = find_directory( B_USER_SETTINGS_DIRECTORY, &path ) ) == B_OK )
1207                 {
1208                         // passing folder is optional
1209                         if ( folder )
1210                                 ret = path.Append( folder );
1211                         if ( ret == B_OK && ( ret = path.Append( fileName ) ) == B_OK )
1212                         {
1213                                 BFile file( path.Path(), B_READ_ONLY );
1214                                 if ( ( ret = file.InitCheck() ) == B_OK )
1215                                 {
1216                                         ret = message->Unflatten( &file );
1217                                         file.Unset();
1218                                 }
1219                         }
1220                 }
1221         }
1222         return ret;
1223 }
1224
1225 /*****************************************************************************
1226  * save_settings
1227  *****************************************************************************/
1228 status_t
1229 save_settings( BMessage* message, const char* fileName, const char* folder )
1230 {
1231         status_t ret = B_BAD_VALUE;
1232         if ( message )
1233         {
1234                 BPath path;
1235                 if ( ( ret = find_directory( B_USER_SETTINGS_DIRECTORY, &path ) ) == B_OK )
1236                 {
1237                         // passing folder is optional
1238                         if ( folder && ( ret = path.Append( folder ) ) == B_OK )
1239                                 ret = create_directory( path.Path(), 0777 );
1240                         if ( ret == B_OK && ( ret = path.Append( fileName ) ) == B_OK )
1241                         {
1242                                 BFile file( path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE );
1243                                 if ( ( ret = file.InitCheck() ) == B_OK )
1244                                 {
1245                                         ret = message->Flatten( &file );
1246                                         file.Unset();
1247                                 }
1248                         }
1249                 }
1250         }
1251         return ret;
1252 }