]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* Fixed hotkey/OSD behaviour for OS X. Also prevents code duplicity.
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <hartman at videolan.org>
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 #include <vlc_keys.h>
33
34 #include "intf.h"
35 #include "vout.h"
36 #include "prefs.h"
37 #include "playlist.h"
38 #include "controls.h"
39
40 /*****************************************************************************
41  * Local prototypes.
42  *****************************************************************************/
43 static void Run ( intf_thread_t *p_intf );
44
45 /*****************************************************************************
46  * OpenIntf: initialize interface
47  *****************************************************************************/
48 int E_(OpenIntf) ( vlc_object_t *p_this )
49 {   
50     intf_thread_t *p_intf = (intf_thread_t*) p_this;
51
52     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
53     if( p_intf->p_sys == NULL )
54     {
55         return( 1 );
56     }
57
58     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
59     
60     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
61
62     /* Put Cocoa into multithread mode as soon as possible.
63      * http://developer.apple.com/techpubs/macosx/Cocoa/
64      * TasksAndConcepts/ProgrammingTopics/Multithreading/index.html
65      * This thread does absolutely nothing at all. */
66     [NSThread detachNewThreadSelector:@selector(self) toTarget:[NSString string] withObject:nil];
67     
68     p_intf->p_sys->o_sendport = [[NSPort port] retain];
69     p_intf->p_sys->p_sub = msg_Subscribe( p_intf );
70     p_intf->b_play = VLC_TRUE;
71     p_intf->pf_run = Run;
72
73     return( 0 );
74 }
75
76 /*****************************************************************************
77  * CloseIntf: destroy interface
78  *****************************************************************************/
79 void E_(CloseIntf) ( vlc_object_t *p_this )
80 {
81     intf_thread_t *p_intf = (intf_thread_t*) p_this;
82     
83     msg_Unsubscribe( p_intf, p_intf->p_sys->p_sub );
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     [[VLCMain sharedInstance] setIntf: p_intf];
101     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
102     [NSApp run];
103     [[VLCMain sharedInstance] terminate];
104 }
105
106 int ExecuteOnMainThread( id target, SEL sel, void * p_arg )
107 {
108     int i_ret = 0;
109
110     //NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
111
112     if( [target respondsToSelector: @selector(performSelectorOnMainThread:
113                                              withObject:waitUntilDone:)] )
114     {
115         [target performSelectorOnMainThread: sel
116                 withObject: [NSValue valueWithPointer: p_arg]
117                 waitUntilDone: NO];
118     }
119     else if( NSApp != nil && [[VLCMain sharedInstance] respondsToSelector: @selector(getIntf)] ) 
120     {
121         NSValue * o_v1;
122         NSValue * o_v2;
123         NSArray * o_array;
124         NSPort * o_recv_port;
125         NSInvocation * o_inv;
126         NSPortMessage * o_msg;
127         intf_thread_t * p_intf;
128         NSConditionLock * o_lock;
129         NSMethodSignature * o_sig;
130
131         id * val[] = { &o_lock, &o_v2 };
132
133         p_intf = (intf_thread_t *)VLCIntf;
134
135         o_recv_port = [[NSPort port] retain];
136         o_v1 = [NSValue valueWithPointer: val]; 
137         o_v2 = [NSValue valueWithPointer: p_arg];
138
139         o_sig = [target methodSignatureForSelector: sel];
140         o_inv = [NSInvocation invocationWithMethodSignature: o_sig];
141         [o_inv setArgument: &o_v1 atIndex: 2];
142         [o_inv setTarget: target];
143         [o_inv setSelector: sel];
144
145         o_array = [NSArray arrayWithObject:
146             [NSData dataWithBytes: &o_inv length: sizeof(o_inv)]];
147         o_msg = [[NSPortMessage alloc]
148             initWithSendPort: p_intf->p_sys->o_sendport
149             receivePort: o_recv_port components: o_array];
150
151         o_lock = [[NSConditionLock alloc] initWithCondition: 0];
152         [o_msg sendBeforeDate: [NSDate distantPast]];
153         [o_lock lockWhenCondition: 1];
154         [o_lock unlock];
155         [o_lock release];
156
157         [o_msg release];
158         [o_recv_port release];
159     } 
160     else
161     {
162         i_ret = 1;
163     }
164
165     //[o_pool release];
166
167     return( i_ret );
168 }
169
170 /*****************************************************************************
171  * playlistChanged: Callback triggered by the intf-change playlist
172  * variable, to let the intf update the playlist.
173  *****************************************************************************/
174 int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
175                      vlc_value_t old_val, vlc_value_t new_val, void *param )
176 {
177     intf_thread_t * p_intf = VLCIntf;
178     p_intf->p_sys->b_playlist_update = TRUE;
179     p_intf->p_sys->b_intf_update = TRUE;
180     return VLC_SUCCESS;
181 }
182
183 static struct
184 {
185     unichar i_nskey;
186     unsigned int i_vlckey;
187 } nskeys_to_vlckeys[] = 
188 {
189     { NSUpArrowFunctionKey, KEY_UP },
190     { NSDownArrowFunctionKey, KEY_DOWN },
191     { NSLeftArrowFunctionKey, KEY_LEFT },
192     { NSRightArrowFunctionKey, KEY_RIGHT },
193     { NSF1FunctionKey, KEY_F1 },
194     { NSF2FunctionKey, KEY_F2 },
195     { NSF3FunctionKey, KEY_F3 },
196     { NSF4FunctionKey, KEY_F4 },
197     { NSF5FunctionKey, KEY_F5 },
198     { NSF6FunctionKey, KEY_F6 },
199     { NSF7FunctionKey, KEY_F7 },
200     { NSF8FunctionKey, KEY_F8 },
201     { NSF9FunctionKey, KEY_F9 },
202     { NSF10FunctionKey, KEY_F10 },
203     { NSF11FunctionKey, KEY_F11 },
204     { NSF12FunctionKey, KEY_F12 },
205     { NSHomeFunctionKey, KEY_HOME },
206     { NSEndFunctionKey, KEY_END },
207     { NSPageUpFunctionKey, KEY_PAGEUP },
208     { NSPageDownFunctionKey, KEY_PAGEDOWN },
209     { NSTabCharacter, KEY_TAB },
210     { NSCarriageReturnCharacter, KEY_ENTER },
211     { NSEnterCharacter, KEY_ENTER },
212     { NSBackspaceCharacter, KEY_BACKSPACE },
213     { (unichar) ' ', KEY_SPACE },
214     { (unichar) 0x1b, KEY_ESC },
215     {0,0}
216 };
217
218 unichar VLCKeyToCocoa( unsigned int i_key )
219 {
220     unsigned int i;
221     
222     for( i = 0; nskeys_to_vlckeys[i].i_vlckey != 0; i++ )
223     {
224         if( nskeys_to_vlckeys[i].i_vlckey == (i_key & ~KEY_MODIFIER) )
225         {
226             return nskeys_to_vlckeys[i].i_nskey;
227         }
228     }
229     return (unichar)(i_key & ~KEY_MODIFIER);
230 }
231
232 unsigned int CocoaKeyToVLC( unichar i_key )
233 {
234     unsigned int i;
235     
236     for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
237     {
238         if( nskeys_to_vlckeys[i].i_nskey == i_key )
239         {
240             return nskeys_to_vlckeys[i].i_vlckey;
241         }
242     }
243     return (unsigned int)i_key;
244 }
245
246 unsigned int VLCModifiersToCocoa( unsigned int i_key )
247 {
248     unsigned int new = 0;
249     if( i_key & KEY_MODIFIER_COMMAND )
250         new |= NSCommandKeyMask;
251     if( i_key & KEY_MODIFIER_ALT )
252         new |= NSAlternateKeyMask;
253     if( i_key & KEY_MODIFIER_SHIFT )
254         new |= NSShiftKeyMask;
255     if( i_key & KEY_MODIFIER_CTRL )
256         new |= NSControlKeyMask;
257     return new;
258 }
259
260 /*****************************************************************************
261  * VLCMain implementation 
262  *****************************************************************************/
263 @implementation VLCMain
264
265 static VLCMain *_o_sharedMainInstance = nil;
266
267 + (VLCMain *)sharedInstance
268 {
269     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
270 }
271
272 - (id)init 
273 {
274     if( _o_sharedMainInstance) {
275         [self dealloc];
276     } else {
277         _o_sharedMainInstance = [super init];
278     }
279     
280     return _o_sharedMainInstance;
281 }
282
283 - (void)setIntf: (intf_thread_t *)p_mainintf {
284     p_intf = p_mainintf;
285 }
286
287 - (intf_thread_t *)getIntf {
288     return p_intf;
289 }
290
291 - (void)awakeFromNib
292 {
293     unsigned int i_key = 0;
294     playlist_t *p_playlist;
295     vlc_value_t val;
296
297     [self initStrings];
298     [o_window setExcludedFromWindowsMenu: TRUE];
299     [o_msgs_panel setExcludedFromWindowsMenu: TRUE];
300     [o_msgs_panel setDelegate: self];
301     
302     i_key = config_GetInt( p_intf, "key-quit" );
303     [o_mi_quit setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
304     [o_mi_quit setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
305     i_key = config_GetInt( p_intf, "key-play-pause" );
306     [o_mi_play setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
307     [o_mi_play setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
308     i_key = config_GetInt( p_intf, "key-stop" );
309     [o_mi_stop setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
310     [o_mi_stop setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
311     i_key = config_GetInt( p_intf, "key-faster" );
312     [o_mi_faster setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
313     [o_mi_faster setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
314     i_key = config_GetInt( p_intf, "key-slower" );
315     [o_mi_slower setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
316     [o_mi_slower setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
317     i_key = config_GetInt( p_intf, "key-prev" );
318     [o_mi_previous setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
319     [o_mi_previous setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
320     i_key = config_GetInt( p_intf, "key-next" );
321     [o_mi_next setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
322     [o_mi_next setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
323     i_key = config_GetInt( p_intf, "key-jump+10sec" );
324     [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
325     [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
326     i_key = config_GetInt( p_intf, "key-jump-10sec" );
327     [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
328     [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
329     i_key = config_GetInt( p_intf, "key-jump+1min" );
330     [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
331     [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
332     i_key = config_GetInt( p_intf, "key-jump-1min" );
333     [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
334     [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
335     i_key = config_GetInt( p_intf, "key-jump+5min" );
336     [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
337     [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
338     i_key = config_GetInt( p_intf, "key-jump-5min" );
339     [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
340     [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
341     i_key = config_GetInt( p_intf, "key-vol-up" );
342     [o_mi_vol_up setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
343     [o_mi_vol_up setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
344     i_key = config_GetInt( p_intf, "key-vol-down" );
345     [o_mi_vol_down setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
346     [o_mi_vol_down setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
347     i_key = config_GetInt( p_intf, "key-vol-mute" );
348     [o_mi_mute setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
349     [o_mi_mute setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
350     i_key = config_GetInt( p_intf, "key-fullscreen" );
351     [o_mi_fullscreen setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
352     [o_mi_fullscreen setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
353
354     var_Create( p_intf, "intf-change", VLC_VAR_BOOL );
355
356     [self setSubmenusEnabled: FALSE];
357     [self manageVolumeSlider];
358     
359     p_playlist = (playlist_t *) vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, FIND_ANYWHERE );
360
361     if( p_playlist )
362     {
363         /* Check if we need to start playing */
364         if( p_intf->b_play )
365         {
366             playlist_Play( p_playlist );
367         }
368         [o_btn_fullscreen setState: ( var_Get( p_playlist, "fullscreen", &val )>=0 && val.b_bool )];
369         vlc_object_release( p_playlist );
370     }
371 }
372
373 - (void)initStrings
374 {
375     [o_window setTitle: _NS("VLC - Controller")];
376     [o_scrollfield setStringValue: _NS("VLC media player")];
377
378     /* button controls */
379     [o_btn_prev setToolTip: _NS("Previous")];
380     [o_btn_rewind setToolTip: _NS("Rewind")];
381     [o_btn_play setToolTip: _NS("Play")];
382     [o_btn_stop setToolTip: _NS("Stop")];
383     [o_btn_ff setToolTip: _NS("Fast Forward")];
384     [o_btn_next setToolTip: _NS("Next")];
385     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
386     [o_volumeslider setToolTip: _NS("Volume")];
387     [o_timeslider setToolTip: _NS("Position")];
388
389     /* messages panel */ 
390     [o_msgs_panel setTitle: _NS("Messages")];
391     [o_msgs_btn_crashlog setTitle: _NS("Open CrashLog")];
392
393     /* main menu */
394     [o_mi_about setTitle: _NS("About VLC media player")];
395     [o_mi_prefs setTitle: _NS("Preferences...")];
396     [o_mi_add_intf setTitle: _NS("Add Interface")];
397     [o_mu_add_intf setTitle: _NS("Add Interface")];
398     [o_mi_services setTitle: _NS("Services")];
399     [o_mi_hide setTitle: _NS("Hide VLC")];
400     [o_mi_hide_others setTitle: _NS("Hide Others")];
401     [o_mi_show_all setTitle: _NS("Show All")];
402     [o_mi_quit setTitle: _NS("Quit VLC")];
403
404     [o_mu_file setTitle: _ANS("1:File")];
405     [o_mi_open_generic setTitle: _NS("Open File...")];
406     [o_mi_open_file setTitle: _NS("Quick Open File...")];
407     [o_mi_open_disc setTitle: _NS("Open Disc...")];
408     [o_mi_open_net setTitle: _NS("Open Network...")];
409     [o_mi_open_recent setTitle: _NS("Open Recent")];
410     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
411
412     [o_mu_edit setTitle: _NS("Edit")];
413     [o_mi_cut setTitle: _NS("Cut")];
414     [o_mi_copy setTitle: _NS("Copy")];
415     [o_mi_paste setTitle: _NS("Paste")];
416     [o_mi_clear setTitle: _NS("Clear")];
417     [o_mi_select_all setTitle: _NS("Select All")];
418
419     [o_mu_controls setTitle: _NS("Controls")];
420     [o_mi_play setTitle: _NS("Play")];
421     [o_mi_stop setTitle: _NS("Stop")];
422     [o_mi_faster setTitle: _NS("Faster")];
423     [o_mi_slower setTitle: _NS("Slower")];
424     [o_mi_previous setTitle: _NS("Previous")];
425     [o_mi_next setTitle: _NS("Next")];
426     [o_mi_random setTitle: _NS("Random")];
427     [o_mi_repeat setTitle: _NS("Repeat One")];
428     [o_mi_loop setTitle: _NS("Repeat All")];
429     [o_mi_fwd setTitle: _NS("Step Forward")];
430     [o_mi_bwd setTitle: _NS("Step Backward")];
431
432     [o_mi_program setTitle: _NS("Program")];
433     [o_mu_program setTitle: _NS("Program")];
434     [o_mi_title setTitle: _NS("Title")];
435     [o_mu_title setTitle: _NS("Title")];
436     [o_mi_chapter setTitle: _NS("Chapter")];
437     [o_mu_chapter setTitle: _NS("Chapter")];
438     
439     [o_mu_audio setTitle: _NS("Audio")];
440     [o_mi_vol_up setTitle: _NS("Volume Up")];
441     [o_mi_vol_down setTitle: _NS("Volume Down")];
442     [o_mi_mute setTitle: _NS("Mute")];
443     [o_mi_audiotrack setTitle: _NS("Audio Track")];
444     [o_mu_audiotrack setTitle: _NS("Audio Track")];
445     [o_mi_channels setTitle: _NS("Audio Channels")];
446     [o_mu_channels setTitle: _NS("Audio Channels")];
447     [o_mi_device setTitle: _NS("Audio Device")];
448     [o_mu_device setTitle: _NS("Audio Device")];
449     [o_mi_visual setTitle: _NS("Visualizations")];
450     [o_mu_visual setTitle: _NS("Visualizations")];
451     
452     [o_mu_video setTitle: _NS("Video")];
453     [o_mi_half_window setTitle: _NS("Half Size")];
454     [o_mi_normal_window setTitle: _NS("Normal Size")];
455     [o_mi_double_window setTitle: _NS("Double Size")];
456     [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
457     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
458     [o_mi_floatontop setTitle: _NS("Float on Top")];
459     [o_mi_videotrack setTitle: _NS("Video Track")];
460     [o_mu_videotrack setTitle: _NS("Video Track")];
461     [o_mi_screen setTitle: _NS("Video Device")];
462     [o_mu_screen setTitle: _NS("Video Device")];
463     [o_mi_subtitle setTitle: _NS("Subtitles Track")];
464     [o_mu_subtitle setTitle: _NS("Subtitles Track")];
465     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
466     [o_mu_deinterlace setTitle: _NS("Deinterlace")];
467
468     [o_mu_window setTitle: _NS("Window")];
469     [o_mi_minimize setTitle: _NS("Minimize Window")];
470     [o_mi_close_window setTitle: _NS("Close Window")];
471     [o_mi_controller setTitle: _NS("Controller")];
472     [o_mi_playlist setTitle: _NS("Playlist")];
473     [o_mi_info setTitle: _NS("Info")];
474     [o_mi_messages setTitle: _NS("Messages")];
475
476     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
477
478     [o_mu_help setTitle: _NS("Help")];
479     [o_mi_readme setTitle: _NS("ReadMe...")];
480     [o_mi_documentation setTitle: _NS("Online Documentation")];
481     [o_mi_reportabug setTitle: _NS("Report a Bug")];
482     [o_mi_website setTitle: _NS("VideoLAN Website")];
483     [o_mi_license setTitle: _NS("License")];
484
485     /* dock menu */
486     [o_dmi_play setTitle: _NS("Play")];
487     [o_dmi_stop setTitle: _NS("Stop")];
488     [o_dmi_next setTitle: _NS("Next")];
489     [o_dmi_previous setTitle: _NS("Previous")];
490     [o_dmi_mute setTitle: _NS("Mute")];
491
492     /* error panel */
493     [o_error setTitle: _NS("Error")];
494     [o_err_lbl setStringValue: _NS("An error has occurred which probably prevented the execution of your request:")];
495     [o_err_bug_lbl setStringValue: _NS("If you believe that it is a bug, please follow the instructions at:")]; 
496     [o_err_btn_msgs setTitle: _NS("Open Messages Window")];
497     [o_err_btn_dismiss setTitle: _NS("Dismiss")];
498     [o_err_ckbk_surpress setTitle: _NS("Suppress further errors")];
499
500     [o_info_window setTitle: _NS("Info")];
501 }
502
503 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
504 {
505     o_msg_lock = [[NSLock alloc] init];
506     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
507
508     o_img_play = [[NSImage imageNamed: @"play"] retain];
509     o_img_play_pressed = [[NSImage imageNamed: @"play_blue"] retain];
510     o_img_pause = [[NSImage imageNamed: @"pause"] retain];
511     o_img_pause_pressed = [[NSImage imageNamed: @"pause_blue"] retain];
512
513     [p_intf->p_sys->o_sendport setDelegate: self];
514     [[NSRunLoop currentRunLoop] 
515         addPort: p_intf->p_sys->o_sendport
516         forMode: NSDefaultRunLoopMode];
517
518     [NSTimer scheduledTimerWithTimeInterval: 0.5
519         target: self selector: @selector(manageIntf:)
520         userInfo: nil repeats: FALSE];
521
522     [NSThread detachNewThreadSelector: @selector(manage)
523         toTarget: self withObject: nil];
524         
525     [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
526         var: "intf-add" selector: @selector(toggleVar:)];
527
528     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
529 }
530
531 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
532 {
533     NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
534     [o_playlist appendArray:
535         [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
536             
537     return( TRUE );
538 }
539
540 - (NSString *)localizedString:(char *)psz
541 {
542     NSString * o_str = nil;
543
544     if( psz != NULL )
545     {
546         o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
547     }
548     if ( o_str == NULL )
549     {
550         msg_Err( VLCIntf, "could not translate: %s", psz );
551     }
552
553     return( o_str );
554 }
555
556 - (char *)delocalizeString:(NSString *)id
557 {
558     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
559                           allowLossyConversion: NO];
560     char * psz_string;
561
562     if ( o_data == nil )
563     {
564         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
565                      allowLossyConversion: YES];
566         psz_string = malloc( [o_data length] + 1 ); 
567         [o_data getBytes: psz_string];
568         psz_string[ [o_data length] ] = '\0';
569         msg_Err( VLCIntf, "cannot convert to wanted encoding: %s",
570                  psz_string );
571     }
572     else
573     {
574         psz_string = malloc( [o_data length] + 1 ); 
575         [o_data getBytes: psz_string];
576         psz_string[ [o_data length] ] = '\0';
577     }
578
579     return psz_string;
580 }
581
582 /* i_width is in pixels */
583 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
584 {
585     NSMutableString *o_wrapped;
586     NSString *o_out_string;
587     NSRange glyphRange, effectiveRange, charRange;
588     NSRect lineFragmentRect;
589     unsigned glyphIndex, breaksInserted = 0;
590
591     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
592         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
593         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
594     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
595     NSTextContainer *o_container = [[NSTextContainer alloc]
596         initWithContainerSize: NSMakeSize(i_width, 2000)];
597     
598     [o_layout_manager addTextContainer: o_container];
599     [o_container release];
600     [o_storage addLayoutManager: o_layout_manager];
601     [o_layout_manager release];
602         
603     o_wrapped = [o_in_string mutableCopy];
604     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
605     
606     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
607             glyphIndex += effectiveRange.length) {
608         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
609                                             effectiveRange: &effectiveRange];
610         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
611                                     actualGlyphRange: &effectiveRange];
612         if ([o_wrapped lineRangeForRange:
613                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
614             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
615             breaksInserted++;
616         }
617     }
618     o_out_string = [NSString stringWithString: o_wrapped];
619     [o_wrapped release];
620     [o_storage release];
621     
622     return o_out_string;
623 }
624
625
626 /*****************************************************************************
627  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
628  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
629  * otherwise ignore it and return NO (where it will get handled by Cocoa).
630  *****************************************************************************/
631 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
632 {
633     unichar key = 0;
634     vlc_value_t val;
635     unsigned int i_pressed_modifiers = 0;
636     struct hotkey *p_hotkeys;
637     int i;
638
639     val.i_int = 0;
640     p_hotkeys = p_intf->p_vlc->p_hotkeys;
641
642     i_pressed_modifiers = [o_event modifierFlags];
643
644     if( i_pressed_modifiers & NSShiftKeyMask )
645         val.i_int |= KEY_MODIFIER_SHIFT;
646     if( i_pressed_modifiers & NSControlKeyMask )
647         val.i_int |= KEY_MODIFIER_CTRL;
648     if( i_pressed_modifiers & NSAlternateKeyMask )
649         val.i_int |= KEY_MODIFIER_ALT;
650     if( i_pressed_modifiers & NSCommandKeyMask )
651         val.i_int |= KEY_MODIFIER_COMMAND;
652
653     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
654
655     val.i_int |= CocoaKeyToVLC( key );
656
657     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
658     {
659         if( p_hotkeys[i].i_key == val.i_int )
660         {
661             var_Set( p_intf->p_vlc, "key-pressed", val );
662             return YES;
663         }
664     }
665
666     return NO;
667 }
668
669 - (id)getControls
670 {
671     if ( o_controls )
672     {
673         return o_controls;
674     }
675     return nil;
676 }
677
678 - (id)getPlaylist
679 {
680     if ( o_playlist )
681     {
682         return o_playlist;
683     }
684     return nil;
685 }
686
687 - (id)getInfo
688 {
689     if ( o_info )
690     {
691         return o_info;
692     }
693     return  nil;
694 }
695
696 - (void)manage
697 {
698     NSDate * o_sleep_date;
699     playlist_t * p_playlist;
700     
701     /* new thread requires a new pool */
702     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
703
704     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
705
706     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, 
707                                               FIND_ANYWHERE );
708
709     if( p_playlist != NULL )
710     {
711         var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
712         var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
713         var_AddCallback( p_playlist, "playlist-current", PlaylistChanged, self );
714         vlc_object_release( p_playlist );
715     }
716
717     while( !p_intf->b_die )
718     {
719         vlc_mutex_lock( &p_intf->change_lock );
720
721 #define p_input p_intf->p_sys->p_input
722         
723         if( p_input == NULL )
724         {
725             p_input = (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
726                                            FIND_ANYWHERE );
727             
728             /* Refresh the interface */
729             if( p_input )
730             {
731                 msg_Dbg( p_intf, "input has changed, refreshing interface" );
732                 p_intf->p_sys->i_play_status = PLAYING_S;
733                 p_intf->p_sys->b_playing = TRUE;
734                 p_intf->p_sys->b_current_title_update = 1;
735                 p_intf->p_sys->b_intf_update = TRUE;
736                 p_intf->p_sys->b_input_update = TRUE;
737             }
738         }
739         else if( p_input->b_dead )
740         {
741             /* input stopped */
742             p_intf->p_sys->b_playing = FALSE;
743             p_intf->p_sys->b_intf_update = TRUE;
744             p_intf->p_sys->i_play_status = PAUSE_S;
745             [o_scrollfield setStringValue: _NS("VLC media player") ];
746             vlc_object_release( p_input );
747             p_input = NULL;
748         }
749 #undef p_input
750
751         vlc_mutex_unlock( &p_intf->change_lock );
752
753         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: .3];
754         [NSThread sleepUntilDate: o_sleep_date];
755     }
756
757     [self terminate];
758     [o_pool release];
759 }
760
761 - (void)manageIntf:(NSTimer *)o_timer
762 {
763     vlc_value_t val;
764
765     if( p_intf->p_vlc->b_die == VLC_TRUE )
766     {
767         [o_timer invalidate];
768         return;
769     }
770
771 #define p_input p_intf->p_sys->p_input
772     if( p_intf->p_sys->b_intf_update )
773     {
774         vlc_bool_t b_input = VLC_FALSE;
775         vlc_bool_t b_plmul = VLC_FALSE;
776         vlc_bool_t b_control = VLC_FALSE;
777         vlc_bool_t b_seekable = VLC_FALSE;
778         vlc_bool_t b_chapters = VLC_FALSE;
779
780         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
781                                                    FIND_ANYWHERE );
782         b_plmul = p_playlist->i_size > 1;
783         
784         vlc_object_release( p_playlist ); 
785
786         if( ( b_input = ( p_input != NULL ) ) )
787         {
788             /* seekable streams */
789             var_Get( p_input, "seekable", &val);
790             b_seekable = val.b_bool;
791
792             /* check wether slow/fast motion is possible*/
793             b_control = p_input->input.b_can_pace_control; 
794
795             /* chapters & titles */
796             //b_chapters = p_input->stream.i_area_nb > 1; 
797         }
798
799         [o_btn_stop setEnabled: b_input];
800         [o_btn_ff setEnabled: b_seekable];
801         [o_btn_rewind setEnabled: b_seekable];
802         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
803         [o_btn_next setEnabled: (b_plmul || b_chapters)];
804
805         [o_timeslider setFloatValue: 0.0];
806         [o_timeslider setEnabled: b_seekable];
807         [o_timefield setStringValue: @"0:00:00"];
808
809         p_intf->p_sys->b_intf_update = VLC_FALSE;
810     }
811     
812     if ( p_intf->p_sys->b_playlist_update )
813     {
814         [o_playlist playlistUpdated];
815         p_intf->p_sys->b_playlist_update = VLC_FALSE;
816     }
817     
818     if( p_intf->p_sys->b_fullscreen_update )
819     {
820         vout_thread_t * p_vout;
821
822         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
823                                                    FIND_ANYWHERE );
824
825         [o_btn_fullscreen setState: ( var_Get( p_playlist, "fullscreen", &val )>=0 && val.b_bool ) ];
826
827         vlc_object_release( p_playlist );
828
829         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
830         if( p_vout != NULL )
831         {
832             [o_btn_fullscreen setEnabled: VLC_TRUE];
833             vlc_object_release( p_vout );
834         }
835         else
836         {
837             [o_btn_fullscreen setEnabled: VLC_FALSE];
838         }
839         p_intf->p_sys->b_fullscreen_update = VLC_FALSE;
840     }
841
842     if( p_input && !p_input->b_die )
843     {
844         vlc_value_t val;
845
846         if( p_intf->p_sys->b_current_title_update )
847         {
848             NSString *o_temp;
849             vout_thread_t *p_vout;
850             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
851                                                        FIND_ANYWHERE );
852
853             if( p_playlist == NULL )
854             {
855                 return;
856             }
857
858             vlc_mutex_lock( &p_playlist->object_lock );
859             o_temp = [NSString stringWithUTF8String: 
860                 p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
861             if( o_temp == NULL )
862                 o_temp = [NSString stringWithCString:
863                     p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
864             vlc_mutex_unlock( &p_playlist->object_lock );
865             [o_scrollfield setStringValue: o_temp ];
866
867
868             p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
869                                                     FIND_ANYWHERE );
870             if( p_vout != NULL )
871             {
872                 id o_vout_wnd;
873                 NSEnumerator * o_enum = [[NSApp orderedWindows] objectEnumerator];
874                 
875                 while( ( o_vout_wnd = [o_enum nextObject] ) )
876                 {
877                     if( [[o_vout_wnd className] isEqualToString: @"VLCWindow"] )
878                     {
879                         ;//[o_vout_wnd updateTitle];
880                     }
881                 }
882                 vlc_object_release( (vlc_object_t *)p_vout );
883             }
884             [o_playlist updateRowSelection];
885             vlc_object_release( p_playlist );
886             p_intf->p_sys->b_current_title_update = FALSE;
887         }
888
889         if( p_intf->p_sys->b_playing && [o_timeslider isEnabled] )
890         {
891             /* Update the slider */
892             vlc_value_t time;
893             NSString * o_time;
894             mtime_t i_seconds;
895             vlc_value_t pos;
896             float f_updated;
897                 
898             var_Get( p_input, "position", &pos );
899             f_updated = 10000. * pos.f_float;
900             [o_timeslider setFloatValue: f_updated];
901                 
902             var_Get( p_input, "time", &time );
903             i_seconds = time.i_time / 1000000;
904             
905             o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
906                             (int) (i_seconds / (60 * 60)),
907                             (int) (i_seconds / 60 % 60),
908                             (int) (i_seconds % 60)];
909             [o_timefield setStringValue: o_time];
910         }
911         
912         /* Manage volume status */
913         [self manageVolumeSlider];
914         
915         /* Manage Playing status */
916         var_Get( p_input, "state", &val );
917         if( p_intf->p_sys->i_play_status != val.i_int )
918         {
919             p_intf->p_sys->i_play_status = val.i_int;
920             [self playStatusUpdated: p_intf->p_sys->i_play_status];
921         }
922     }
923     else if( p_intf->p_sys->b_playing && !p_intf->b_die )
924     {
925         p_intf->p_sys->i_play_status = PAUSE_S;
926         p_intf->p_sys->b_intf_update = VLC_TRUE;
927         [self playStatusUpdated: p_intf->p_sys->i_play_status];
928         [self setSubmenusEnabled: FALSE];
929     }
930
931 #undef p_input
932
933     [self updateMessageArray];
934
935     [NSTimer scheduledTimerWithTimeInterval: 0.3
936         target: self selector: @selector(manageIntf:)
937         userInfo: nil repeats: FALSE];
938 }
939
940 - (void)setupMenus
941 {
942     playlist_t *p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST, 
943                                                 FIND_ANYWHERE );
944     
945     if( p_playlist != NULL )
946     {
947 #define p_input p_playlist->p_input
948         if( p_input != NULL )
949         {
950             [o_controls setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
951                 var: "program" selector: @selector(toggleVar:)];
952             
953             [o_controls setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
954                 var: "title" selector: @selector(toggleVar:)];
955             
956             [o_controls setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
957                 var: "chapter" selector: @selector(toggleVar:)];
958             
959             [o_controls setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
960                 var: "audio-es" selector: @selector(toggleVar:)];
961             
962             [o_controls setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
963                 var: "video-es" selector: @selector(toggleVar:)];
964             
965             [o_controls setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
966                 var: "spu-es" selector: @selector(toggleVar:)];
967         
968             aout_instance_t * p_aout = vlc_object_find( p_intf, VLC_OBJECT_AOUT,
969                                                         FIND_ANYWHERE );
970             if ( p_aout != NULL )
971             {
972                 [o_controls setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
973                     var: "audio-channels" selector: @selector(toggleVar:)];
974             
975                 [o_controls setupVarMenuItem: o_mi_device target: (vlc_object_t *)p_aout
976                     var: "audio-device" selector: @selector(toggleVar:)];
977                     
978                 [o_controls setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
979                     var: "visual" selector: @selector(toggleVar:)];
980                 vlc_object_release( (vlc_object_t *)p_aout );
981             }
982             
983             vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
984                                                                 FIND_ANYWHERE );
985             
986             if ( p_vout != NULL )
987             {
988                 [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
989                     var: "video-device" selector: @selector(toggleVar:)];
990                 
991                 [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
992                     var: "deinterlace" selector: @selector(toggleVar:)];
993                 vlc_object_release( (vlc_object_t *)p_vout );
994             }
995         }
996 #undef p_input
997     }
998     vlc_object_release( (vlc_object_t *)p_playlist );
999 }
1000
1001 - (void)updateMessageArray
1002 {
1003     int i_start, i_stop;
1004     vlc_value_t quiet;
1005
1006     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1007     i_stop = *p_intf->p_sys->p_sub->pi_stop;
1008     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1009
1010     if( p_intf->p_sys->p_sub->i_start != i_stop )
1011     {
1012         NSColor *o_white = [NSColor whiteColor];
1013         NSColor *o_red = [NSColor redColor];
1014         NSColor *o_yellow = [NSColor yellowColor];
1015         NSColor *o_gray = [NSColor grayColor];
1016
1017         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1018         static const char * ppsz_type[4] = { ": ", " error: ",
1019                                              " warning: ", " debug: " };
1020
1021         for( i_start = p_intf->p_sys->p_sub->i_start;
1022              i_start != i_stop;
1023              i_start = (i_start+1) % VLC_MSG_QSIZE )
1024         {
1025             NSString *o_msg;
1026             NSDictionary *o_attr;
1027             NSAttributedString *o_msg_color;
1028
1029             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
1030
1031             [o_msg_lock lock];
1032
1033             if( [o_msg_arr count] + 2 > 400 )
1034             {
1035                 unsigned rid[] = { 0, 1 };
1036                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
1037                            numIndices: sizeof(rid)/sizeof(rid[0])];
1038             }
1039
1040             o_attr = [NSDictionary dictionaryWithObject: o_gray
1041                 forKey: NSForegroundColorAttributeName];
1042             o_msg = [NSString stringWithFormat: @"%s%s",
1043                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1044                 ppsz_type[i_type]];
1045             o_msg_color = [[NSAttributedString alloc]
1046                 initWithString: o_msg attributes: o_attr];
1047             [o_msg_arr addObject: [o_msg_color autorelease]];
1048
1049             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
1050                 forKey: NSForegroundColorAttributeName];
1051             o_msg = [NSString stringWithFormat: @"%s\n",
1052                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1053             o_msg_color = [[NSAttributedString alloc]
1054                 initWithString: o_msg attributes: o_attr];
1055             [o_msg_arr addObject: [o_msg_color autorelease]];
1056
1057             [o_msg_lock unlock];
1058
1059             var_Get( p_intf->p_vlc, "verbose", &quiet );
1060
1061             if( i_type == 1 && quiet.i_int > -1 )
1062             {
1063                 NSString *o_my_msg = [NSString stringWithFormat: @"%s: %s\n",
1064                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1065                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1066
1067                 NSRange s_r = NSMakeRange( [[o_err_msg string] length], 0 );
1068                 [o_err_msg setEditable: YES];
1069                 [o_err_msg setSelectedRange: s_r];
1070                 [o_err_msg insertText: o_my_msg];
1071
1072                 [o_error makeKeyAndOrderFront: self];
1073                 [o_err_msg setEditable: NO];
1074             }
1075         }
1076
1077         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1078         p_intf->p_sys->p_sub->i_start = i_start;
1079         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1080     }
1081 }
1082
1083 - (void)playStatusUpdated:(int)i_status
1084 {
1085     if( i_status != PAUSE_S )
1086     {
1087         [o_btn_play setImage: o_img_pause];
1088         [o_btn_play setAlternateImage: o_img_pause_pressed];
1089         [o_btn_play setToolTip: _NS("Pause")];
1090         [o_mi_play setTitle: _NS("Pause")];
1091         [o_dmi_play setTitle: _NS("Pause")];
1092     }
1093     else
1094     {
1095         [o_btn_play setImage: o_img_play];
1096         [o_btn_play setAlternateImage: o_img_play_pressed];
1097         [o_btn_play setToolTip: _NS("Play")];
1098         [o_mi_play setTitle: _NS("Play")];
1099         [o_dmi_play setTitle: _NS("Play")];
1100     }
1101 }
1102
1103 - (void)setSubmenusEnabled:(BOOL)b_enabled
1104 {
1105     [o_mi_program setEnabled: b_enabled];
1106     [o_mi_title setEnabled: b_enabled];
1107     [o_mi_chapter setEnabled: b_enabled];
1108     [o_mi_audiotrack setEnabled: b_enabled];
1109     [o_mi_visual setEnabled: b_enabled];
1110     [o_mi_videotrack setEnabled: b_enabled];
1111     [o_mi_subtitle setEnabled: b_enabled];
1112     [o_mi_channels setEnabled: b_enabled];
1113     [o_mi_deinterlace setEnabled: b_enabled];
1114     [o_mi_device setEnabled: b_enabled];
1115     [o_mi_screen setEnabled: b_enabled];
1116 }
1117
1118 - (void)manageVolumeSlider
1119 {
1120     audio_volume_t i_volume;
1121
1122     aout_VolumeGet( p_intf, &i_volume );
1123
1124     [o_volumeslider setFloatValue: (float)i_volume / AOUT_VOLUME_STEP]; 
1125     [o_volumeslider setEnabled: TRUE];
1126
1127     p_intf->p_sys->b_mute = ( i_volume == 0 );
1128 }
1129
1130 - (IBAction)timesliderUpdate:(id)sender
1131 {
1132     input_thread_t * p_input;
1133     float f_updated;
1134
1135     switch( [[NSApp currentEvent] type] )
1136     {
1137         case NSLeftMouseUp:
1138         case NSLeftMouseDown:
1139         case NSLeftMouseDragged:
1140             f_updated = [sender floatValue];
1141             break;
1142
1143         default:
1144             return;
1145     }
1146
1147     p_input = vlc_object_find( p_intf, VLC_OBJECT_INPUT,
1148                                             FIND_ANYWHERE );
1149
1150     if( p_input != NULL )
1151     {
1152         vlc_value_t time;
1153         vlc_value_t pos;
1154         mtime_t i_seconds;
1155         NSString * o_time;
1156
1157         pos.f_float = f_updated / 10000.;
1158         var_Set( p_input, "position", pos );
1159         [o_timeslider setFloatValue: f_updated];
1160
1161         var_Get( p_input, "time", &time );
1162         i_seconds = time.i_time / 1000000;
1163         
1164         o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
1165                         (int) (i_seconds / (60 * 60)),
1166                         (int) (i_seconds / 60 % 60),
1167                         (int) (i_seconds % 60)];
1168         [o_timefield setStringValue: o_time];
1169
1170         vlc_object_release( p_input );
1171     }
1172 }
1173
1174 - (void)terminate
1175 {
1176     playlist_t * p_playlist;
1177     vout_thread_t * p_vout;
1178
1179     /* Stop playback */
1180     if( ( p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1181                                         FIND_ANYWHERE ) ) )
1182     {
1183         playlist_Stop( p_playlist );
1184         vlc_object_release( p_playlist );
1185     }
1186
1187     /* FIXME - Wait here until all vouts are terminated because
1188        libvlc's VLC_CleanUp destroys interfaces before vouts, which isn't
1189        good on OS X. We definitly need a cleaner way to handle this,
1190        but this may hopefully be good enough for now.
1191          -- titer 2003/11/22 */
1192     while( ( p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1193                                        FIND_ANYWHERE ) ) )
1194     {
1195         vlc_object_release( p_vout );
1196         msleep( 100000 );
1197     }
1198     msleep( 500000 );
1199
1200     if( o_img_pause_pressed != nil )
1201     {
1202         [o_img_pause_pressed release];
1203         o_img_pause_pressed = nil;
1204     }
1205     
1206     if( o_img_pause_pressed != nil )
1207     {
1208         [o_img_pause_pressed release];
1209         o_img_pause_pressed = nil;
1210     }
1211     
1212     if( o_img_pause != nil )
1213     {
1214         [o_img_pause release];
1215         o_img_pause = nil;
1216     }
1217
1218     if( o_img_play != nil )
1219     {
1220         [o_img_play release];
1221         o_img_play = nil;
1222     }
1223
1224     if( o_msg_arr != nil )
1225     {
1226         [o_msg_arr removeAllObjects];
1227         [o_msg_arr release];
1228         o_msg_arr = nil;
1229     }
1230
1231     if( o_msg_lock != nil )
1232     {
1233         [o_msg_lock release];
1234         o_msg_lock = nil;
1235     }
1236
1237     /* write cached user defaults to disk */
1238     [[NSUserDefaults standardUserDefaults] synchronize];
1239     
1240     p_intf->b_die = VLC_TRUE;
1241     [NSApp stop:NULL];
1242 }
1243
1244 - (IBAction)clearRecentItems:(id)sender
1245 {
1246     [[NSDocumentController sharedDocumentController]
1247                           clearRecentDocuments: nil];
1248 }
1249
1250 - (void)openRecentItem:(id)sender
1251 {
1252     [self application: nil openFile: [sender title]]; 
1253 }
1254
1255 - (IBAction)viewPreferences:(id)sender
1256 {
1257     [o_prefs showPrefs];
1258 }
1259
1260 - (IBAction)closeError:(id)sender
1261 {
1262     vlc_value_t val;
1263
1264     if( [o_err_ckbk_surpress state] == NSOnState )
1265     {
1266         val.i_int = -1;
1267         var_Set( p_intf->p_vlc, "verbose", val );
1268     }
1269     [o_err_msg setString: @""];
1270     [o_error performClose: self];
1271 }
1272
1273 - (IBAction)openReadMe:(id)sender
1274 {
1275     NSString * o_path = [[NSBundle mainBundle] 
1276         pathForResource: @"README.MacOSX" ofType: @"rtf"]; 
1277
1278     [[NSWorkspace sharedWorkspace] openFile: o_path 
1279                                    withApplication: @"TextEdit"];
1280 }
1281
1282 - (IBAction)openDocumentation:(id)sender
1283 {
1284     NSURL * o_url = [NSURL URLWithString: 
1285         @"http://www.videolan.org/doc/"];
1286
1287     [[NSWorkspace sharedWorkspace] openURL: o_url];
1288 }
1289
1290 - (IBAction)reportABug:(id)sender
1291 {
1292     NSURL * o_url = [NSURL URLWithString: 
1293         @"http://www.videolan.org/support/bug-reporting.html"];
1294
1295     [[NSWorkspace sharedWorkspace] openURL: o_url];
1296 }
1297
1298 - (IBAction)openWebsite:(id)sender
1299 {
1300     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
1301
1302     [[NSWorkspace sharedWorkspace] openURL: o_url];
1303 }
1304
1305 - (IBAction)openLicense:(id)sender
1306 {
1307     NSString * o_path = [[NSBundle mainBundle] 
1308         pathForResource: @"COPYING" ofType: nil];
1309
1310     [[NSWorkspace sharedWorkspace] openFile: o_path 
1311                                    withApplication: @"TextEdit"];
1312 }
1313
1314 - (IBAction)openCrashLog:(id)sender
1315 {
1316     NSString * o_path = [@"~/Library/Logs/CrashReporter/VLC.crash.log"
1317                                     stringByExpandingTildeInPath]; 
1318
1319     
1320     if ( [[NSFileManager defaultManager] fileExistsAtPath: o_path ] )
1321     {
1322         [[NSWorkspace sharedWorkspace] openFile: o_path 
1323                                     withApplication: @"Console"];
1324     }
1325     else
1326     {
1327         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.") );
1328
1329     }
1330 }
1331
1332 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1333 {
1334     if( [o_notification object] == o_msgs_panel )
1335     {
1336         id o_msg;
1337         NSEnumerator * o_enum;
1338
1339         [o_messages setString: @""]; 
1340
1341         [o_msg_lock lock];
1342
1343         o_enum = [o_msg_arr objectEnumerator];
1344
1345         while( ( o_msg = [o_enum nextObject] ) != nil )
1346         {
1347             [o_messages insertText: o_msg];
1348         }
1349
1350         [o_msg_lock unlock];
1351     }
1352 }
1353
1354 @end
1355
1356 @implementation VLCMain (NSMenuValidation)
1357
1358 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1359 {
1360     NSString *o_title = [o_mi title];
1361     BOOL bEnabled = TRUE;
1362
1363     if( [o_title isEqualToString: _NS("License")] )
1364     {
1365         /* we need to do this only once */
1366         [self setupMenus];
1367     }
1368
1369     /* Recent Items Menu */
1370     if( [o_title isEqualToString: _NS("Clear Menu")] )
1371     {
1372         NSMenu * o_menu = [o_mi_open_recent submenu];
1373         int i_nb_items = [o_menu numberOfItems];
1374         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1375                                                        recentDocumentURLs];
1376         UInt32 i_nb_docs = [o_docs count];
1377
1378         if( i_nb_items > 1 )
1379         {
1380             while( --i_nb_items )
1381             {
1382                 [o_menu removeItemAtIndex: 0];
1383             }
1384         }
1385
1386         if( i_nb_docs > 0 )
1387         {
1388             NSURL * o_url;
1389             NSString * o_doc;
1390
1391             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1392
1393             while( TRUE )
1394             {
1395                 i_nb_docs--;
1396
1397                 o_url = [o_docs objectAtIndex: i_nb_docs];
1398
1399                 if( [o_url isFileURL] )
1400                 {
1401                     o_doc = [o_url path];
1402                 }
1403                 else
1404                 {
1405                     o_doc = [o_url absoluteString];
1406                 }
1407
1408                 [o_menu insertItemWithTitle: o_doc
1409                     action: @selector(openRecentItem:)
1410                     keyEquivalent: @"" atIndex: 0]; 
1411
1412                 if( i_nb_docs == 0 )
1413                 {
1414                     break;
1415                 }
1416             } 
1417         }
1418         else
1419         {
1420             bEnabled = FALSE;
1421         }
1422     }
1423     return( bEnabled );
1424 }
1425
1426 @end
1427
1428 @implementation VLCMain (Internal)
1429
1430 - (void)handlePortMessage:(NSPortMessage *)o_msg
1431 {
1432     id ** val;
1433     NSData * o_data;
1434     NSValue * o_value;
1435     NSInvocation * o_inv;
1436     NSConditionLock * o_lock;
1437  
1438     o_data = [[o_msg components] lastObject];
1439     o_inv = *((NSInvocation **)[o_data bytes]); 
1440     [o_inv getArgument: &o_value atIndex: 2];
1441     val = (id **)[o_value pointerValue];
1442     [o_inv setArgument: val[1] atIndex: 2];
1443     o_lock = *(val[0]);
1444
1445     [o_lock lock];
1446     [o_inv invoke];
1447     [o_lock unlockWithCondition: 1];
1448 }
1449
1450 @end