7d9854fb94c41aa8affad6eb7ec88073689319dc
[vlc.git] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface plugin
3  *****************************************************************************
4  * Copyright (C) 2002-2003 VideoLAN
5  * $Id: intf.m,v 1.45 2003/02/05 01:36:00 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <thedj@users.sourceforge.net>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  * 
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
24  *****************************************************************************/
25
26 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29 #include <stdlib.h>                                      /* malloc(), free() */
30 #include <sys/param.h>                                    /* for MAXPATHLEN */
31 #include <string.h>
32
33 #include "intf.h"
34 #include "vout.h"
35 #include "prefs.h"
36 #include "playlist.h"
37
38 /*****************************************************************************
39  * Local prototypes.
40  *****************************************************************************/
41 static void Run ( intf_thread_t *p_intf );
42
43 /*****************************************************************************
44  * OpenIntf: initialize interface
45  *****************************************************************************/
46 int E_(OpenIntf) ( vlc_object_t *p_this )
47 {   
48     intf_thread_t *p_intf = (intf_thread_t*) p_this;
49
50     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
51     if( p_intf->p_sys == NULL )
52     {
53         return( 1 );
54     }
55
56     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
57
58     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
59     p_intf->p_sys->o_sendport = [[NSPort port] retain];
60
61     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
62
63     p_intf->pf_run = Run;
64
65     [[VLCApplication sharedApplication] autorelease];
66     [NSApp initIntlSupport];
67     [NSApp setIntf: p_intf];
68
69     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
70
71     return( 0 );
72 }
73
74 /*****************************************************************************
75  * CloseIntf: destroy interface
76  *****************************************************************************/
77 void E_(CloseIntf) ( vlc_object_t *p_this )
78 {
79     intf_thread_t *p_intf = (intf_thread_t*) p_this;
80
81     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
82
83     config_SaveConfigFile( p_intf, MODULE_STRING );
84
85     [p_intf->p_sys->o_sendport release];
86     [p_intf->p_sys->o_pool release];
87
88     free( p_intf->p_sys );
89 }
90
91 /*****************************************************************************
92  * Run: main loop
93  *****************************************************************************/
94 static void Run( intf_thread_t *p_intf )
95 {
96     /* Do it again - for some unknown reason, vlc_thread_create() often
97      * fails to go to real-time priority with the first launched thread
98      * (???) --Meuuh */
99     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
100
101     [NSApp run];
102 }
103
104 /*****************************************************************************
105  * VLCApplication implementation 
106  *****************************************************************************/
107 @implementation VLCApplication
108
109 - (id)init
110 {
111     /* default encoding: ISO-8859-1 */
112     i_encoding = NSISOLatin1StringEncoding;
113
114     return( [super init] );
115 }
116
117 - (void)initIntlSupport
118 {
119     char *psz_lang = getenv( "LANG" );
120
121     if( psz_lang == NULL )
122     {
123         return;
124     }
125
126     if( strncmp( psz_lang, "pl", 2 ) == 0 )
127     {
128         i_encoding = NSISOLatin2StringEncoding;
129     }
130     else if( strncmp( psz_lang, "ja", 2 ) == 0 ) 
131     {
132         i_encoding = NSJapaneseEUCStringEncoding;
133     }
134     else if( strncmp( psz_lang, "ru", 2 ) == 0 )
135     {
136 #define CFSENC2NSSENC(e) CFStringConvertEncodingToNSStringEncoding(e)
137         i_encoding = CFSENC2NSSENC( kCFStringEncodingKOI8_R ); 
138 #undef CFSENC2NSSENC
139     }
140 }
141
142 - (NSString *)localizedString:(char *)psz
143 {
144     NSString * o_str = nil;
145
146     if( psz != NULL )
147     {
148         UInt32 uiLength = (UInt32)strlen( psz );
149         NSData * o_data = [NSData dataWithBytes: psz length: uiLength];
150         o_str = [[[NSString alloc] initWithData: o_data
151                                        encoding: i_encoding] autorelease];
152     }
153
154     return( o_str );
155 }
156
157 - (void)setIntf:(intf_thread_t *)_p_intf
158 {
159     p_intf = _p_intf;
160 }
161
162 - (intf_thread_t *)getIntf
163 {
164     return( p_intf );
165 }
166
167 - (void)terminate:(id)sender
168 {
169     p_intf->p_vlc->b_die = VLC_TRUE;
170 }
171
172 @end
173
174 int ExecuteOnMainThread( id target, SEL sel, void * p_arg )
175 {
176     int i_ret = 0;
177
178     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
179
180     if( [target respondsToSelector: @selector(performSelectorOnMainThread:
181                                              withObject:waitUntilDone:)] )
182     {
183         [target performSelectorOnMainThread: sel
184                 withObject: [NSValue valueWithPointer: p_arg]
185                 waitUntilDone: YES];
186     }
187     else if( NSApp != nil && [NSApp respondsToSelector: @selector(getIntf)] ) 
188     {
189         NSValue * o_v1;
190         NSValue * o_v2;
191         NSArray * o_array;
192         NSPort * o_recv_port;
193         NSInvocation * o_inv;
194         NSPortMessage * o_msg;
195         intf_thread_t * p_intf;
196         NSConditionLock * o_lock;
197         NSMethodSignature * o_sig;
198
199         id * val[] = { &o_lock, &o_v2 };
200
201         p_intf = (intf_thread_t *)[NSApp getIntf];
202
203         o_recv_port = [[NSPort port] retain];
204         o_v1 = [NSValue valueWithPointer: val]; 
205         o_v2 = [NSValue valueWithPointer: p_arg];
206
207         o_sig = [target methodSignatureForSelector: sel];
208         o_inv = [NSInvocation invocationWithMethodSignature: o_sig];
209         [o_inv setArgument: &o_v1 atIndex: 2];
210         [o_inv setTarget: target];
211         [o_inv setSelector: sel];
212
213         o_array = [NSArray arrayWithObject:
214             [NSData dataWithBytes: &o_inv length: sizeof(o_inv)]];
215         o_msg = [[NSPortMessage alloc]
216             initWithSendPort: p_intf->p_sys->o_sendport
217             receivePort: o_recv_port components: o_array];
218
219         o_lock = [[NSConditionLock alloc] initWithCondition: 0];
220         [o_msg sendBeforeDate: [NSDate distantPast]];
221         [o_lock lockWhenCondition: 1];
222         [o_lock unlock];
223         [o_lock release];
224
225         [o_msg release];
226         [o_recv_port release];
227     } 
228     else
229     {
230         i_ret = 1;
231     }
232
233     [o_pool release];
234
235     return( i_ret );
236 }
237
238 /*****************************************************************************
239  * VLCMain implementation 
240  *****************************************************************************/
241 @implementation VLCMain
242
243 - (void)awakeFromNib
244 {
245     [o_window setTitle: _NS("VLC - Controller")];
246     [o_window setExcludedFromWindowsMenu: TRUE];
247
248     /* button controls */
249     [o_btn_playlist setToolTip: _NS("Playlist")];
250     [o_btn_prev setToolTip: _NS("Previous")];
251     [o_btn_slower setToolTip: _NS("Slower")];
252     [o_btn_play setToolTip: _NS("Play")];
253     [o_btn_stop setToolTip: _NS("Stop")];
254     [o_btn_faster setToolTip: _NS("Faster")];
255     [o_btn_next setToolTip: _NS("Next")];
256     [o_btn_prefs setToolTip: _NS("Preferences")];
257     [o_volumeslider setToolTip: _NS("Volume")];
258     [o_timeslider setToolTip: _NS("Position")];
259
260     /* messages panel */ 
261     [o_msgs_panel setDelegate: self];
262     [o_msgs_panel setTitle: _NS("Messages")];
263     [o_msgs_panel setExcludedFromWindowsMenu: TRUE];
264     [o_msgs_btn_ok setTitle: _NS("Close")];
265
266     /* main menu */
267     [o_mi_about setTitle: _NS("About VLC Media Player")];
268     [o_mi_prefs setTitle: _NS("Preferences")];
269     [o_mi_hide setTitle: _NS("Hide VLC")];
270     [o_mi_hide_others setTitle: _NS("Hide Others")];
271     [o_mi_show_all setTitle: _NS("Show All")];
272     [o_mi_quit setTitle: _NS("Quit VLC")];
273
274     [o_mu_file setTitle: _NS("File")];
275     [o_mi_open_generic setTitle: _NS("Open...")];
276     [o_mi_open_file setTitle: _NS("Open File...")];
277     [o_mi_open_disc setTitle: _NS("Open Disc...")];
278     [o_mi_open_net setTitle: _NS("Open Network...")];
279     [o_mi_open_recent setTitle: _NS("Open Recent")];
280     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
281
282     [o_mu_edit setTitle: _NS("Edit")];
283     [o_mi_cut setTitle: _NS("Cut")];
284     [o_mi_copy setTitle: _NS("Copy")];
285     [o_mi_paste setTitle: _NS("Paste")];
286     [o_mi_clear setTitle: _NS("Clear")];
287     [o_mi_select_all setTitle: _NS("Select All")];
288
289     [o_mu_controls setTitle: _NS("Controls")];
290     [o_mi_play setTitle: _NS("Play")];
291     [o_mi_stop setTitle: _NS("Stop")];
292     [o_mi_faster setTitle: _NS("Faster")];
293     [o_mi_slower setTitle: _NS("Slower")];
294     [o_mi_previous setTitle: _NS("Previous")];
295     [o_mi_next setTitle: _NS("Next")];
296     [o_mi_loop setTitle: _NS("Loop")];
297     [o_mi_program setTitle: _NS("Program")];
298     [o_mi_title setTitle: _NS("Title")];
299     [o_mi_chapter setTitle: _NS("Chapter")];
300     [o_mi_language setTitle: _NS("Language")];
301     [o_mi_subtitle setTitle: _NS("Subtitles")];
302     
303     [o_mu_audio setTitle: _NS("Audio")];
304     [o_mi_vol_up setTitle: _NS("Volume Up")];
305     [o_mi_vol_down setTitle: _NS("Volume Down")];
306     [o_mi_mute setTitle: _NS("Mute")];
307     [o_mi_channels setTitle: _NS("Channels")];
308     [o_mi_device setTitle: _NS("Device")];
309     
310     [o_mu_video setTitle: _NS("Video")];
311     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
312     [o_mi_screen setTitle: _NS("Screen")];
313     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
314
315     [o_mu_window setTitle: _NS("Window")];
316     [o_mi_minimize setTitle: _NS("Minimize Window")];
317     [o_mi_close_window setTitle: _NS("Close Window")];
318     [o_mi_controller setTitle: _NS("Controller")];
319     [o_mi_playlist setTitle: _NS("Playlist")];
320     [o_mi_messages setTitle: _NS("Messages")];
321
322     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
323
324     [o_mu_help setTitle: _NS("Help")];
325     [o_mi_readme setTitle: _NS("ReadMe...")];
326     [o_mi_reportabug setTitle: _NS("Report A Bug")];
327     [o_mi_website setTitle: _NS("VideoLAN Website")];
328     [o_mi_license setTitle: _NS("License")];
329
330     /* dock menu */
331     [o_dmi_play setTitle: _NS("Play")];
332     [o_dmi_stop setTitle: _NS("Stop")];
333     [o_dmi_next setTitle: _NS("Next")];
334     [o_dmi_previous setTitle: _NS("Previous")];
335
336     /* error panel */
337     [o_error setTitle: _NS("Error")];
338     [o_err_lbl setStringValue: _NS("An error has occurred which probably prevented the execution of your request:")];
339     [o_err_bug_lbl setStringValue: _NS("If you believe that it is a bug, please follow the instructions at:")]; 
340     [o_err_btn_msgs setTitle: _NS("Open Messages Window")];
341     [o_err_btn_dismiss setTitle: _NS("Dismiss")];
342
343     [self setSubmenusEnabled: FALSE];
344     [self manageVolumeSlider];
345 }
346
347 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
348 {
349     intf_thread_t * p_intf = [NSApp getIntf];
350
351     o_msg_lock = [[NSLock alloc] init];
352     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
353
354     o_img_play = [[NSImage imageNamed: @"play"] retain];
355     o_img_pause = [[NSImage imageNamed: @"pause"] retain];
356
357     [p_intf->p_sys->o_sendport setDelegate: self];
358     [[NSRunLoop currentRunLoop] 
359         addPort: p_intf->p_sys->o_sendport
360         forMode: NSDefaultRunLoopMode];
361
362     [NSTimer scheduledTimerWithTimeInterval: 0.1
363         target: self selector: @selector(manageIntf:)
364         userInfo: nil repeats: TRUE];
365
366     [NSThread detachNewThreadSelector: @selector(manage)
367         toTarget: self withObject: nil];
368 }
369
370 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
371 {
372     [o_playlist appendArray:
373         [NSArray arrayWithObject: o_filename] atPos: -1 enqueue: NO];
374
375     return( TRUE );
376 }
377
378 - (void)manage
379 {
380     NSDate * o_sleep_date;
381     intf_thread_t * p_intf = [NSApp getIntf];
382     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
383
384     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
385
386     while( !p_intf->b_die )
387     {
388         playlist_t * p_playlist;
389
390         vlc_mutex_lock( &p_intf->change_lock );
391
392         p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, 
393                                               FIND_ANYWHERE );
394
395         if( p_playlist != NULL )
396         {
397             vlc_mutex_lock( &p_playlist->object_lock );
398
399             [self manage: p_playlist];
400
401             vlc_mutex_unlock( &p_playlist->object_lock );
402             vlc_object_release( p_playlist );
403         }
404
405         vlc_mutex_unlock( &p_intf->change_lock );
406
407         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: 0.1];
408         [NSThread sleepUntilDate: o_sleep_date];
409     }
410
411     [self terminate];
412
413     [o_pool release];
414 }
415
416 - (void)manage:(playlist_t *)p_playlist
417 {
418     intf_thread_t * p_intf = [NSApp getIntf];
419
420 #define p_input p_playlist->p_input
421
422     if( p_input )
423     {
424         vout_thread_t   * p_vout  = NULL;
425         aout_instance_t * p_aout  = NULL; 
426         vlc_bool_t b_need_menus = VLC_FALSE;
427
428         vlc_mutex_lock( &p_input->stream.stream_lock );
429
430         if( !p_input->b_die )
431         {
432             /* New input or stream map change */
433             if( p_input->stream.b_changed )
434             {
435                 p_intf->p_sys->b_playing = 1;
436                 [self manageMode: p_playlist];
437                 b_need_menus = VLC_TRUE;
438             }
439
440             if( p_intf->p_sys->i_part !=
441                 p_input->stream.p_selected_area->i_part )
442             {
443                 p_intf->p_sys->b_chapter_update = 1;
444                 b_need_menus = VLC_TRUE;
445             }
446
447             p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
448                                               FIND_ANYWHERE );
449             if( p_aout != NULL )
450             {
451                 vlc_value_t val;
452                 audio_volume_t i_volume;
453
454                 if( var_Get( (vlc_object_t *)p_aout, "intf-change", &val )
455                     >= 0 && val.b_bool )
456                 {
457                     p_intf->p_sys->b_aout_update = 1;
458                     b_need_menus = VLC_TRUE;
459                 }
460
461                 aout_VolumeGet( p_aout, &i_volume );
462                 vlc_object_release( (vlc_object_t *)p_aout );
463
464                 p_intf->p_sys->b_mute = ( i_volume == 0 );
465             }
466
467             p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
468                                               FIND_ANYWHERE );
469             if( p_vout != NULL )
470             {
471                 vlc_value_t val;
472
473                 if( var_Get( (vlc_object_t *)p_vout, "intf-change", &val )
474                     >= 0 && val.b_bool )
475                 {
476                     p_intf->p_sys->b_vout_update = 1;
477                     b_need_menus = VLC_TRUE;
478                 }
479
480                 vlc_object_release( (vlc_object_t *)p_vout );
481             } 
482
483             if( b_need_menus )
484             {
485                 [self setupMenus: p_input];
486             }
487         }
488
489         vlc_mutex_unlock( &p_input->stream.stream_lock );
490     }
491     else if( p_intf->p_sys->b_playing && !p_intf->b_die )
492     {
493         p_intf->p_sys->b_playing = 0;
494         [self manageMode: p_playlist];
495     }
496
497 #undef p_input
498 }
499
500 - (void)manageMode:(playlist_t *)p_playlist
501 {
502     intf_thread_t * p_intf = [NSApp getIntf];
503
504     if( p_playlist->p_input != NULL )
505     {
506         /* get ready for menu regeneration */
507         p_intf->p_sys->b_program_update = 1;
508         p_intf->p_sys->b_title_update = 1;
509         p_intf->p_sys->b_chapter_update = 1;
510         p_intf->p_sys->b_audio_update = 1;
511         p_intf->p_sys->b_spu_update = 1;
512         p_intf->p_sys->i_part = 0;
513
514         p_playlist->p_input->stream.b_changed = 0;
515         msg_Dbg( p_intf, "stream has changed, refreshing interface" );
516     }
517     else
518     {
519         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
520                                                           FIND_ANYWHERE );
521         if( p_vout != NULL )
522         {
523             vlc_object_detach( p_vout );
524             vlc_object_release( p_vout );
525
526             vlc_mutex_unlock( &p_playlist->object_lock );
527             vout_Destroy( p_vout );
528             vlc_mutex_lock( &p_playlist->object_lock );
529         }
530
531         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
532                                                             FIND_ANYWHERE );
533         if( p_aout != NULL )
534         {
535             vlc_object_detach( (vlc_object_t *)p_aout );
536             vlc_object_release( (vlc_object_t *)p_aout );
537             aout_Delete( p_aout ); 
538         }
539     }
540
541     p_intf->p_sys->b_intf_update = VLC_TRUE;
542 }
543
544 - (void)manageIntf:(NSTimer *)o_timer
545 {
546     intf_thread_t * p_intf = [NSApp getIntf];
547
548     if( p_intf->p_vlc->b_die == VLC_TRUE )
549     {
550         [o_timer invalidate];
551         return;
552     }
553
554     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
555                                                        FIND_ANYWHERE );
556
557     if( p_playlist == NULL )
558     {
559         return;
560     }
561
562     vlc_mutex_lock( &p_playlist->object_lock );
563
564 #define p_input p_playlist->p_input
565
566     if( p_input != NULL )
567     {
568         vlc_mutex_lock( &p_input->stream.stream_lock );
569     }
570
571     if( p_intf->p_sys->b_intf_update )
572     {
573         vlc_bool_t b_input = VLC_FALSE;
574         vlc_bool_t b_plmul = VLC_FALSE;
575         vlc_bool_t b_control = VLC_FALSE;
576         vlc_bool_t b_seekable = VLC_FALSE;
577         vlc_bool_t b_chapters = VLC_FALSE;
578
579         b_plmul = p_playlist->i_size > 1;
580
581         if( ( b_input = ( p_input != NULL ) ) )
582         {
583             /* seekable streams */
584             b_seekable = p_input->stream.b_seekable;
585
586             /* control buttons for free pace streams */
587             b_control = p_input->stream.b_pace_control; 
588
589             /* chapters */
590             b_chapters = p_input->stream.p_selected_area->i_part_nb > 1; 
591
592             /* play status */
593             p_intf->p_sys->b_play_status = 
594                 p_input->stream.control.i_status != PAUSE_S;
595         }
596         else
597         {
598             /* play status */
599             p_intf->p_sys->b_play_status = VLC_FALSE;
600
601             [self setSubmenusEnabled: FALSE];
602         }
603
604         [self playStatusUpdated: p_intf->p_sys->b_play_status];
605
606         [o_btn_stop setEnabled: b_input];
607         [o_btn_faster setEnabled: b_control];
608         [o_btn_slower setEnabled: b_control];
609         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
610         [o_btn_next setEnabled: (b_plmul || b_chapters)];
611
612         [o_timeslider setFloatValue: 0.0];
613         [o_timeslider setEnabled: b_seekable];
614         [o_timefield setStringValue: @"0:00:00"];
615
616         [self manageVolumeSlider];
617
618         p_intf->p_sys->b_intf_update = VLC_FALSE;
619     }
620
621 #define p_area p_input->stream.p_selected_area
622
623     if( p_intf->p_sys->b_playing && p_input != NULL )
624     {
625         vlc_bool_t b_field_update = VLC_TRUE;
626
627         if( !p_input->b_die && ( p_intf->p_sys->b_play_status !=
628             ( p_input->stream.control.i_status != PAUSE_S ) ) ) 
629         {
630             p_intf->p_sys->b_play_status =
631                 !p_intf->p_sys->b_play_status;
632
633             [self playStatusUpdated: p_intf->p_sys->b_play_status]; 
634         }
635
636         if( p_input->stream.b_seekable )
637         {
638             if( f_slider == f_slider_old )
639             {
640                 float f_updated = ( 100. * p_area->i_tell ) /
641                                            p_area->i_size;
642
643                 if( f_slider != f_updated )
644                 {
645                     [o_timeslider setFloatValue: f_updated];
646                 }
647             }
648             else
649             {
650                 off_t i_seek = ( f_slider * p_area->i_size ) / 100;
651
652                 /* release the lock to be able to seek */
653                 vlc_mutex_unlock( &p_input->stream.stream_lock );
654                 input_Seek( p_input, i_seek, INPUT_SEEK_SET );
655                 vlc_mutex_lock( &p_input->stream.stream_lock );
656
657                 /* update the old value */
658                 f_slider_old = f_slider; 
659
660                 b_field_update = VLC_FALSE;
661             }
662         }
663
664         if( b_field_update )
665         {
666             NSString * o_time;
667             char psz_time[ OFFSETTOTIME_MAX_SIZE ];
668
669             input_OffsetToTime( p_input, psz_time, p_area->i_tell );
670
671             o_time = [NSString stringWithCString: psz_time];
672             [o_timefield setStringValue: o_time];
673         }
674
675         /* disable screen saver */
676         UpdateSystemActivity( UsrActivity );
677     }
678
679 #undef p_area
680
681     if( p_input != NULL )
682     {
683         vlc_mutex_unlock( &p_input->stream.stream_lock );
684     }
685
686 #undef p_input
687
688     vlc_mutex_unlock( &p_playlist->object_lock );
689     vlc_object_release( p_playlist );
690
691     [self updateMessageArray];
692 }
693
694 - (void)updateMessageArray
695 {
696     int i_start, i_stop;
697     intf_thread_t * p_intf = [NSApp getIntf];
698
699     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
700     i_stop = *p_intf->p_sys->p_sub->pi_stop;
701     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
702
703     if( p_intf->p_sys->p_sub->i_start != i_stop )
704     {
705         NSColor *o_white = [NSColor whiteColor];
706         NSColor *o_red = [NSColor redColor];
707         NSColor *o_yellow = [NSColor yellowColor];
708         NSColor *o_gray = [NSColor grayColor];
709
710         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
711         static const char * ppsz_type[4] = { ": ", " error: ",
712                                              " warning: ", " debug: " };
713
714         for( i_start = p_intf->p_sys->p_sub->i_start;
715              i_start != i_stop;
716              i_start = (i_start+1) % VLC_MSG_QSIZE )
717         {
718             NSString *o_msg;
719             NSDictionary *o_attr;
720             NSAttributedString *o_msg_color;
721
722             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
723
724             [o_msg_lock lock];
725
726             if( [o_msg_arr count] + 2 > 200 )
727             {
728                 unsigned rid[] = { 0, 1 };
729                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
730                            numIndices: sizeof(rid)/sizeof(rid[0])];
731             }
732
733             o_attr = [NSDictionary dictionaryWithObject: o_gray
734                 forKey: NSForegroundColorAttributeName];
735             o_msg = [NSString stringWithFormat: @"%s%s",
736                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
737                 ppsz_type[i_type]];
738             o_msg_color = [[NSAttributedString alloc]
739                 initWithString: o_msg attributes: o_attr];
740             [o_msg_arr addObject: [o_msg_color autorelease]];
741
742             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
743                 forKey: NSForegroundColorAttributeName];
744             o_msg = [NSString stringWithFormat: @"%s\n",
745                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
746             o_msg_color = [[NSAttributedString alloc]
747                 initWithString: o_msg attributes: o_attr];
748             [o_msg_arr addObject: [o_msg_color autorelease]];
749
750             [o_msg_lock unlock];
751
752             if( i_type == 1 )
753             {
754                 NSString *o_my_msg = [NSString stringWithFormat: @"%s: %s\n",
755                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
756                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
757
758                 NSRange s_r = NSMakeRange( [[o_err_msg string] length], 0 );
759                 [o_err_msg setEditable: YES];
760                 [o_err_msg setSelectedRange: s_r];
761                 [o_err_msg insertText: o_my_msg];
762
763                 [o_error makeKeyAndOrderFront: self];
764                 [o_err_msg setEditable: NO];
765             }
766         }
767
768         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
769         p_intf->p_sys->p_sub->i_start = i_start;
770         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
771     }
772 }
773
774 - (void)playStatusUpdated:(BOOL)b_pause
775 {
776     if( b_pause )
777     {
778         [o_btn_play setImage: o_img_pause];
779         [o_btn_play setToolTip: _NS("Pause")];
780         [o_mi_play setTitle: _NS("Pause")];
781         [o_dmi_play setTitle: _NS("Pause")];
782     }
783     else
784     {
785         [o_btn_play setImage: o_img_play];
786         [o_btn_play setToolTip: _NS("Play")];
787         [o_mi_play setTitle: _NS("Play")];
788         [o_dmi_play setTitle: _NS("Play")];
789     }
790 }
791
792 - (void)setSubmenusEnabled:(BOOL)b_enabled
793 {
794     [o_mi_program setEnabled: b_enabled];
795     [o_mi_title setEnabled: b_enabled];
796     [o_mi_chapter setEnabled: b_enabled];
797     [o_mi_language setEnabled: b_enabled];
798     [o_mi_subtitle setEnabled: b_enabled];
799     [o_mi_channels setEnabled: b_enabled];
800     [o_mi_device setEnabled: b_enabled];
801     [o_mi_screen setEnabled: b_enabled];
802 }
803
804 - (void)manageVolumeSlider
805 {
806     audio_volume_t i_volume;
807     vlc_bool_t b_audio = VLC_FALSE;
808     intf_thread_t * p_intf = [NSApp getIntf];
809
810     aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT, 
811                                                         FIND_ANYWHERE );
812     if( p_aout != NULL )
813     {
814         b_audio = VLC_TRUE;
815
816         aout_VolumeGet( p_aout, &i_volume );
817         vlc_object_release( (vlc_object_t *)p_aout );
818     }
819     else
820     {
821         i_volume = (audio_volume_t)config_GetInt( p_intf, "volume" );
822     }
823
824     [o_volumeslider setFloatValue: (float)i_volume / AOUT_VOLUME_STEP]; 
825     [o_volumeslider setEnabled: b_audio];
826
827     p_intf->p_sys->b_mute = ( i_volume == 0 );
828 }
829
830 - (void)terminate
831 {
832     NSEvent * o_event;
833     vout_thread_t * p_vout;
834     playlist_t * p_playlist;
835     intf_thread_t * p_intf = [NSApp getIntf];
836
837     /*
838      * Free playlists
839      */
840     msg_Dbg( p_intf, "removing all playlists" );
841     while( (p_playlist = vlc_object_find( p_intf->p_vlc, VLC_OBJECT_PLAYLIST,
842                                           FIND_CHILD )) )
843     {
844         vlc_object_detach( p_playlist );
845         vlc_object_release( p_playlist );
846         playlist_Destroy( p_playlist );
847     }
848
849     /*
850      * Free video outputs
851      */
852     msg_Dbg( p_intf, "removing all video outputs" );
853     while( (p_vout = vlc_object_find( p_intf->p_vlc, 
854                                       VLC_OBJECT_VOUT, FIND_CHILD )) )
855     {
856         vlc_object_detach( p_vout );
857         vlc_object_release( p_vout );
858         vout_Destroy( p_vout );
859     }
860
861     if( o_img_pause != nil )
862     {
863         [o_img_pause release];
864         o_img_pause = nil;
865     }
866
867     if( o_img_play != nil )
868     {
869         [o_img_play release];
870         o_img_play = nil;
871     }
872
873     if( o_msg_arr != nil )
874     {
875         [o_msg_arr removeAllObjects];
876         [o_msg_arr release];
877         o_msg_arr = nil;
878     }
879
880     if( o_msg_lock != nil )
881     {
882         [o_msg_lock release];
883         o_msg_lock = nil;
884     }
885
886     if( o_prefs != nil )
887     {
888         [o_prefs release];
889         o_prefs = nil;
890     }
891
892     [NSApp stop: nil];
893
894     /* write cached user defaults to disk */
895     [[NSUserDefaults standardUserDefaults] synchronize];
896
897     /* send a dummy event to break out of the event loop */
898     o_event = [NSEvent mouseEventWithType: NSLeftMouseDown
899                 location: NSMakePoint( 1, 1 ) modifierFlags: 0
900                 timestamp: 1 windowNumber: [[NSApp mainWindow] windowNumber]
901                 context: [NSGraphicsContext currentContext] eventNumber: 1
902                 clickCount: 1 pressure: 0.0];
903     [NSApp postEvent: o_event atStart: YES];
904 }
905
906 - (void)setupMenus:(input_thread_t *)p_input
907 {
908     unsigned int i, i_nb_items;
909     NSMenuItem * o_item;
910     NSString * o_menu_title;
911     char psz_title[ 256 ];
912
913     es_descriptor_t * p_audio_es = NULL;
914     es_descriptor_t * p_spu_es = NULL;
915
916     intf_thread_t * p_intf = [NSApp getIntf];
917
918     p_intf->p_sys->b_chapter_update |= p_intf->p_sys->b_title_update;
919     p_intf->p_sys->b_audio_update |= p_intf->p_sys->b_title_update |
920                                      p_intf->p_sys->b_program_update;
921     p_intf->p_sys->b_spu_update |= p_intf->p_sys->b_title_update |
922                                    p_intf->p_sys->b_program_update;
923
924     if( p_intf->p_sys->b_program_update )
925     {
926         NSMenu * o_program;
927         SEL pf_toggle_program;
928         pgrm_descriptor_t * p_pgrm;
929
930         if( p_input->stream.p_new_program )
931         {
932             p_pgrm = p_input->stream.p_new_program;
933         }
934         else
935         {
936             p_pgrm = p_input->stream.p_selected_program;
937         }
938
939         o_program = [o_mi_program submenu];
940         pf_toggle_program = @selector(toggleProgram:);
941
942         /* remove previous program items */
943         i_nb_items = [o_program numberOfItems];
944         for( i = 0; i < i_nb_items; i++ )
945         {
946             [o_program removeItemAtIndex: 0];
947         }
948
949         /* make (un)sensitive */
950         [o_mi_program setEnabled: 
951             p_input->stream.i_pgrm_number > 1];
952
953         /* add program items */
954         for( i = 0 ; i < p_input->stream.i_pgrm_number ; i++ )
955         {
956             snprintf( psz_title, sizeof(psz_title), "id %d",
957                 p_input->stream.pp_programs[i]->i_number );
958             psz_title[sizeof(psz_title) - 1] = '\0';
959
960             o_menu_title = [NSApp localizedString: psz_title];
961
962             o_item = [o_program addItemWithTitle: o_menu_title
963                 action: pf_toggle_program keyEquivalent: @""];
964             [o_item setTag: p_input->stream.pp_programs[i]->i_number];
965             [o_item setTarget: o_controls];
966
967             if( p_pgrm == p_input->stream.pp_programs[i] )
968             {
969                 [o_item setState: NSOnState];
970             }
971         }
972
973         p_intf->p_sys->b_program_update = 0;
974     }
975
976     if( p_intf->p_sys->b_title_update )
977     {
978         NSMenu * o_title;
979         SEL pf_toggle_title;
980
981         o_title = [o_mi_title submenu];
982         pf_toggle_title = @selector(toggleTitle:);
983
984         /* remove previous title items */
985         i_nb_items = [o_title numberOfItems];
986         for( i = 0; i < i_nb_items; i++ )
987         {
988             [o_title removeItemAtIndex: 0];
989         }
990
991         /* make (un)sensitive */
992         [o_mi_title setEnabled: 
993             p_input->stream.i_area_nb > 1];
994
995         /* add title items */
996         for( i = 1 ; i < p_input->stream.i_area_nb ; i++ )
997         {
998             snprintf( psz_title, sizeof(psz_title), "Title %d (%d)", i,
999                 p_input->stream.pp_areas[i]->i_part_nb );
1000             psz_title[sizeof(psz_title) - 1] = '\0';
1001
1002             o_menu_title = [NSApp localizedString: psz_title];
1003
1004             o_item = [o_title addItemWithTitle: o_menu_title
1005                 action: pf_toggle_title keyEquivalent: @""];
1006             [o_item setTag: i];
1007             [o_item setTarget: o_controls];
1008
1009             if( ( p_input->stream.pp_areas[i] ==
1010                 p_input->stream.p_selected_area ) )
1011             {
1012                 [o_item setState: NSOnState];
1013             }
1014         }
1015
1016         p_intf->p_sys->b_title_update = 0;
1017     }
1018
1019     if( p_intf->p_sys->b_chapter_update )
1020     {
1021         NSMenu * o_chapter;
1022         SEL pf_toggle_chapter;
1023
1024         o_chapter = [o_mi_chapter submenu];
1025         pf_toggle_chapter = @selector(toggleChapter:);
1026
1027         /* remove previous chapter items */
1028         i_nb_items = [o_chapter numberOfItems];
1029         for( i = 0; i < i_nb_items; i++ )
1030         {
1031             [o_chapter removeItemAtIndex: 0];
1032         }
1033
1034         /* make (un)sensitive */
1035         [o_mi_chapter setEnabled: 
1036             p_input->stream.p_selected_area->i_part_nb > 1];
1037
1038         /* add chapter items */
1039         for( i = 0 ; i < p_input->stream.p_selected_area->i_part_nb ; i++ )
1040         {
1041             snprintf( psz_title, sizeof(psz_title), "Chapter %d", i + 1 );
1042             psz_title[sizeof(psz_title) - 1] = '\0';
1043
1044             o_menu_title = [NSApp localizedString: psz_title];
1045
1046             o_item = [o_chapter addItemWithTitle: o_menu_title
1047                 action: pf_toggle_chapter keyEquivalent: @""];
1048             [o_item setTag: i + 1];
1049             [o_item setTarget: o_controls];
1050
1051             if( ( p_input->stream.p_selected_area->i_part == i + 1 ) )
1052             {
1053                 [o_item setState: NSOnState];
1054             }
1055         }
1056
1057         p_intf->p_sys->i_part =
1058                 p_input->stream.p_selected_area->i_part;
1059
1060         p_intf->p_sys->b_chapter_update = 0;
1061     }
1062
1063     for( i = 0 ; i < p_input->stream.i_selected_es_number ; i++ )
1064     {
1065         if( p_input->stream.pp_selected_es[i]->i_cat == AUDIO_ES )
1066         {
1067             p_audio_es = p_input->stream.pp_selected_es[i];
1068         }
1069         else if( p_input->stream.pp_selected_es[i]->i_cat == SPU_ES )
1070         {
1071             p_spu_es = p_input->stream.pp_selected_es[i];
1072         }
1073     }
1074
1075     if( p_intf->p_sys->b_audio_update )
1076     {
1077         [self setupLangMenu: p_input mi: o_mi_language es: p_audio_es
1078             category: AUDIO_ES selector: @selector(toggleLanguage:)];
1079
1080         p_intf->p_sys->b_audio_update = 0;
1081     }
1082
1083     if( p_intf->p_sys->b_spu_update )
1084     {
1085         [self setupLangMenu: p_input mi: o_mi_subtitle es: p_spu_es
1086             category: SPU_ES selector: @selector(toggleLanguage:)];
1087
1088         p_intf->p_sys->b_spu_update = 0;
1089     }
1090
1091     if ( p_intf->p_sys->b_aout_update )
1092     {
1093         aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
1094                                                     FIND_ANYWHERE );
1095
1096         if ( p_aout != NULL )
1097         {
1098             vlc_value_t val;
1099             val.b_bool = 0;
1100
1101             var_Set( (vlc_object_t *)p_aout, "intf-change", val );
1102
1103             [self setupVarMenu: o_mi_channels target: (vlc_object_t *)p_aout
1104                 var: "audio-channels" selector: @selector(toggleVar:)];
1105
1106             [self setupVarMenu: o_mi_device target: (vlc_object_t *)p_aout
1107                 var: "audio-device" selector: @selector(toggleVar:)];
1108
1109             vlc_object_release( (vlc_object_t *)p_aout );
1110         }
1111
1112         p_intf->p_sys->b_aout_update = 0;
1113     }
1114
1115     if( p_intf->p_sys->b_vout_update )
1116     {
1117         vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1118                                                           FIND_ANYWHERE );
1119
1120         if ( p_vout != NULL )
1121         {
1122             vlc_value_t val;
1123             val.b_bool = 0;
1124
1125             var_Set( (vlc_object_t *)p_vout, "intf-change", val );
1126
1127             [self setupVarMenu: o_mi_screen target: (vlc_object_t *)p_vout
1128                 var: "video-device" selector: @selector(toggleVar:)];
1129
1130             vlc_object_release( (vlc_object_t *)p_vout );
1131
1132             [o_mi_close_window setEnabled: TRUE];
1133         }
1134
1135         p_intf->p_sys->b_vout_update = 0;
1136     }
1137
1138 #undef p_input
1139 }
1140
1141 - (void)setupLangMenu:(input_thread_t *)p_input
1142                       mi:(NSMenuItem *)o_mi
1143                       es:(es_descriptor_t *)p_es
1144                       category:(int)i_cat
1145                       selector:(SEL)pf_callback
1146 {
1147     unsigned int i, i_nb_items;
1148     NSMenu * o_menu = [o_mi submenu];
1149
1150     /* remove previous language items */
1151     i_nb_items = [o_menu numberOfItems];
1152     for( i = 0; i < i_nb_items; i++ )
1153     {
1154         [o_menu removeItemAtIndex: 0];
1155     }
1156
1157     /* make sensitive : we can't change it after we build the menu, and
1158      * before, we don't yet how many items we will have. So make it
1159      * always sensitive. --Meuuh */
1160     [o_mi setEnabled: TRUE];
1161
1162 #if 0
1163     /* We do not use this code, because you need to start stop .avi for
1164      * it to work, so not very useful now  --hartman */
1165     if ( o_mi == o_mi_subtitle ) {
1166         NSLog(@"testing");
1167         [o_mi setEnabled: TRUE ];
1168         NSMenuItem * o_lmi;
1169         NSString * o_title;
1170         o_title = _NS("Load from file..");
1171         o_lmi = [o_menu addItemWithTitle: o_title
1172                  action: pf_callback keyEquivalent: @""];
1173         [o_lmi setTag: 2000];
1174         [o_lmi setTarget: o_controls];
1175     }
1176 #endif
1177
1178 #define ES p_input->stream.pp_es[i]
1179     for( i = 0 ; i < p_input->stream.i_es_number ; i++ )
1180     {
1181         if( ( ES->i_cat == i_cat ) &&
1182             ( !ES->p_pgrm ||
1183               ES->p_pgrm ==
1184                  p_input->stream.p_selected_program ) )
1185         {
1186             NSMenuItem * o_lmi;
1187             NSString * o_title;
1188
1189             if( *ES->psz_desc )
1190             {
1191                 o_title = [NSApp localizedString: ES->psz_desc];
1192             }
1193             else
1194             {
1195                 char psz_title[ 256 ];
1196
1197                 snprintf( psz_title, sizeof(psz_title), _("Language 0x%x"),
1198                           ES->i_id );
1199                 psz_title[sizeof(psz_title) - 1] = '\0';
1200
1201                 o_title = [NSApp localizedString: psz_title];
1202             }
1203
1204             o_lmi = [o_menu addItemWithTitle: o_title
1205                 action: pf_callback keyEquivalent: @""];
1206             [o_lmi setRepresentedObject: 
1207                 [NSValue valueWithPointer: ES]];
1208             [o_lmi setTarget: o_controls];
1209             [o_lmi setTag: i_cat];
1210
1211             if( /*p_es == ES*/ ES->p_decoder_fifo != NULL )
1212             {
1213                 [o_lmi setState: NSOnState];
1214             }
1215         }
1216     }
1217 #undef ES
1218 }
1219
1220 - (void)setupVarMenu:(NSMenuItem *)o_mi
1221                      target:(vlc_object_t *)p_object
1222                      var:(const char *)psz_variable
1223                      selector:(SEL)pf_callback
1224 {
1225     int i, i_nb_items;
1226     NSMenu * o_menu = [o_mi submenu];
1227     vlc_value_t val;
1228     char * psz_value;
1229
1230     /* remove previous items */
1231     i_nb_items = [o_menu numberOfItems];
1232     for( i = 0; i < i_nb_items; i++ )
1233     {
1234         [o_menu removeItemAtIndex: 0];
1235     }
1236
1237     if ( var_Get( p_object, psz_variable, &val ) < 0 )
1238     {
1239         return;
1240     }
1241     psz_value = val.psz_string;
1242
1243     if ( var_Change( p_object, psz_variable,
1244                      VLC_VAR_GETLIST, &val ) < 0 )
1245     {
1246         free( psz_value );
1247         return;
1248     }
1249
1250     /* make (un)sensitive */
1251     [o_mi setEnabled: ( val.p_list->i_count > 0 )];
1252
1253     for ( i = 0; i < val.p_list->i_count; i++ )
1254     {
1255         NSMenuItem * o_lmi;
1256         NSString * o_title;
1257
1258         //o_title = [NSApp localizedString: val.p_list->p_values[i].psz_string];
1259         o_title = [NSString stringWithCString: val.p_list->p_values[i].psz_string];
1260         o_lmi = [o_menu addItemWithTitle: o_title
1261                  action: pf_callback keyEquivalent: @""];
1262         /* FIXME: this isn't 64-bit clean ! */
1263         [o_lmi setTag: (int)psz_variable];
1264         [o_lmi setRepresentedObject:
1265             [NSValue valueWithPointer: p_object]];
1266         [o_lmi setTarget: o_controls];
1267
1268         if ( !strcmp( psz_value, val.p_list->p_values[i].psz_string ) )
1269             [o_lmi setState: NSOnState];
1270     }
1271
1272     var_Change( p_object, psz_variable, VLC_VAR_FREELIST,
1273                 &val );
1274
1275     free( psz_value );
1276 }
1277
1278 - (IBAction)clearRecentItems:(id)sender
1279 {
1280     [[NSDocumentController sharedDocumentController]
1281                           clearRecentDocuments: nil];
1282 }
1283
1284 - (void)openRecentItem:(id)sender
1285 {
1286     [self application: nil openFile: [sender title]]; 
1287 }
1288
1289 - (IBAction)viewPreferences:(id)sender
1290 {
1291     if( o_prefs == nil )
1292     {
1293         o_prefs = [[VLCPrefs alloc] init];
1294     }
1295
1296     [o_prefs createPrefPanel: @"main"];
1297 }
1298
1299 - (IBAction)timesliderUpdate:(id)sender
1300 {
1301     float f_updated;
1302
1303     switch( [[NSApp currentEvent] type] )
1304     {
1305         case NSLeftMouseUp:
1306         case NSLeftMouseDown:
1307             f_slider = [sender floatValue];
1308             return;
1309
1310         case NSLeftMouseDragged:
1311             f_updated = [sender floatValue];
1312             break;
1313
1314         default:
1315             return;
1316     }
1317
1318     intf_thread_t * p_intf = [NSApp getIntf];
1319
1320     playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1321                                                        FIND_ANYWHERE );
1322
1323     if( p_playlist == NULL )
1324     {
1325         return;
1326     }
1327
1328     vlc_mutex_lock( &p_playlist->object_lock );
1329
1330     if( p_playlist->p_input != NULL )
1331     {
1332         off_t i_tell;
1333         NSString * o_time;
1334         char psz_time[ OFFSETTOTIME_MAX_SIZE ];
1335
1336 #define p_area p_playlist->p_input->stream.p_selected_area
1337         vlc_mutex_lock( &p_playlist->p_input->stream.stream_lock );
1338         i_tell = f_updated / 100. * p_area->i_size;
1339         input_OffsetToTime( p_playlist->p_input, psz_time, i_tell );
1340         vlc_mutex_unlock( &p_playlist->p_input->stream.stream_lock );
1341 #undef p_area
1342
1343         o_time = [NSString stringWithCString: psz_time];
1344         [o_timefield setStringValue: o_time]; 
1345     }
1346
1347     vlc_mutex_unlock( &p_playlist->object_lock );
1348     vlc_object_release( p_playlist );
1349 }
1350
1351 - (IBAction)closeError:(id)sender
1352 {
1353     [o_err_msg setString: @""];
1354     [o_error performClose: self];
1355 }
1356
1357 - (IBAction)openReadMe:(id)sender
1358 {
1359     NSString * o_path = [[NSBundle mainBundle] 
1360         pathForResource: @"README.MacOSX" ofType: @"rtf"]; 
1361
1362     [[NSWorkspace sharedWorkspace] openFile: o_path 
1363                                    withApplication: @"TextEdit"];
1364 }
1365
1366 - (IBAction)reportABug:(id)sender
1367 {
1368     NSURL * o_url = [NSURL URLWithString: 
1369         @"http://www.videolan.org/support/bug-reporting.html"];
1370
1371     [[NSWorkspace sharedWorkspace] openURL: o_url];
1372 }
1373
1374 - (IBAction)openWebsite:(id)sender
1375 {
1376     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org"];
1377
1378     [[NSWorkspace sharedWorkspace] openURL: o_url];
1379 }
1380
1381 - (IBAction)openLicense:(id)sender
1382 {
1383     NSString * o_path = [[NSBundle mainBundle] 
1384         pathForResource: @"COPYING" ofType: nil];
1385
1386     [[NSWorkspace sharedWorkspace] openFile: o_path 
1387                                    withApplication: @"TextEdit"];
1388 }
1389
1390 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1391 {
1392     if( [o_notification object] == o_msgs_panel )
1393     {
1394         id o_msg;
1395         NSEnumerator * o_enum;
1396
1397         [o_messages setString: @""]; 
1398
1399         [o_msg_lock lock];
1400
1401         o_enum = [o_msg_arr objectEnumerator];
1402
1403         while( ( o_msg = [o_enum nextObject] ) != nil )
1404         {
1405             [o_messages insertText: o_msg];
1406         }
1407
1408         [o_msg_lock unlock];
1409     }
1410 }
1411
1412 @end
1413
1414 @implementation VLCMain (NSMenuValidation)
1415
1416 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1417 {
1418     BOOL bEnabled = TRUE;
1419
1420     /* Recent Items Menu */
1421
1422     if( [[o_mi title] isEqualToString: _NS("Clear Menu")] )
1423     {
1424         NSMenu * o_menu = [o_mi_open_recent submenu];
1425         int i_nb_items = [o_menu numberOfItems];
1426         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1427                                                        recentDocumentURLs];
1428         UInt32 i_nb_docs = [o_docs count];
1429
1430         if( i_nb_items > 1 )
1431         {
1432             while( --i_nb_items )
1433             {
1434                 [o_menu removeItemAtIndex: 0];
1435             }
1436         }
1437
1438         if( i_nb_docs > 0 )
1439         {
1440             NSURL * o_url;
1441             NSString * o_doc;
1442
1443             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1444
1445             while( TRUE )
1446             {
1447                 i_nb_docs--;
1448
1449                 o_url = [o_docs objectAtIndex: i_nb_docs];
1450
1451                 if( [o_url isFileURL] )
1452                 {
1453                     o_doc = [o_url path];
1454                 }
1455                 else
1456                 {
1457                     o_doc = [o_url absoluteString];
1458                 }
1459
1460                 [o_menu insertItemWithTitle: o_doc
1461                     action: @selector(openRecentItem:)
1462                     keyEquivalent: @"" atIndex: 0]; 
1463
1464                 if( i_nb_docs == 0 )
1465                 {
1466                     break;
1467                 }
1468             } 
1469         }
1470         else
1471         {
1472             bEnabled = FALSE;
1473         }
1474     }
1475
1476     return( bEnabled );
1477 }
1478
1479 @end
1480
1481 @implementation VLCMain (Internal)
1482
1483 - (void)handlePortMessage:(NSPortMessage *)o_msg
1484 {
1485     id ** val;
1486     NSData * o_data;
1487     NSValue * o_value;
1488     NSInvocation * o_inv;
1489     NSConditionLock * o_lock;
1490  
1491     o_data = [[o_msg components] lastObject];
1492     o_inv = *((NSInvocation **)[o_data bytes]); 
1493     [o_inv getArgument: &o_value atIndex: 2];
1494     val = (id **)[o_value pointerValue];
1495     [o_inv setArgument: val[1] atIndex: 2];
1496     o_lock = *(val[0]);
1497
1498     [o_lock lock];
1499     [o_inv invoke];
1500     [o_lock unlockWithCondition: 1];
1501 }
1502
1503 @end