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