]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
* Fix the play/pause button status
[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     [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
468     [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
469
470     [o_mu_window setTitle: _NS("Window")];
471     [o_mi_minimize setTitle: _NS("Minimize Window")];
472     [o_mi_close_window setTitle: _NS("Close Window")];
473     [o_mi_controller setTitle: _NS("Controller")];
474     [o_mi_equalizer setTitle: _NS("Equalizer")];
475     [o_mi_playlist setTitle: _NS("Playlist")];
476     [o_mi_info setTitle: _NS("Info")];
477     [o_mi_messages setTitle: _NS("Messages")];
478
479     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
480
481     [o_mu_help setTitle: _NS("Help")];
482     [o_mi_readme setTitle: _NS("ReadMe...")];
483     [o_mi_documentation setTitle: _NS("Online Documentation")];
484     [o_mi_reportabug setTitle: _NS("Report a Bug")];
485     [o_mi_website setTitle: _NS("VideoLAN Website")];
486     [o_mi_license setTitle: _NS("License")];
487
488     /* dock menu */
489     [o_dmi_play setTitle: _NS("Play")];
490     [o_dmi_stop setTitle: _NS("Stop")];
491     [o_dmi_next setTitle: _NS("Next")];
492     [o_dmi_previous setTitle: _NS("Previous")];
493     [o_dmi_mute setTitle: _NS("Mute")];
494
495     /* error panel */
496     [o_error setTitle: _NS("Error")];
497     [o_err_lbl setStringValue: _NS("An error has occurred which probably prevented the execution of your request:")];
498     [o_err_bug_lbl setStringValue: _NS("If you believe that it is a bug, please follow the instructions at:")];
499     [o_err_btn_msgs setTitle: _NS("Open Messages Window")];
500     [o_err_btn_dismiss setTitle: _NS("Dismiss")];
501     [o_err_ckbk_surpress setTitle: _NS("Suppress further errors")];
502
503     [o_info_window setTitle: _NS("Info")];
504 }
505
506 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
507 {
508     o_msg_lock = [[NSLock alloc] init];
509     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
510
511     o_img_play = [[NSImage imageNamed: @"play"] retain];
512     o_img_play_pressed = [[NSImage imageNamed: @"play_blue"] retain];
513     o_img_pause = [[NSImage imageNamed: @"pause"] retain];
514     o_img_pause_pressed = [[NSImage imageNamed: @"pause_blue"] retain];
515
516     [p_intf->p_sys->o_sendport setDelegate: self];
517     [[NSRunLoop currentRunLoop]
518         addPort: p_intf->p_sys->o_sendport
519         forMode: NSDefaultRunLoopMode];
520
521     [NSTimer scheduledTimerWithTimeInterval: 0.5
522         target: self selector: @selector(manageIntf:)
523         userInfo: nil repeats: FALSE];
524
525     [NSThread detachNewThreadSelector: @selector(manage)
526         toTarget: self withObject: nil];
527
528     [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
529         var: "intf-add" selector: @selector(toggleVar:)];
530
531     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
532 }
533
534 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
535 {
536     NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
537     [o_playlist appendArray:
538         [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
539
540     return( TRUE );
541 }
542
543 - (NSString *)localizedString:(char *)psz
544 {
545     NSString * o_str = nil;
546
547     if( psz != NULL )
548     {
549         o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
550     }
551     if ( o_str == NULL )
552     {
553         msg_Err( VLCIntf, "could not translate: %s", psz );
554     }
555
556     return( o_str );
557 }
558
559 - (char *)delocalizeString:(NSString *)id
560 {
561     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
562                           allowLossyConversion: NO];
563     char * psz_string;
564
565     if ( o_data == nil )
566     {
567         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
568                      allowLossyConversion: YES];
569         psz_string = malloc( [o_data length] + 1 );
570         [o_data getBytes: psz_string];
571         psz_string[ [o_data length] ] = '\0';
572         msg_Err( VLCIntf, "cannot convert to wanted encoding: %s",
573                  psz_string );
574     }
575     else
576     {
577         psz_string = malloc( [o_data length] + 1 );
578         [o_data getBytes: psz_string];
579         psz_string[ [o_data length] ] = '\0';
580     }
581
582     return psz_string;
583 }
584
585 /* i_width is in pixels */
586 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
587 {
588     NSMutableString *o_wrapped;
589     NSString *o_out_string;
590     NSRange glyphRange, effectiveRange, charRange;
591     NSRect lineFragmentRect;
592     unsigned glyphIndex, breaksInserted = 0;
593
594     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
595         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
596         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
597     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
598     NSTextContainer *o_container = [[NSTextContainer alloc]
599         initWithContainerSize: NSMakeSize(i_width, 2000)];
600
601     [o_layout_manager addTextContainer: o_container];
602     [o_container release];
603     [o_storage addLayoutManager: o_layout_manager];
604     [o_layout_manager release];
605
606     o_wrapped = [o_in_string mutableCopy];
607     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
608
609     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
610             glyphIndex += effectiveRange.length) {
611         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
612                                             effectiveRange: &effectiveRange];
613         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
614                                     actualGlyphRange: &effectiveRange];
615         if ([o_wrapped lineRangeForRange:
616                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
617             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
618             breaksInserted++;
619         }
620     }
621     o_out_string = [NSString stringWithString: o_wrapped];
622     [o_wrapped release];
623     [o_storage release];
624
625     return o_out_string;
626 }
627
628
629 /*****************************************************************************
630  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
631  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
632  * otherwise ignore it and return NO (where it will get handled by Cocoa).
633  *****************************************************************************/
634 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
635 {
636     unichar key = 0;
637     vlc_value_t val;
638     unsigned int i_pressed_modifiers = 0;
639     struct hotkey *p_hotkeys;
640     int i;
641
642     val.i_int = 0;
643     p_hotkeys = p_intf->p_vlc->p_hotkeys;
644
645     i_pressed_modifiers = [o_event modifierFlags];
646
647     if( i_pressed_modifiers & NSShiftKeyMask )
648         val.i_int |= KEY_MODIFIER_SHIFT;
649     if( i_pressed_modifiers & NSControlKeyMask )
650         val.i_int |= KEY_MODIFIER_CTRL;
651     if( i_pressed_modifiers & NSAlternateKeyMask )
652         val.i_int |= KEY_MODIFIER_ALT;
653     if( i_pressed_modifiers & NSCommandKeyMask )
654         val.i_int |= KEY_MODIFIER_COMMAND;
655
656     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
657
658     val.i_int |= CocoaKeyToVLC( key );
659
660     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
661     {
662         if( p_hotkeys[i].i_key == val.i_int )
663         {
664             var_Set( p_intf->p_vlc, "key-pressed", val );
665             return YES;
666         }
667     }
668
669     return NO;
670 }
671
672 - (id)getControls
673 {
674     if ( o_controls )
675     {
676         return o_controls;
677     }
678     return nil;
679 }
680
681 - (id)getPlaylist
682 {
683     if ( o_playlist )
684     {
685         return o_playlist;
686     }
687     return nil;
688 }
689
690 - (id)getInfo
691 {
692     if ( o_info )
693     {
694         return o_info;
695     }
696     return  nil;
697 }
698
699 - (void)manage
700 {
701     NSDate * o_sleep_date;
702     playlist_t * p_playlist;
703
704     /* new thread requires a new pool */
705     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
706
707     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
708
709     p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
710                                               FIND_ANYWHERE );
711
712     if( p_playlist != NULL )
713     {
714         var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
715         var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
716         var_AddCallback( p_playlist, "playlist-current", PlaylistChanged, self );
717         vlc_object_release( p_playlist );
718     }
719
720     while( !p_intf->b_die )
721     {
722         vlc_mutex_lock( &p_intf->change_lock );
723
724 #define p_input p_intf->p_sys->p_input
725
726         if( p_input == NULL )
727         {
728             p_input = (input_thread_t *)vlc_object_find( p_intf, VLC_OBJECT_INPUT,
729                                            FIND_ANYWHERE );
730
731             /* Refresh the interface */
732             if( p_input )
733             {
734                 msg_Dbg( p_intf, "input has changed, refreshing interface" );
735                 p_intf->p_sys->b_input_update = VLC_TRUE;
736             }
737         }
738         else if( p_input->b_die || p_input->b_dead )
739         {
740             /* input stopped */
741             p_intf->p_sys->b_intf_update = VLC_TRUE;
742             p_intf->p_sys->i_play_status = END_S;
743             [o_scrollfield setStringValue: _NS("VLC media player") ];
744             vlc_object_release( p_input );
745             p_input = NULL;
746         }
747 #undef p_input
748
749         vlc_mutex_unlock( &p_intf->change_lock );
750
751         o_sleep_date = [NSDate dateWithTimeIntervalSinceNow: .1];
752         [NSThread sleepUntilDate: o_sleep_date];
753     }
754
755     [self terminate];
756     [o_pool release];
757 }
758
759 - (void)manageIntf:(NSTimer *)o_timer
760 {
761     vlc_value_t val;
762
763     if( p_intf->p_vlc->b_die == VLC_TRUE )
764     {
765         [o_timer invalidate];
766         return;
767     }
768
769 #define p_input p_intf->p_sys->p_input
770     if( p_intf->p_sys->b_input_update )
771     {
772         /* Called when new input is opened */
773         p_intf->p_sys->b_current_title_update = VLC_TRUE;
774         p_intf->p_sys->b_intf_update = VLC_TRUE;
775         p_intf->p_sys->b_input_update = VLC_FALSE;
776     }
777     if( p_intf->p_sys->b_intf_update )
778     {
779         vlc_bool_t b_input = VLC_FALSE;
780         vlc_bool_t b_plmul = VLC_FALSE;
781         vlc_bool_t b_control = VLC_FALSE;
782         vlc_bool_t b_seekable = VLC_FALSE;
783         vlc_bool_t b_chapters = VLC_FALSE;
784
785         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
786                                                    FIND_ANYWHERE );
787         b_plmul = p_playlist->i_size > 1;
788
789         vlc_object_release( p_playlist );
790
791         if( ( b_input = ( p_input != NULL ) ) )
792         {
793             /* seekable streams */
794             var_Get( p_input, "seekable", &val);
795             b_seekable = val.b_bool;
796
797             /* check wether slow/fast motion is possible*/
798             b_control = p_input->input.b_can_pace_control;
799
800             /* chapters & titles */
801             //b_chapters = p_input->stream.i_area_nb > 1;
802         }
803
804         [o_btn_stop setEnabled: b_input];
805         [o_btn_ff setEnabled: b_seekable];
806         [o_btn_rewind setEnabled: b_seekable];
807         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
808         [o_btn_next setEnabled: (b_plmul || b_chapters)];
809
810         [o_timeslider setFloatValue: 0.0];
811         [o_timeslider setEnabled: b_seekable];
812         [o_timefield setStringValue: @"0:00:00"];
813
814         p_intf->p_sys->b_intf_update = VLC_FALSE;
815     }
816
817     if ( p_intf->p_sys->b_playlist_update )
818     {
819         [o_playlist playlistUpdated];
820         p_intf->p_sys->b_playlist_update = VLC_FALSE;
821     }
822
823     if( p_intf->p_sys->b_fullscreen_update )
824     {
825         vout_thread_t * p_vout;
826
827         playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
828                                                    FIND_ANYWHERE );
829
830         [o_btn_fullscreen setState: ( var_Get( p_playlist, "fullscreen", &val )>=0 && val.b_bool ) ];
831
832         vlc_object_release( p_playlist );
833
834         p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT, FIND_ANYWHERE );
835         if( p_vout != NULL )
836         {
837             [o_btn_fullscreen setEnabled: VLC_TRUE];
838             vlc_object_release( p_vout );
839         }
840         else
841         {
842             [o_btn_fullscreen setEnabled: VLC_FALSE];
843         }
844         p_intf->p_sys->b_fullscreen_update = VLC_FALSE;
845     }
846
847     if( p_input && !p_input->b_die )
848     {
849         vlc_value_t val;
850
851         if( p_intf->p_sys->b_current_title_update )
852         {
853             NSString *o_temp;
854             vout_thread_t *p_vout;
855             playlist_t * p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
856                                                        FIND_ANYWHERE );
857
858             if( p_playlist == NULL )
859             {
860                 return;
861             }
862
863             vlc_mutex_lock( &p_playlist->object_lock );
864             o_temp = [NSString stringWithUTF8String:
865                 p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
866             if( o_temp == NULL )
867                 o_temp = [NSString stringWithCString:
868                     p_playlist->pp_items[p_playlist->i_index]->input.psz_name];
869             vlc_mutex_unlock( &p_playlist->object_lock );
870             [o_scrollfield setStringValue: o_temp ];
871
872
873             /*p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
874                                                     FIND_ANYWHERE );
875             if( p_vout != NULL )
876             {
877                 id o_vout_wnd;
878                 NSEnumerator * o_enum = [[NSApp orderedWindows] objectEnumerator];
879
880                 while( ( o_vout_wnd = [o_enum nextObject] ) )
881                 {
882                     if( [[o_vout_wnd className] isEqualToString: @"VLCWindow"] )
883                     {
884                         ;[o_vout_wnd updateTitle];
885                     }
886                 }
887                 vlc_object_release( (vlc_object_t *)p_vout );
888             }*/
889             [o_playlist updateRowSelection];
890             vlc_object_release( p_playlist );
891             p_intf->p_sys->b_current_title_update = FALSE;
892         }
893
894         if( p_input && [o_timeslider isEnabled] )
895         {
896             /* Update the slider */
897             vlc_value_t time;
898             NSString * o_time;
899             mtime_t i_seconds;
900             vlc_value_t pos;
901             float f_updated;
902
903             var_Get( p_input, "position", &pos );
904             f_updated = 10000. * pos.f_float;
905             [o_timeslider setFloatValue: f_updated];
906
907             var_Get( p_input, "time", &time );
908             i_seconds = time.i_time / 1000000;
909
910             o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
911                             (int) (i_seconds / (60 * 60)),
912                             (int) (i_seconds / 60 % 60),
913                             (int) (i_seconds % 60)];
914             [o_timefield setStringValue: o_time];
915         }
916
917         /* Manage volume status */
918         [self manageVolumeSlider];
919
920         /* Manage Playing status */
921         var_Get( p_input, "state", &val );
922         if( p_intf->p_sys->i_play_status != val.i_int )
923         {
924             p_intf->p_sys->i_play_status = val.i_int;
925             [self playStatusUpdated: p_intf->p_sys->i_play_status];
926         }
927     }
928     else
929     {
930         p_intf->p_sys->i_play_status = END_S;
931         p_intf->p_sys->b_intf_update = VLC_TRUE;
932         [self playStatusUpdated: p_intf->p_sys->i_play_status];
933         [self setSubmenusEnabled: FALSE];
934     }
935
936 #undef p_input
937
938     [self updateMessageArray];
939
940     [NSTimer scheduledTimerWithTimeInterval: 0.3
941         target: self selector: @selector(manageIntf:)
942         userInfo: nil repeats: FALSE];
943 }
944
945 - (void)setupMenus
946 {
947 #define p_input p_intf->p_sys->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             vlc_object_t * p_dec_obj;
989
990             [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
991                 var: "video-device" selector: @selector(toggleVar:)];
992
993             [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
994                 var: "deinterlace" selector: @selector(toggleVar:)];
995
996             p_dec_obj = (vlc_object_t *)vlc_object_find(
997                                                  (vlc_object_t *)p_vout,
998                                                  VLC_OBJECT_DECODER,
999                                                  FIND_PARENT );
1000             if ( p_dec_obj != NULL )
1001             {
1002                [o_controls setupVarMenuItem: o_mi_ffmpeg_pp target:
1003                     (vlc_object_t *)p_dec_obj var:"ffmpeg-pp-q" selector:
1004                     @selector(toggleVar:)];
1005
1006                 vlc_object_release(p_dec_obj);
1007             }
1008             vlc_object_release( (vlc_object_t *)p_vout );
1009         }
1010     }
1011 #undef p_input
1012 }
1013
1014 - (void)updateMessageArray
1015 {
1016     int i_start, i_stop;
1017     vlc_value_t quiet;
1018
1019     vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1020     i_stop = *p_intf->p_sys->p_sub->pi_stop;
1021     vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1022
1023     if( p_intf->p_sys->p_sub->i_start != i_stop )
1024     {
1025         NSColor *o_white = [NSColor whiteColor];
1026         NSColor *o_red = [NSColor redColor];
1027         NSColor *o_yellow = [NSColor yellowColor];
1028         NSColor *o_gray = [NSColor grayColor];
1029
1030         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1031         static const char * ppsz_type[4] = { ": ", " error: ",
1032                                              " warning: ", " debug: " };
1033
1034         for( i_start = p_intf->p_sys->p_sub->i_start;
1035              i_start != i_stop;
1036              i_start = (i_start+1) % VLC_MSG_QSIZE )
1037         {
1038             NSString *o_msg;
1039             NSDictionary *o_attr;
1040             NSAttributedString *o_msg_color;
1041
1042             int i_type = p_intf->p_sys->p_sub->p_msg[i_start].i_type;
1043
1044             [o_msg_lock lock];
1045
1046             if( [o_msg_arr count] + 2 > 400 )
1047             {
1048                 unsigned rid[] = { 0, 1 };
1049                 [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
1050                            numIndices: sizeof(rid)/sizeof(rid[0])];
1051             }
1052
1053             o_attr = [NSDictionary dictionaryWithObject: o_gray
1054                 forKey: NSForegroundColorAttributeName];
1055             o_msg = [NSString stringWithFormat: @"%s%s",
1056                 p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1057                 ppsz_type[i_type]];
1058             o_msg_color = [[NSAttributedString alloc]
1059                 initWithString: o_msg attributes: o_attr];
1060             [o_msg_arr addObject: [o_msg_color autorelease]];
1061
1062             o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
1063                 forKey: NSForegroundColorAttributeName];
1064             o_msg = [NSString stringWithFormat: @"%s\n",
1065                 p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1066             o_msg_color = [[NSAttributedString alloc]
1067                 initWithString: o_msg attributes: o_attr];
1068             [o_msg_arr addObject: [o_msg_color autorelease]];
1069
1070             [o_msg_lock unlock];
1071
1072             var_Get( p_intf->p_vlc, "verbose", &quiet );
1073
1074             if( i_type == 1 && quiet.i_int > -1 )
1075             {
1076                 NSString *o_my_msg = [NSString stringWithFormat: @"%s: %s\n",
1077                     p_intf->p_sys->p_sub->p_msg[i_start].psz_module,
1078                     p_intf->p_sys->p_sub->p_msg[i_start].psz_msg];
1079
1080                 NSRange s_r = NSMakeRange( [[o_err_msg string] length], 0 );
1081                 [o_err_msg setEditable: YES];
1082                 [o_err_msg setSelectedRange: s_r];
1083                 [o_err_msg insertText: o_my_msg];
1084
1085                 [o_error makeKeyAndOrderFront: self];
1086                 [o_err_msg setEditable: NO];
1087             }
1088         }
1089
1090         vlc_mutex_lock( p_intf->p_sys->p_sub->p_lock );
1091         p_intf->p_sys->p_sub->i_start = i_start;
1092         vlc_mutex_unlock( p_intf->p_sys->p_sub->p_lock );
1093     }
1094 }
1095
1096 - (void)playStatusUpdated:(int)i_status
1097 {
1098     if( i_status == PLAYING_S )
1099     {
1100         [o_btn_play setImage: o_img_pause];
1101         [o_btn_play setAlternateImage: o_img_pause_pressed];
1102         [o_btn_play setToolTip: _NS("Pause")];
1103         [o_mi_play setTitle: _NS("Pause")];
1104         [o_dmi_play setTitle: _NS("Pause")];
1105     }
1106     else
1107     {
1108         [o_btn_play setImage: o_img_play];
1109         [o_btn_play setAlternateImage: o_img_play_pressed];
1110         [o_btn_play setToolTip: _NS("Play")];
1111         [o_mi_play setTitle: _NS("Play")];
1112         [o_dmi_play setTitle: _NS("Play")];
1113     }
1114 }
1115
1116 - (void)setSubmenusEnabled:(BOOL)b_enabled
1117 {
1118     [o_mi_program setEnabled: b_enabled];
1119     [o_mi_title setEnabled: b_enabled];
1120     [o_mi_chapter setEnabled: b_enabled];
1121     [o_mi_audiotrack setEnabled: b_enabled];
1122     [o_mi_visual setEnabled: b_enabled];
1123     [o_mi_videotrack setEnabled: b_enabled];
1124     [o_mi_subtitle setEnabled: b_enabled];
1125     [o_mi_channels setEnabled: b_enabled];
1126     [o_mi_deinterlace setEnabled: b_enabled];
1127     [o_mi_ffmpeg_pp setEnabled: b_enabled];
1128     [o_mi_device setEnabled: b_enabled];
1129     [o_mi_screen setEnabled: b_enabled];
1130 }
1131
1132 - (void)manageVolumeSlider
1133 {
1134     audio_volume_t i_volume;
1135
1136     aout_VolumeGet( p_intf, &i_volume );
1137
1138     [o_volumeslider setFloatValue: (float)i_volume / AOUT_VOLUME_STEP];
1139     [o_volumeslider setEnabled: TRUE];
1140
1141     p_intf->p_sys->b_mute = ( i_volume == 0 );
1142 }
1143
1144 - (IBAction)timesliderUpdate:(id)sender
1145 {
1146 #define p_input p_intf->p_sys->p_input
1147     float f_updated;
1148
1149     switch( [[NSApp currentEvent] type] )
1150     {
1151         case NSLeftMouseUp:
1152         case NSLeftMouseDown:
1153         case NSLeftMouseDragged:
1154             f_updated = [sender floatValue];
1155             break;
1156
1157         default:
1158             return;
1159     }
1160
1161     if( p_input != NULL )
1162     {
1163         vlc_value_t time;
1164         vlc_value_t pos;
1165         mtime_t i_seconds;
1166         NSString * o_time;
1167
1168         pos.f_float = f_updated / 10000.;
1169         var_Set( p_input, "position", pos );
1170         [o_timeslider setFloatValue: f_updated];
1171
1172         var_Get( p_input, "time", &time );
1173         i_seconds = time.i_time / 1000000;
1174
1175         o_time = [NSString stringWithFormat: @"%d:%02d:%02d",
1176                         (int) (i_seconds / (60 * 60)),
1177                         (int) (i_seconds / 60 % 60),
1178                         (int) (i_seconds % 60)];
1179         [o_timefield setStringValue: o_time];
1180     }
1181 #undef p_input
1182 }
1183
1184 - (void)terminate
1185 {
1186     playlist_t * p_playlist;
1187     vout_thread_t * p_vout;
1188
1189     /* Stop playback */
1190     if( ( p_playlist = vlc_object_find( p_intf, VLC_OBJECT_PLAYLIST,
1191                                         FIND_ANYWHERE ) ) )
1192     {
1193         playlist_Stop( p_playlist );
1194         vlc_object_release( p_playlist );
1195     }
1196
1197     /* FIXME - Wait here until all vouts are terminated because
1198        libvlc's VLC_CleanUp destroys interfaces before vouts, which isn't
1199        good on OS X. We definitly need a cleaner way to handle this,
1200        but this may hopefully be good enough for now.
1201          -- titer 2003/11/22 */
1202     while( ( p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1203                                        FIND_ANYWHERE ) ) )
1204     {
1205         vlc_object_release( p_vout );
1206         msleep( 100000 );
1207     }
1208     msleep( 500000 );
1209
1210     if( o_img_pause_pressed != nil )
1211     {
1212         [o_img_pause_pressed release];
1213         o_img_pause_pressed = nil;
1214     }
1215
1216     if( o_img_pause_pressed != nil )
1217     {
1218         [o_img_pause_pressed release];
1219         o_img_pause_pressed = nil;
1220     }
1221
1222     if( o_img_pause != nil )
1223     {
1224         [o_img_pause release];
1225         o_img_pause = nil;
1226     }
1227
1228     if( o_img_play != nil )
1229     {
1230         [o_img_play release];
1231         o_img_play = nil;
1232     }
1233
1234     if( o_msg_arr != nil )
1235     {
1236         [o_msg_arr removeAllObjects];
1237         [o_msg_arr release];
1238         o_msg_arr = nil;
1239     }
1240
1241     if( o_msg_lock != nil )
1242     {
1243         [o_msg_lock release];
1244         o_msg_lock = nil;
1245     }
1246
1247     /* write cached user defaults to disk */
1248     [[NSUserDefaults standardUserDefaults] synchronize];
1249
1250     p_intf->b_die = VLC_TRUE;
1251     [NSApp stop:NULL];
1252 }
1253
1254 - (IBAction)clearRecentItems:(id)sender
1255 {
1256     [[NSDocumentController sharedDocumentController]
1257                           clearRecentDocuments: nil];
1258 }
1259
1260 - (void)openRecentItem:(id)sender
1261 {
1262     [self application: nil openFile: [sender title]];
1263 }
1264
1265 - (IBAction)viewPreferences:(id)sender
1266 {
1267     [o_prefs showPrefs];
1268 }
1269
1270 - (IBAction)closeError:(id)sender
1271 {
1272     vlc_value_t val;
1273
1274     if( [o_err_ckbk_surpress state] == NSOnState )
1275     {
1276         val.i_int = -1;
1277         var_Set( p_intf->p_vlc, "verbose", val );
1278     }
1279     [o_err_msg setString: @""];
1280     [o_error performClose: self];
1281 }
1282
1283 - (IBAction)openReadMe:(id)sender
1284 {
1285     NSString * o_path = [[NSBundle mainBundle]
1286         pathForResource: @"README.MacOSX" ofType: @"rtf"];
1287
1288     [[NSWorkspace sharedWorkspace] openFile: o_path
1289                                    withApplication: @"TextEdit"];
1290 }
1291
1292 - (IBAction)openDocumentation:(id)sender
1293 {
1294     NSURL * o_url = [NSURL URLWithString:
1295         @"http://www.videolan.org/doc/"];
1296
1297     [[NSWorkspace sharedWorkspace] openURL: o_url];
1298 }
1299
1300 - (IBAction)reportABug:(id)sender
1301 {
1302     NSURL * o_url = [NSURL URLWithString:
1303         @"http://www.videolan.org/support/bug-reporting.html"];
1304
1305     [[NSWorkspace sharedWorkspace] openURL: o_url];
1306 }
1307
1308 - (IBAction)openWebsite:(id)sender
1309 {
1310     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
1311
1312     [[NSWorkspace sharedWorkspace] openURL: o_url];
1313 }
1314
1315 - (IBAction)openLicense:(id)sender
1316 {
1317     NSString * o_path = [[NSBundle mainBundle]
1318         pathForResource: @"COPYING" ofType: nil];
1319
1320     [[NSWorkspace sharedWorkspace] openFile: o_path
1321                                    withApplication: @"TextEdit"];
1322 }
1323
1324 - (IBAction)openCrashLog:(id)sender
1325 {
1326     NSString * o_path = [@"~/Library/Logs/CrashReporter/VLC.crash.log"
1327                                     stringByExpandingTildeInPath];
1328
1329
1330     if ( [[NSFileManager defaultManager] fileExistsAtPath: o_path ] )
1331     {
1332         [[NSWorkspace sharedWorkspace] openFile: o_path
1333                                     withApplication: @"Console"];
1334     }
1335     else
1336     {
1337         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.") );
1338
1339     }
1340 }
1341
1342 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1343 {
1344     if( [o_notification object] == o_msgs_panel )
1345     {
1346         id o_msg;
1347         NSEnumerator * o_enum;
1348
1349         [o_messages setString: @""];
1350
1351         [o_msg_lock lock];
1352
1353         o_enum = [o_msg_arr objectEnumerator];
1354
1355         while( ( o_msg = [o_enum nextObject] ) != nil )
1356         {
1357             [o_messages insertText: o_msg];
1358         }
1359
1360         [o_msg_lock unlock];
1361     }
1362 }
1363
1364 @end
1365
1366 @implementation VLCMain (NSMenuValidation)
1367
1368 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1369 {
1370     NSString *o_title = [o_mi title];
1371     BOOL bEnabled = TRUE;
1372
1373     if( [o_title isEqualToString: _NS("License")] )
1374     {
1375         /* we need to do this only once */
1376         [self setupMenus];
1377     }
1378
1379     /* Recent Items Menu */
1380     if( [o_title isEqualToString: _NS("Clear Menu")] )
1381     {
1382         NSMenu * o_menu = [o_mi_open_recent submenu];
1383         int i_nb_items = [o_menu numberOfItems];
1384         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
1385                                                        recentDocumentURLs];
1386         UInt32 i_nb_docs = [o_docs count];
1387
1388         if( i_nb_items > 1 )
1389         {
1390             while( --i_nb_items )
1391             {
1392                 [o_menu removeItemAtIndex: 0];
1393             }
1394         }
1395
1396         if( i_nb_docs > 0 )
1397         {
1398             NSURL * o_url;
1399             NSString * o_doc;
1400
1401             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
1402
1403             while( TRUE )
1404             {
1405                 i_nb_docs--;
1406
1407                 o_url = [o_docs objectAtIndex: i_nb_docs];
1408
1409                 if( [o_url isFileURL] )
1410                 {
1411                     o_doc = [o_url path];
1412                 }
1413                 else
1414                 {
1415                     o_doc = [o_url absoluteString];
1416                 }
1417
1418                 [o_menu insertItemWithTitle: o_doc
1419                     action: @selector(openRecentItem:)
1420                     keyEquivalent: @"" atIndex: 0];
1421
1422                 if( i_nb_docs == 0 )
1423                 {
1424                     break;
1425                 }
1426             }
1427         }
1428         else
1429         {
1430             bEnabled = FALSE;
1431         }
1432     }
1433     return( bEnabled );
1434 }
1435
1436 @end
1437
1438 @implementation VLCMain (Internal)
1439
1440 - (void)handlePortMessage:(NSPortMessage *)o_msg
1441 {
1442     id ** val;
1443     NSData * o_data;
1444     NSValue * o_value;
1445     NSInvocation * o_inv;
1446     NSConditionLock * o_lock;
1447
1448     o_data = [[o_msg components] lastObject];
1449     o_inv = *((NSInvocation **)[o_data bytes]);
1450     [o_inv getArgument: &o_value atIndex: 2];
1451     val = (id **)[o_value pointerValue];
1452     [o_inv setArgument: val[1] atIndex: 2];
1453     o_lock = *(val[0]);
1454
1455     [o_lock lock];
1456     [o_inv invoke];
1457     [o_lock unlockWithCondition: 1];
1458 }
1459
1460 @end