]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
macosx: remove the interaction code from intf.m and add a draft for dialog-fatal...
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2009 the VideoLAN team
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  *          Felix Paul Kühne <fkuehne at videolan dot org>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <stdlib.h>                                      /* malloc(), free() */
31 #include <sys/param.h>                                    /* for MAXPATHLEN */
32 #include <string.h>
33 #include <vlc_common.h>
34 #include <vlc_keys.h>
35 #include <vlc_dialog.h>
36 #include <unistd.h> /* execl() */
37 #import <vlc_dialog.h>
38
39 #import "intf.h"
40 #import "fspanel.h"
41 #import "vout.h"
42 #import "prefs.h"
43 #import "playlist.h"
44 #import "playlistinfo.h"
45 #import "controls.h"
46 #import "about.h"
47 #import "open.h"
48 #import "wizard.h"
49 #import "extended.h"
50 #import "bookmarks.h"
51 #import "interaction.h"
52 #import "embeddedwindow.h"
53 #import "update.h"
54 #import "AppleRemote.h"
55 #import "eyetv.h"
56 #import "simple_prefs.h"
57 #import "vlm.h"
58
59 #import <AddressBook/AddressBook.h>
60
61 /*****************************************************************************
62  * Local prototypes.
63  *****************************************************************************/
64 static void Run ( intf_thread_t *p_intf );
65
66 static void * ManageThread( void *user_data );
67
68 static unichar VLCKeyToCocoa( unsigned int i_key );
69 static unsigned int VLCModifiersToCocoa( unsigned int i_key );
70
71 #pragma mark -
72 #pragma mark VLC Interface Object Callbacks
73
74 /*****************************************************************************
75  * OpenIntf: initialize interface
76  *****************************************************************************/
77 int OpenIntf ( vlc_object_t *p_this )
78 {
79     intf_thread_t *p_intf = (intf_thread_t*) p_this;
80
81     p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
82     if( p_intf->p_sys == NULL )
83         return VLC_ENOMEM;
84
85     memset( p_intf->p_sys, 0, sizeof( *p_intf->p_sys ) );
86
87     p_intf->p_sys->o_pool = [[NSAutoreleasePool alloc] init];
88
89     /* subscribe to LibVLCCore's messages */
90     p_intf->p_sys->p_sub = msg_Subscribe( p_intf->p_libvlc, MsgCallback, NULL );
91     p_intf->pf_run = Run;
92     p_intf->b_should_run_on_first_thread = true;
93
94     return VLC_SUCCESS;
95 }
96
97 /*****************************************************************************
98  * CloseIntf: destroy interface
99  *****************************************************************************/
100 void CloseIntf ( vlc_object_t *p_this )
101 {
102     intf_thread_t *p_intf = (intf_thread_t*) p_this;
103
104     [p_intf->p_sys->o_pool release];
105
106     free( p_intf->p_sys );
107 }
108
109 /*****************************************************************************
110  * Run: main loop
111  *****************************************************************************/
112 jmp_buf jmpbuffer;
113
114 static void Run( intf_thread_t *p_intf )
115 {
116     sigset_t set;
117
118     /* Do it again - for some unknown reason, vlc_thread_create() often
119      * fails to go to real-time priority with the first launched thread
120      * (???) --Meuuh */
121     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
122
123     /* Make sure the "force quit" menu item does quit instantly.
124      * VLC overrides SIGTERM which is sent by the "force quit"
125      * menu item to make sure deamon mode quits gracefully, so
126      * we un-override SIGTERM here. */
127     sigemptyset( &set );
128     sigaddset( &set, SIGTERM );
129     pthread_sigmask( SIG_UNBLOCK, &set, NULL );
130
131     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
132
133     /* Install a jmpbuffer to where we can go back before the NSApp exit
134      * see applicationWillTerminate: */
135     [NSApplication sharedApplication];
136
137     [[VLCMain sharedInstance] setIntf: p_intf];
138     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
139
140     /* Install a jmpbuffer to where we can go back before the NSApp exit
141      * see applicationWillTerminate: */
142     if(setjmp(jmpbuffer) == 0)
143         [NSApp run];
144     
145     [o_pool release];
146 }
147
148 #pragma mark -
149 #pragma mark Variables Callback
150
151 /*****************************************************************************
152  * MsgCallback: Callback triggered by the core once a new debug message is
153  * ready to be displayed. We store everything in a NSArray in our Cocoa part
154  * of this file, so we are forwarding everything through notifications.
155  *****************************************************************************/
156 static void MsgCallback( msg_cb_data_t *data, msg_item_t *item, unsigned int i )
157 {
158     int canc = vlc_savecancel();
159     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
160     
161     NSDictionary *o_dict = [NSDictionary dictionaryWithObjects: 
162                             [NSArray arrayWithObjects: 
163                              [NSString stringWithUTF8String: item->psz_module],
164                              [NSString stringWithUTF8String: item->psz_msg],
165                              [NSNumber numberWithInt: item->i_type], nil] 
166                                                        forKeys:
167                             [NSArray arrayWithObjects: @"Module", @"Message", @"Type", nil]];
168     
169     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCCoreMessageReceived" 
170                                                         object: nil 
171                                                       userInfo: o_dict];
172     
173     [o_pool release];
174     vlc_restorecancel( canc );
175 }
176
177
178 /*****************************************************************************
179  * playlistChanged: Callback triggered by the intf-change playlist
180  * variable, to let the intf update the playlist.
181  *****************************************************************************/
182 static int PlaylistChanged( vlc_object_t *p_this, const char *psz_variable,
183                      vlc_value_t old_val, vlc_value_t new_val, void *param )
184 {
185     intf_thread_t * p_intf = VLCIntf;
186     if( p_intf && p_intf->p_sys )
187     {
188         p_intf->p_sys->b_intf_update = true;
189         p_intf->p_sys->b_playlist_update = true;
190         p_intf->p_sys->b_playmode_update = true;
191         p_intf->p_sys->b_current_title_update = true;
192     }
193     return VLC_SUCCESS;
194 }
195
196 /*****************************************************************************
197  * ShowController: Callback triggered by the show-intf playlist variable
198  * through the ShowIntf-control-intf, to let us show the controller-win;
199  * usually when in fullscreen-mode
200  *****************************************************************************/
201 static int ShowController( vlc_object_t *p_this, const char *psz_variable,
202                      vlc_value_t old_val, vlc_value_t new_val, void *param )
203 {
204     intf_thread_t * p_intf = VLCIntf;
205     if( p_intf && p_intf->p_sys )
206         p_intf->p_sys->b_intf_show = true;
207     return VLC_SUCCESS;
208 }
209
210 /*****************************************************************************
211  * FullscreenChanged: Callback triggered by the fullscreen-change playlist
212  * variable, to let the intf update the controller.
213  *****************************************************************************/
214 static int FullscreenChanged( vlc_object_t *p_this, const char *psz_variable,
215                      vlc_value_t old_val, vlc_value_t new_val, void *param )
216 {
217     intf_thread_t * p_intf = VLCIntf;
218     if( p_intf && p_intf->p_sys )
219         p_intf->p_sys->b_fullscreen_update = true;
220     return VLC_SUCCESS;
221 }
222
223 /*****************************************************************************
224  * DialogCallback: Callback triggered by the "dialog-*" variables 
225  * to let the intf display error and interaction dialogs
226  *****************************************************************************/
227 static int DialogCallback( vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data )
228 {
229     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
230     VLCMain *interface = (VLCMain *)data;
231     
232     NSLog( @"dialog callback triggered; type of dialogue is '%s'", type );
233     if(!strcmp (type, "dialog-fatal"))
234     {
235         const dialog_fatal_t *p_dialog = (const dialog_fatal_t *)value.p_address;
236         NSLog( @"fatal dialogue with title '%s' and message '%s'", p_dialog->title, p_dialog->message );
237 #if 0
238         NSValue *o_value = [NSValue valueWithPointer:p_dialog];
239
240         [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCNewInteractionEventNotification" object:[interface getInteractionList] userInfo:[NSDictionary dictionaryWithObject:o_value forKey:@"VLCDialogPointer"]];
241 #endif
242     }
243
244     [o_pool release];
245     return VLC_SUCCESS;
246 }
247
248 #pragma mark -
249 #pragma mark Private
250
251 @interface VLCMain ()
252 - (void)_removeOldPreferences;
253 @end
254
255 /*****************************************************************************
256  * VLCMain implementation
257  *****************************************************************************/
258 @implementation VLCMain
259
260 #pragma mark -
261 #pragma mark Initialization
262
263 static VLCMain *_o_sharedMainInstance = nil;
264
265 + (VLCMain *)sharedInstance
266 {
267     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
268 }
269
270 - (id)init
271 {
272     if( _o_sharedMainInstance) 
273     {
274         [self dealloc];
275         return _o_sharedMainInstance;
276     } 
277     else
278         _o_sharedMainInstance = [super init];
279
280     o_msg_lock = [[NSLock alloc] init];
281     o_msg_arr = [[NSMutableArray arrayWithCapacity: 200] retain];
282     /* subscribe to LibVLC's debug messages as early as possible (for us) */
283     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(libvlcMessageReceived:) name: @"VLCCoreMessageReceived" object: nil];
284     
285     o_about = [[VLAboutBox alloc] init];
286     o_prefs = nil;
287     o_open = [[VLCOpen alloc] init];
288     o_wizard = [[VLCWizard alloc] init];
289     o_vlm = [[VLCVLMController alloc] init];
290     o_extended = nil;
291     o_bookmarks = [[VLCBookmarks alloc] init];
292     o_embedded_list = [[VLCEmbeddedList alloc] init];
293     o_interaction_list = [[VLCInteractionList alloc] init];
294     o_info = [[VLCInfo alloc] init];
295 #ifdef UPDATE_CHECK
296     o_update = [[VLCUpdate alloc] init];
297 #endif
298
299     i_lastShownVolume = -1;
300
301     o_remote = [[AppleRemote alloc] init];
302     [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
303     [o_remote setDelegate: _o_sharedMainInstance];
304
305     o_eyetv = [[VLCEyeTVController alloc] init];
306
307     /* announce our launch to a potential eyetv plugin */
308     [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
309                                                                    object: @"VLCEyeTVSupport"
310                                                                  userInfo: NULL
311                                                        deliverImmediately: YES];
312
313     return _o_sharedMainInstance;
314 }
315
316 - (void)setIntf: (intf_thread_t *)p_mainintf {
317     p_intf = p_mainintf;
318 }
319
320 - (intf_thread_t *)getIntf {
321     return p_intf;
322 }
323
324 - (void)awakeFromNib
325 {
326     unsigned int i_key = 0;
327     playlist_t *p_playlist;
328     vlc_value_t val;
329
330     /* Check if we already did this once. Opening the other nibs calls it too, because VLCMain is the owner */
331     if( nib_main_loaded ) return;
332
333     /* check whether the user runs a valid version of OS X */
334     if( MACOS_VERSION < 10.5f )
335     {
336         NSAlert *ourAlert;
337         int i_returnValue;
338         NSString *o_blabla;
339         if( MACOS_VERSION == 10.4f )
340             o_blabla = _NS("VLC's last release for your OS is the 0.9 series." );
341         else if( MACOS_VERSION == 10.3f )
342             o_blabla = _NS("VLC's last release for your OS is VLC 0.8.6i, which is prone to known security issues." );
343         else // 10.2 and 10.1, still 3% of the OS X market share
344             o_blabla = _NS("VLC's last release for your OS is VLC 0.7.2, which is highly out of date and prone to " \
345                          "known security issues. We recommend you to update your Mac to a modern version of Mac OS X.");
346         ourAlert = [NSAlert alertWithMessageText: _NS("Your version of Mac OS X is no longer supported")
347                                    defaultButton: _NS("Quit")
348                                  alternateButton: NULL
349                                      otherButton: NULL
350                        informativeTextWithFormat: _NS("VLC media player %s requires Mac OS X 10.5 or higher.\n\n%@"), VLC_Version(), o_blabla];
351         [ourAlert setAlertStyle: NSCriticalAlertStyle];
352         i_returnValue = [ourAlert runModal];
353         [NSApp performSelectorOnMainThread: @selector(terminate:) withObject:nil waitUntilDone:NO];
354         return;
355     }
356
357     [self initStrings];
358
359     [o_window setExcludedFromWindowsMenu: YES];
360     [o_msgs_panel setExcludedFromWindowsMenu: YES];
361     [o_msgs_panel setDelegate: self];
362
363     /* In code and not in Nib for 10.4 compat */
364     NSToolbar * toolbar = [[[NSToolbar alloc] initWithIdentifier:@"mainControllerToolbar"] autorelease];
365     [toolbar setDelegate:self];
366     [toolbar setShowsBaselineSeparator:NO];
367     [toolbar setAllowsUserCustomization:NO];
368     [toolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
369     [toolbar setAutosavesConfiguration:YES];
370     [o_window setToolbar:toolbar];
371
372     i_key = config_GetInt( p_intf, "key-quit" );
373     [o_mi_quit setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
374     [o_mi_quit setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
375     i_key = config_GetInt( p_intf, "key-play-pause" );
376     [o_mi_play setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
377     [o_mi_play setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
378     i_key = config_GetInt( p_intf, "key-stop" );
379     [o_mi_stop setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
380     [o_mi_stop setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
381     i_key = config_GetInt( p_intf, "key-faster" );
382     [o_mi_faster setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
383     [o_mi_faster setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
384     i_key = config_GetInt( p_intf, "key-slower" );
385     [o_mi_slower setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
386     [o_mi_slower setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
387     i_key = config_GetInt( p_intf, "key-prev" );
388     [o_mi_previous setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
389     [o_mi_previous setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
390     i_key = config_GetInt( p_intf, "key-next" );
391     [o_mi_next setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
392     [o_mi_next setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
393     i_key = config_GetInt( p_intf, "key-jump+short" );
394     [o_mi_fwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
395     [o_mi_fwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
396     i_key = config_GetInt( p_intf, "key-jump-short" );
397     [o_mi_bwd setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
398     [o_mi_bwd setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
399     i_key = config_GetInt( p_intf, "key-jump+medium" );
400     [o_mi_fwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
401     [o_mi_fwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
402     i_key = config_GetInt( p_intf, "key-jump-medium" );
403     [o_mi_bwd1m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
404     [o_mi_bwd1m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
405     i_key = config_GetInt( p_intf, "key-jump+long" );
406     [o_mi_fwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
407     [o_mi_fwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
408     i_key = config_GetInt( p_intf, "key-jump-long" );
409     [o_mi_bwd5m setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
410     [o_mi_bwd5m setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
411     i_key = config_GetInt( p_intf, "key-vol-up" );
412     [o_mi_vol_up setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
413     [o_mi_vol_up setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
414     i_key = config_GetInt( p_intf, "key-vol-down" );
415     [o_mi_vol_down setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
416     [o_mi_vol_down setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
417     i_key = config_GetInt( p_intf, "key-vol-mute" );
418     [o_mi_mute setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
419     [o_mi_mute setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
420     i_key = config_GetInt( p_intf, "key-fullscreen" );
421     [o_mi_fullscreen setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
422     [o_mi_fullscreen setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
423     i_key = config_GetInt( p_intf, "key-snapshot" );
424     [o_mi_snapshot setKeyEquivalent: [NSString stringWithFormat:@"%C", VLCKeyToCocoa( i_key )]];
425     [o_mi_snapshot setKeyEquivalentModifierMask: VLCModifiersToCocoa(i_key)];
426
427     var_Create( p_intf, "intf-change", VLC_VAR_BOOL );
428
429     [self setSubmenusEnabled: FALSE];
430     [o_volumeslider setEnabled: YES];
431     [self manageVolumeSlider];
432     [o_window setDelegate: self];
433  
434     b_restore_size = false;
435
436     // Set that here as IB seems to be buggy
437     [o_window setContentMinSize:NSMakeSize(338., 30.)];
438
439     if( [o_window contentRectForFrameRect:[o_window frame]].size.height <= 169. )
440     {
441         b_small_window = YES;
442         [o_window setFrame: NSMakeRect( [o_window frame].origin.x,
443             [o_window frame].origin.y, [o_window frame].size.width,
444             [o_window minSize].height ) display: YES animate:YES];
445         [o_playlist_view setAutoresizesSubviews: NO];
446     }
447     else
448     {
449         b_small_window = NO;
450         NSRect contentRect = [o_window contentRectForFrameRect:[o_window frame]];
451         [o_playlist_view setFrame: NSMakeRect( 0, 0, contentRect.size.width, contentRect.size.height - [o_window contentMinSize].height )];
452         [o_playlist_view setNeedsDisplay:YES];
453         [o_playlist_view setAutoresizesSubviews: YES];
454         [[o_window contentView] addSubview: o_playlist_view];
455     }
456
457     [self updateTogglePlaylistState];
458
459     o_size_with_playlist = [o_window contentRectForFrameRect:[o_window frame]].size;
460
461     p_playlist = pl_Hold( p_intf );
462
463     var_Create( p_playlist, "fullscreen", VLC_VAR_BOOL | VLC_VAR_DOINHERIT);
464     val.b_bool = false;
465
466     var_AddCallback( p_playlist, "fullscreen", FullscreenChanged, self);
467     var_AddCallback( p_intf->p_libvlc, "intf-show", ShowController, self);
468
469     pl_Release( p_intf );
470
471     /* subscribe to various interactive dialogues */
472     var_Create( p_intf, "dialog-fatal", VLC_VAR_ADDRESS );
473     var_AddCallback( p_intf, "dialog-fatal", DialogCallback, self );
474     dialog_Register( p_intf );
475
476     /* update the playmode stuff */
477     p_intf->p_sys->b_playmode_update = true;
478
479     [[NSNotificationCenter defaultCenter] addObserver: self
480                                              selector: @selector(refreshVoutDeviceMenu:)
481                                                  name: NSApplicationDidChangeScreenParametersNotification
482                                                object: nil];
483
484     /* take care of tint changes during runtime */
485     o_img_play = [NSImage imageNamed: @"play"];
486     o_img_pause = [NSImage imageNamed: @"pause"];    
487     [self controlTintChanged];
488     [[NSNotificationCenter defaultCenter] addObserver: self
489                                              selector: @selector( controlTintChanged )
490                                                  name: NSControlTintDidChangeNotification
491                                                object: nil];
492
493     /* yeah, we are done */
494     nib_main_loaded = TRUE;
495 }
496
497 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
498 {
499     /* FIXME: don't poll */
500     interfaceTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.5
501                                      target: self selector: @selector(manageIntf:)
502                                    userInfo: nil repeats: FALSE] retain];
503
504     /* Note: we use the pthread API to support pre-10.5 */
505     pthread_create( &manage_thread, NULL, ManageThread, self );
506
507     [o_controls setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
508         var: "intf-add" selector: @selector(toggleVar:)];
509
510     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
511 }
512
513 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
514 {
515     [self _removeOldPreferences];
516
517 #ifdef UPDATE_CHECK
518     /* Check for update silently on startup */
519     if( !nib_update_loaded )
520         nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner: NSApp];
521
522     if([o_update shouldCheckForUpdate])
523         [NSThread detachNewThreadSelector:@selector(checkForUpdate) toTarget:o_update withObject:nil];
524 #endif
525
526     /* Handle sleep notification */
527     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
528            name:NSWorkspaceWillSleepNotification object:nil];
529
530     [NSThread detachNewThreadSelector:@selector(lookForCrashLog) toTarget:self withObject:nil];
531 }
532
533 - (void)initStrings
534 {
535     [o_window setTitle: _NS("VLC media player")];
536     [self setScrollField:_NS("VLC media player") stopAfter:-1];
537
538     /* button controls */
539     [o_btn_prev setToolTip: _NS("Previous")];
540     [o_btn_rewind setToolTip: _NS("Rewind")];
541     [o_btn_play setToolTip: _NS("Play")];
542     [o_btn_stop setToolTip: _NS("Stop")];
543     [o_btn_ff setToolTip: _NS("Fast Forward")];
544     [o_btn_next setToolTip: _NS("Next")];
545     [o_btn_fullscreen setToolTip: _NS("Fullscreen")];
546     [o_volumeslider setToolTip: _NS("Volume")];
547     [o_timeslider setToolTip: _NS("Position")];
548     [o_btn_playlist setToolTip: _NS("Playlist")];
549
550     /* messages panel */
551     [o_msgs_panel setTitle: _NS("Messages")];
552     [o_msgs_crashlog_btn setTitle: _NS("Open CrashLog...")];
553     [o_msgs_save_btn setTitle: _NS("Save this Log...")];
554
555     /* main menu */
556     [o_mi_about setTitle: [_NS("About VLC media player") \
557         stringByAppendingString: @"..."]];
558     [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
559     [o_mi_prefs setTitle: _NS("Preferences...")];
560     [o_mi_add_intf setTitle: _NS("Add Interface")];
561     [o_mu_add_intf setTitle: _NS("Add Interface")];
562     [o_mi_services setTitle: _NS("Services")];
563     [o_mi_hide setTitle: _NS("Hide VLC")];
564     [o_mi_hide_others setTitle: _NS("Hide Others")];
565     [o_mi_show_all setTitle: _NS("Show All")];
566     [o_mi_quit setTitle: _NS("Quit VLC")];
567
568     [o_mu_file setTitle: _ANS("1:File")];
569     [o_mi_open_generic setTitle: _NS("Advanced Open File...")];
570     [o_mi_open_file setTitle: _NS("Open File...")];
571     [o_mi_open_disc setTitle: _NS("Open Disc...")];
572     [o_mi_open_net setTitle: _NS("Open Network...")];
573     [o_mi_open_capture setTitle: _NS("Open Capture Device...")];
574     [o_mi_open_recent setTitle: _NS("Open Recent")];
575     [o_mi_open_recent_cm setTitle: _NS("Clear Menu")];
576     [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
577
578     [o_mu_edit setTitle: _NS("Edit")];
579     [o_mi_cut setTitle: _NS("Cut")];
580     [o_mi_copy setTitle: _NS("Copy")];
581     [o_mi_paste setTitle: _NS("Paste")];
582     [o_mi_clear setTitle: _NS("Clear")];
583     [o_mi_select_all setTitle: _NS("Select All")];
584
585     [o_mu_controls setTitle: _NS("Playback")];
586     [o_mi_play setTitle: _NS("Play")];
587     [o_mi_stop setTitle: _NS("Stop")];
588     [o_mi_faster setTitle: _NS("Faster")];
589     [o_mi_slower setTitle: _NS("Slower")];
590     [o_mi_previous setTitle: _NS("Previous")];
591     [o_mi_next setTitle: _NS("Next")];
592     [o_mi_random setTitle: _NS("Random")];
593     [o_mi_repeat setTitle: _NS("Repeat One")];
594     [o_mi_loop setTitle: _NS("Repeat All")];
595     [o_mi_fwd setTitle: _NS("Step Forward")];
596     [o_mi_bwd setTitle: _NS("Step Backward")];
597
598     [o_mi_program setTitle: _NS("Program")];
599     [o_mu_program setTitle: _NS("Program")];
600     [o_mi_title setTitle: _NS("Title")];
601     [o_mu_title setTitle: _NS("Title")];
602     [o_mi_chapter setTitle: _NS("Chapter")];
603     [o_mu_chapter setTitle: _NS("Chapter")];
604
605     [o_mu_audio setTitle: _NS("Audio")];
606     [o_mi_vol_up setTitle: _NS("Increase Volume")];
607     [o_mi_vol_down setTitle: _NS("Decrease Volume")];
608     [o_mi_mute setTitle: _NS("Mute")];
609     [o_mi_audiotrack setTitle: _NS("Audio Track")];
610     [o_mu_audiotrack setTitle: _NS("Audio Track")];
611     [o_mi_channels setTitle: _NS("Audio Channels")];
612     [o_mu_channels setTitle: _NS("Audio Channels")];
613     [o_mi_device setTitle: _NS("Audio Device")];
614     [o_mu_device setTitle: _NS("Audio Device")];
615     [o_mi_visual setTitle: _NS("Visualizations")];
616     [o_mu_visual setTitle: _NS("Visualizations")];
617
618     [o_mu_video setTitle: _NS("Video")];
619     [o_mi_half_window setTitle: _NS("Half Size")];
620     [o_mi_normal_window setTitle: _NS("Normal Size")];
621     [o_mi_double_window setTitle: _NS("Double Size")];
622     [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
623     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
624     [o_mi_floatontop setTitle: _NS("Float on Top")];
625     [o_mi_snapshot setTitle: _NS("Snapshot")];
626     [o_mi_videotrack setTitle: _NS("Video Track")];
627     [o_mu_videotrack setTitle: _NS("Video Track")];
628     [o_mi_aspect_ratio setTitle: _NS("Aspect-ratio")];
629     [o_mu_aspect_ratio setTitle: _NS("Aspect-ratio")];
630     [o_mi_crop setTitle: _NS("Crop")];
631     [o_mu_crop setTitle: _NS("Crop")];
632     [o_mi_screen setTitle: _NS("Fullscreen Video Device")];
633     [o_mu_screen setTitle: _NS("Fullscreen Video Device")];
634     [o_mi_subtitle setTitle: _NS("Subtitles Track")];
635     [o_mu_subtitle setTitle: _NS("Subtitles Track")];
636     [o_mi_addSub setTitle: _NS("Open File...")];
637     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
638     [o_mu_deinterlace setTitle: _NS("Deinterlace")];
639     [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
640     [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
641     [o_mi_teletext setTitle: _NS("Teletext")];
642     [o_mi_teletext_transparent setTitle: _NS("Transparent")];
643     [o_mi_teletext_index setTitle: _NS("Index")];
644     [o_mi_teletext_red setTitle: _NS("Red")];
645     [o_mi_teletext_green setTitle: _NS("Green")];
646     [o_mi_teletext_yellow setTitle: _NS("Yellow")];
647     [o_mi_teletext_blue setTitle: _NS("Blue")];
648
649     [o_mu_window setTitle: _NS("Window")];
650     [o_mi_minimize setTitle: _NS("Minimize Window")];
651     [o_mi_close_window setTitle: _NS("Close Window")];
652     [o_mi_controller setTitle: _NS("Controller...")];
653     [o_mi_equalizer setTitle: _NS("Equalizer...")];
654     [o_mi_extended setTitle: _NS("Extended Controls...")];
655     [o_mi_bookmarks setTitle: _NS("Bookmarks...")];
656     [o_mi_playlist setTitle: _NS("Playlist...")];
657     [o_mi_info setTitle: _NS("Media Information...")];
658     [o_mi_messages setTitle: _NS("Messages...")];
659     [o_mi_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
660
661     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
662
663     [o_mu_help setTitle: _NS("Help")];
664     [o_mi_help setTitle: _NS("VLC media player Help...")];
665     [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
666     [o_mi_license setTitle: _NS("License")];
667     [o_mi_documentation setTitle: _NS("Online Documentation...")];
668     [o_mi_website setTitle: _NS("VideoLAN Website...")];
669     [o_mi_donation setTitle: _NS("Make a donation...")];
670     [o_mi_forum setTitle: _NS("Online Forum...")];
671
672     /* dock menu */
673     [o_dmi_play setTitle: _NS("Play")];
674     [o_dmi_stop setTitle: _NS("Stop")];
675     [o_dmi_next setTitle: _NS("Next")];
676     [o_dmi_previous setTitle: _NS("Previous")];
677     [o_dmi_mute setTitle: _NS("Mute")];
678  
679     /* vout menu */
680     [o_vmi_play setTitle: _NS("Play")];
681     [o_vmi_stop setTitle: _NS("Stop")];
682     [o_vmi_prev setTitle: _NS("Previous")];
683     [o_vmi_next setTitle: _NS("Next")];
684     [o_vmi_volup setTitle: _NS("Volume Up")];
685     [o_vmi_voldown setTitle: _NS("Volume Down")];
686     [o_vmi_mute setTitle: _NS("Mute")];
687     [o_vmi_fullscreen setTitle: _NS("Fullscreen")];
688     [o_vmi_snapshot setTitle: _NS("Snapshot")];
689
690     /* crash reporter panel */
691     [o_crashrep_send_btn setTitle: _NS("Send")];
692     [o_crashrep_dontSend_btn setTitle: _NS("Don't Send")];
693     [o_crashrep_title_txt setStringValue: _NS("VLC crashed previously")];
694     [o_crashrep_win setTitle: _NS("VLC crashed previously")];
695     [o_crashrep_desc_txt setStringValue: _NS("Do you want to send details on the crash to VLC's development team?\n\nIf you want, you can enter a few lines on what you did before VLC crashed along with other helpful information: a link to download a sample file, a URL of a network stream, ...")];
696     [o_crashrep_includeEmail_ckb setTitle: _NS("I agree to be possibly contacted about this bugreport.")];
697     [o_crashrep_includeEmail_txt setStringValue: _NS("Only your default E-Mail address will be submitted, including no further information.")];
698 }
699
700 #pragma mark -
701 #pragma mark Termination
702
703 - (void)applicationWillTerminate:(NSNotification *)notification
704 {
705     playlist_t * p_playlist;
706     vout_thread_t * p_vout;
707     int returnedValue = 0;
708  
709     msg_Dbg( p_intf, "Terminating" );
710
711     /* Make sure the manage_thread won't call -terminate: again */
712     pthread_cancel( manage_thread );
713
714     /* Make sure the intf object is getting killed */
715     vlc_object_kill( p_intf );
716
717     /* Make sure our manage_thread ends */
718     pthread_join( manage_thread, NULL );
719
720     /* Make sure the interfaceTimer is destroyed */
721     [interfaceTimer invalidate];
722     [interfaceTimer release];
723     interfaceTimer = nil;
724
725     /* make sure that the current volume is saved */
726     config_PutInt( p_intf->p_libvlc, "volume", i_lastShownVolume );
727
728     /* save the prefs if they were changed in the extended panel */
729     if(o_extended && [o_extended getConfigChanged])
730     {
731         [o_extended savePrefs];
732     }
733
734     /* unsubscribe from the interactive dialogues */
735     dialog_Unregister( p_intf );
736     var_DelCallback( p_intf, "dialog-fatal", DialogCallback, self );
737
738     /* remove global observer watching for vout device changes correctly */
739     [[NSNotificationCenter defaultCenter] removeObserver: self];
740
741     [o_update end];
742
743     /* release some other objects here, because it isn't sure whether dealloc
744      * will be called later on */
745     if( nib_about_loaded )
746         [o_about release];
747
748     if( nib_prefs_loaded )
749     {
750         [o_sprefs release];
751         [o_prefs release];
752     }
753
754     if( nib_open_loaded )
755         [o_open release];
756
757     if( nib_extended_loaded )
758     {
759         [o_extended release];
760     }
761
762     if( nib_bookmarks_loaded )
763         [o_bookmarks release];
764
765     if( o_info )
766     {
767         [o_info stopTimers];
768         [o_info release];
769     }
770
771     if( nib_wizard_loaded )
772         [o_wizard release];
773
774     [crashLogURLConnection cancel];
775     [crashLogURLConnection release];
776  
777     [o_embedded_list release];
778     [o_interaction_list release];
779     [o_eyetv release];
780
781     [o_img_pause_pressed release];
782     [o_img_play_pressed release];
783     [o_img_pause release];
784     [o_img_play release];
785
786     /* unsubscribe from libvlc's debug messages */
787     msg_Unsubscribe( p_intf->p_sys->p_sub );
788
789     [o_msg_arr removeAllObjects];
790     [o_msg_arr release];
791
792     [o_msg_lock release];
793
794     /* write cached user defaults to disk */
795     [[NSUserDefaults standardUserDefaults] synchronize];
796
797     /* Kill the playlist, so that it doesn't accept new request
798      * such as the play request from vlc.c (we are a blocking interface). */
799     p_playlist = pl_Hold( p_intf );
800     vlc_object_kill( p_playlist );
801     pl_Release( p_intf );
802
803     libvlc_Quit( p_intf->p_libvlc );
804
805     [self setIntf:nil];
806
807     /* Go back to Run() and make libvlc exit properly */
808     if( jmpbuffer )
809         longjmp( jmpbuffer, 1 );
810     /* not reached */
811 }
812
813 #pragma mark -
814 #pragma mark Toolbar delegate
815
816 /* Our item identifiers */
817 static NSString * VLCToolbarMediaControl     = @"VLCToolbarMediaControl";
818
819 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar *)toolbar
820 {
821     return [NSArray arrayWithObjects:
822 //                        NSToolbarCustomizeToolbarItemIdentifier,
823 //                        NSToolbarFlexibleSpaceItemIdentifier,
824 //                        NSToolbarSpaceItemIdentifier,
825 //                        NSToolbarSeparatorItemIdentifier,
826                         VLCToolbarMediaControl,
827                         nil ];
828 }
829
830 - (NSArray *) toolbarDefaultItemIdentifiers: (NSToolbar *) toolbar
831 {
832     return [NSArray arrayWithObjects:
833                         VLCToolbarMediaControl,
834                         nil ];
835 }
836
837 - (NSToolbarItem *) toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
838 {
839     NSToolbarItem *toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: itemIdentifier] autorelease];
840
841  
842     if( [itemIdentifier isEqual: VLCToolbarMediaControl] )
843     {
844         [toolbarItem setLabel:@"Media Controls"];
845         [toolbarItem setPaletteLabel:@"Media Controls"];
846
847         NSSize size = toolbarMediaControl.frame.size;
848         [toolbarItem setView:toolbarMediaControl];
849         [toolbarItem setMinSize:size];
850         size.width += 1000.;
851         [toolbarItem setMaxSize:size];
852
853         // Hack: For some reason we need to make sure
854         // that the those element are on top
855         // Add them again will put them frontmost
856         [toolbarMediaControl addSubview:o_scrollfield];
857         [toolbarMediaControl addSubview:o_timeslider];
858         [toolbarMediaControl addSubview:o_timefield];
859         [toolbarMediaControl addSubview:o_main_pgbar];
860
861         /* TODO: setup a menu */
862     }
863     else
864     {
865         /* itemIdentifier referred to a toolbar item that is not
866          * provided or supported by us or Cocoa
867          * Returning nil will inform the toolbar
868          * that this kind of item is not supported */
869         toolbarItem = nil;
870     }
871     return toolbarItem;
872 }
873
874 #pragma mark -
875 #pragma mark Other notification
876
877 - (void)controlTintChanged
878 {
879     BOOL b_playing = NO;
880     
881     if( [o_btn_play alternateImage] == o_img_play_pressed )
882         b_playing = YES;
883     
884     if( [NSColor currentControlTint] == NSGraphiteControlTint )
885     {
886         o_img_play_pressed = [NSImage imageNamed: @"play_graphite"];
887         o_img_pause_pressed = [NSImage imageNamed: @"pause_graphite"];
888         
889         [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_graphite"]];
890         [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_graphite"]];
891         [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_graphite"]];
892         [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_graphite"]];
893         [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_graphite"]];
894         [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_graphite"]];
895         [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_graphite"]];
896         [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_graphite"]];
897     }
898     else
899     {
900         o_img_play_pressed = [NSImage imageNamed: @"play_blue"];
901         o_img_pause_pressed = [NSImage imageNamed: @"pause_blue"];
902         
903         [o_btn_prev setAlternateImage: [NSImage imageNamed: @"previous_blue"]];
904         [o_btn_rewind setAlternateImage: [NSImage imageNamed: @"skip_previous_blue"]];
905         [o_btn_stop setAlternateImage: [NSImage imageNamed: @"stop_blue"]];
906         [o_btn_ff setAlternateImage: [NSImage imageNamed: @"skip_forward_blue"]];
907         [o_btn_next setAlternateImage: [NSImage imageNamed: @"next_blue"]];
908         [o_btn_fullscreen setAlternateImage: [NSImage imageNamed: @"fullscreen_blue"]];
909         [o_btn_playlist setAlternateImage: [NSImage imageNamed: @"playlistdrawer_blue"]];
910         [o_btn_equalizer setAlternateImage: [NSImage imageNamed: @"equalizerdrawer_blue"]];
911     }
912     
913     if( b_playing )
914         [o_btn_play setAlternateImage: o_img_play_pressed];
915     else
916         [o_btn_play setAlternateImage: o_img_pause_pressed];
917 }
918
919 /* Listen to the remote in exclusive mode, only when VLC is the active
920    application */
921 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
922 {
923     [o_remote startListening: self];
924 }
925 - (void)applicationDidResignActive:(NSNotification *)aNotification
926 {
927     [o_remote stopListening: self];
928 }
929
930 /* Triggered when the computer goes to sleep */
931 - (void)computerWillSleep: (NSNotification *)notification
932 {
933     /* Pause */
934     if( p_intf->p_sys->i_play_status == PLAYING_S )
935     {
936         vlc_value_t val;
937         val.i_int = config_GetInt( p_intf, "key-play-pause" );
938         var_Set( p_intf->p_libvlc, "key-pressed", val );
939     }
940 }
941
942 #pragma mark -
943 #pragma mark File opening
944
945 - (BOOL)application:(NSApplication *)o_app openFile:(NSString *)o_filename
946 {
947     BOOL b_autoplay = config_GetInt( VLCIntf, "macosx-autoplay" );
948     NSDictionary *o_dic = [NSDictionary dictionaryWithObjectsAndKeys: o_filename, @"ITEM_URL", nil];
949     if( b_autoplay )
950         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: NO];
951     else
952         [o_playlist appendArray: [NSArray arrayWithObject: o_dic] atPos: -1 enqueue: YES];
953
954     return( TRUE );
955 }
956
957 /* When user click in the Dock icon our double click in the finder */
958 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
959 {    
960     if(!hasVisibleWindows)
961         [o_window makeKeyAndOrderFront:self];
962
963     return YES;
964 }
965
966 #pragma mark -
967 #pragma mark Apple Remote Control
968
969 /* Helper method for the remote control interface in order to trigger forward/backward and volume
970    increase/decrease as long as the user holds the left/right, plus/minus button */
971 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
972 {
973     if(b_remote_button_hold)
974     {
975         switch([buttonIdentifierNumber intValue])
976         {
977             case kRemoteButtonRight_Hold:
978                   [o_controls forward: self];
979             break;
980             case kRemoteButtonLeft_Hold:
981                   [o_controls backward: self];
982             break;
983             case kRemoteButtonVolume_Plus_Hold:
984                 [o_controls volumeUp: self];
985             break;
986             case kRemoteButtonVolume_Minus_Hold:
987                 [o_controls volumeDown: self];
988             break;
989         }
990         if(b_remote_button_hold)
991         {
992             /* trigger event */
993             [self performSelector:@selector(executeHoldActionForRemoteButton:)
994                          withObject:buttonIdentifierNumber
995                          afterDelay:0.25];
996         }
997     }
998 }
999
1000 /* Apple Remote callback */
1001 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1002                pressedDown: (BOOL) pressedDown
1003                 clickCount: (unsigned int) count
1004 {
1005     switch( buttonIdentifier )
1006     {
1007         case kRemoteButtonPlay:
1008             if(count >= 2) {
1009                 [o_controls toogleFullscreen:self];
1010             } else {
1011                 [o_controls play: self];
1012             }
1013             break;
1014         case kRemoteButtonVolume_Plus:
1015             [o_controls volumeUp: self];
1016             break;
1017         case kRemoteButtonVolume_Minus:
1018             [o_controls volumeDown: self];
1019             break;
1020         case kRemoteButtonRight:
1021             [o_controls next: self];
1022             break;
1023         case kRemoteButtonLeft:
1024             [o_controls prev: self];
1025             break;
1026         case kRemoteButtonRight_Hold:
1027         case kRemoteButtonLeft_Hold:
1028         case kRemoteButtonVolume_Plus_Hold:
1029         case kRemoteButtonVolume_Minus_Hold:
1030             /* simulate an event as long as the user holds the button */
1031             b_remote_button_hold = pressedDown;
1032             if( pressedDown )
1033             {
1034                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];
1035                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1036                            withObject:buttonIdentifierNumber];
1037             }
1038             break;
1039         case kRemoteButtonMenu:
1040             [o_controls showPosition: self];
1041             break;
1042         default:
1043             /* Add here whatever you want other buttons to do */
1044             break;
1045     }
1046 }
1047
1048 #pragma mark -
1049 #pragma mark String utility
1050 // FIXME: this has nothing to do here
1051
1052 - (NSString *)localizedString:(const char *)psz
1053 {
1054     NSString * o_str = nil;
1055
1056     if( psz != NULL )
1057     {
1058         o_str = [[[NSString alloc] initWithUTF8String: psz] autorelease];
1059
1060         if( o_str == NULL )
1061         {
1062             msg_Err( VLCIntf, "could not translate: %s", psz );
1063             return( @"" );
1064         }
1065     }
1066     else
1067     {
1068         msg_Warn( VLCIntf, "can't translate empty strings" );
1069         return( @"" );
1070     }
1071
1072     return( o_str );
1073 }
1074
1075
1076
1077 - (char *)delocalizeString:(NSString *)id
1078 {
1079     NSData * o_data = [id dataUsingEncoding: NSUTF8StringEncoding
1080                           allowLossyConversion: NO];
1081     char * psz_string;
1082
1083     if( o_data == nil )
1084     {
1085         o_data = [id dataUsingEncoding: NSUTF8StringEncoding
1086                      allowLossyConversion: YES];
1087         psz_string = malloc( [o_data length] + 1 );
1088         [o_data getBytes: psz_string];
1089         psz_string[ [o_data length] ] = '\0';
1090         msg_Err( VLCIntf, "cannot convert to the requested encoding: %s",
1091                  psz_string );
1092     }
1093     else
1094     {
1095         psz_string = malloc( [o_data length] + 1 );
1096         [o_data getBytes: psz_string];
1097         psz_string[ [o_data length] ] = '\0';
1098     }
1099
1100     return psz_string;
1101 }
1102
1103 /* i_width is in pixels */
1104 - (NSString *)wrapString: (NSString *)o_in_string toWidth: (int) i_width
1105 {
1106     NSMutableString *o_wrapped;
1107     NSString *o_out_string;
1108     NSRange glyphRange, effectiveRange, charRange;
1109     NSRect lineFragmentRect;
1110     unsigned glyphIndex, breaksInserted = 0;
1111
1112     NSTextStorage *o_storage = [[NSTextStorage alloc] initWithString: o_in_string
1113         attributes: [NSDictionary dictionaryWithObjectsAndKeys:
1114         [NSFont labelFontOfSize: 0.0], NSFontAttributeName, nil]];
1115     NSLayoutManager *o_layout_manager = [[NSLayoutManager alloc] init];
1116     NSTextContainer *o_container = [[NSTextContainer alloc]
1117         initWithContainerSize: NSMakeSize(i_width, 2000)];
1118
1119     [o_layout_manager addTextContainer: o_container];
1120     [o_container release];
1121     [o_storage addLayoutManager: o_layout_manager];
1122     [o_layout_manager release];
1123
1124     o_wrapped = [o_in_string mutableCopy];
1125     glyphRange = [o_layout_manager glyphRangeForTextContainer: o_container];
1126
1127     for( glyphIndex = glyphRange.location ; glyphIndex < NSMaxRange(glyphRange) ;
1128             glyphIndex += effectiveRange.length) {
1129         lineFragmentRect = [o_layout_manager lineFragmentRectForGlyphAtIndex: glyphIndex
1130                                             effectiveRange: &effectiveRange];
1131         charRange = [o_layout_manager characterRangeForGlyphRange: effectiveRange
1132                                     actualGlyphRange: &effectiveRange];
1133         if([o_wrapped lineRangeForRange:
1134                 NSMakeRange(charRange.location + breaksInserted, charRange.length)].length > charRange.length) {
1135             [o_wrapped insertString: @"\n" atIndex: NSMaxRange(charRange) + breaksInserted];
1136             breaksInserted++;
1137         }
1138     }
1139     o_out_string = [NSString stringWithString: o_wrapped];
1140     [o_wrapped release];
1141     [o_storage release];
1142
1143     return o_out_string;
1144 }
1145
1146
1147 #pragma mark -
1148 #pragma mark Key Shortcuts
1149
1150 static struct
1151 {
1152     unichar i_nskey;
1153     unsigned int i_vlckey;
1154 } nskeys_to_vlckeys[] =
1155 {
1156     { NSUpArrowFunctionKey, KEY_UP },
1157     { NSDownArrowFunctionKey, KEY_DOWN },
1158     { NSLeftArrowFunctionKey, KEY_LEFT },
1159     { NSRightArrowFunctionKey, KEY_RIGHT },
1160     { NSF1FunctionKey, KEY_F1 },
1161     { NSF2FunctionKey, KEY_F2 },
1162     { NSF3FunctionKey, KEY_F3 },
1163     { NSF4FunctionKey, KEY_F4 },
1164     { NSF5FunctionKey, KEY_F5 },
1165     { NSF6FunctionKey, KEY_F6 },
1166     { NSF7FunctionKey, KEY_F7 },
1167     { NSF8FunctionKey, KEY_F8 },
1168     { NSF9FunctionKey, KEY_F9 },
1169     { NSF10FunctionKey, KEY_F10 },
1170     { NSF11FunctionKey, KEY_F11 },
1171     { NSF12FunctionKey, KEY_F12 },
1172     { NSInsertFunctionKey, KEY_INSERT },
1173     { NSHomeFunctionKey, KEY_HOME },
1174     { NSEndFunctionKey, KEY_END },
1175     { NSPageUpFunctionKey, KEY_PAGEUP },
1176     { NSPageDownFunctionKey, KEY_PAGEDOWN },
1177     { NSMenuFunctionKey, KEY_MENU },
1178     { NSTabCharacter, KEY_TAB },
1179     { NSCarriageReturnCharacter, KEY_ENTER },
1180     { NSEnterCharacter, KEY_ENTER },
1181     { NSBackspaceCharacter, KEY_BACKSPACE },
1182     { (unichar) ' ', KEY_SPACE },
1183     { (unichar) 0x1b, KEY_ESC },
1184     {0,0}
1185 };
1186
1187 static unichar VLCKeyToCocoa( unsigned int i_key )
1188 {
1189     unsigned int i;
1190
1191     for( i = 0; nskeys_to_vlckeys[i].i_vlckey != 0; i++ )
1192     {
1193         if( nskeys_to_vlckeys[i].i_vlckey == (i_key & ~KEY_MODIFIER) )
1194         {
1195             return nskeys_to_vlckeys[i].i_nskey;
1196         }
1197     }
1198     return (unichar)(i_key & ~KEY_MODIFIER);
1199 }
1200
1201 unsigned int CocoaKeyToVLC( unichar i_key )
1202 {
1203     unsigned int i;
1204
1205     for( i = 0; nskeys_to_vlckeys[i].i_nskey != 0; i++ )
1206     {
1207         if( nskeys_to_vlckeys[i].i_nskey == i_key )
1208         {
1209             return nskeys_to_vlckeys[i].i_vlckey;
1210         }
1211     }
1212     return (unsigned int)i_key;
1213 }
1214
1215 static unsigned int VLCModifiersToCocoa( unsigned int i_key )
1216 {
1217     unsigned int new = 0;
1218     if( i_key & KEY_MODIFIER_COMMAND )
1219         new |= NSCommandKeyMask;
1220     if( i_key & KEY_MODIFIER_ALT )
1221         new |= NSAlternateKeyMask;
1222     if( i_key & KEY_MODIFIER_SHIFT )
1223         new |= NSShiftKeyMask;
1224     if( i_key & KEY_MODIFIER_CTRL )
1225         new |= NSControlKeyMask;
1226     return new;
1227 }
1228
1229 /*****************************************************************************
1230  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1231  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1232  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1233  *****************************************************************************/
1234 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event
1235 {
1236     unichar key = 0;
1237     vlc_value_t val;
1238     unsigned int i_pressed_modifiers = 0;
1239     struct hotkey *p_hotkeys;
1240     int i;
1241
1242     val.i_int = 0;
1243     p_hotkeys = p_intf->p_libvlc->p_hotkeys;
1244
1245     i_pressed_modifiers = [o_event modifierFlags];
1246
1247     if( i_pressed_modifiers & NSShiftKeyMask )
1248         val.i_int |= KEY_MODIFIER_SHIFT;
1249     if( i_pressed_modifiers & NSControlKeyMask )
1250         val.i_int |= KEY_MODIFIER_CTRL;
1251     if( i_pressed_modifiers & NSAlternateKeyMask )
1252         val.i_int |= KEY_MODIFIER_ALT;
1253     if( i_pressed_modifiers & NSCommandKeyMask )
1254         val.i_int |= KEY_MODIFIER_COMMAND;
1255
1256     key = [[o_event charactersIgnoringModifiers] characterAtIndex: 0];
1257
1258     switch( key )
1259     {
1260         case NSDeleteCharacter:
1261         case NSDeleteFunctionKey:
1262         case NSDeleteCharFunctionKey:
1263         case NSBackspaceCharacter:
1264         case NSUpArrowFunctionKey:
1265         case NSDownArrowFunctionKey:
1266         case NSRightArrowFunctionKey:
1267         case NSLeftArrowFunctionKey:
1268         case NSEnterCharacter:
1269         case NSCarriageReturnCharacter:
1270             return NO;
1271     }
1272
1273     val.i_int |= CocoaKeyToVLC( key );
1274
1275     for( i = 0; p_hotkeys[i].psz_action != NULL; i++ )
1276     {
1277         if( p_hotkeys[i].i_key == val.i_int )
1278         {
1279             var_Set( p_intf->p_libvlc, "key-pressed", val );
1280             return YES;
1281         }
1282     }
1283
1284     return NO;
1285 }
1286
1287 #pragma mark -
1288 #pragma mark Other objects getters
1289 // FIXME: this is ugly and does not respect cocoa naming scheme
1290
1291 - (id)getControls
1292 {
1293     if( o_controls )
1294         return o_controls;
1295
1296     return nil;
1297 }
1298
1299 - (id)getSimplePreferences
1300 {
1301     if( !o_sprefs )
1302         return nil;
1303
1304     if( !nib_prefs_loaded )
1305         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1306
1307     return o_sprefs;
1308 }
1309
1310 - (id)getPreferences
1311 {
1312     if( !o_prefs )
1313         return nil;
1314
1315     if( !nib_prefs_loaded )
1316         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1317
1318     return o_prefs;
1319 }
1320
1321 - (id)getPlaylist
1322 {
1323     if( o_playlist )
1324         return o_playlist;
1325
1326     return nil;
1327 }
1328
1329 - (BOOL)isPlaylistCollapsed
1330 {
1331     return ![o_btn_playlist state];
1332 }
1333
1334 - (id)getInfo
1335 {
1336     if( o_info )
1337         return o_info;
1338
1339     return nil;
1340 }
1341
1342 - (id)getWizard
1343 {
1344     if( o_wizard )
1345         return o_wizard;
1346
1347     return nil;
1348 }
1349
1350 - (id)getVLM
1351 {
1352     return o_vlm;
1353 }
1354
1355 - (id)getBookmarks
1356 {
1357     if( o_bookmarks )
1358         return o_bookmarks;
1359
1360     return nil;
1361 }
1362
1363 - (id)getEmbeddedList
1364 {
1365     if( o_embedded_list )
1366         return o_embedded_list;
1367
1368     return nil;
1369 }
1370
1371 - (id)getInteractionList
1372 {
1373     if( o_interaction_list )
1374         return o_interaction_list;
1375
1376     return nil;
1377 }
1378
1379 - (id)getMainIntfPgbar
1380 {
1381     if( o_main_pgbar )
1382         return o_main_pgbar;
1383
1384     return nil;
1385 }
1386
1387 - (id)getControllerWindow
1388 {
1389     if( o_window )
1390         return o_window;
1391     return nil;
1392 }
1393
1394 - (id)getVoutMenu
1395 {
1396     return o_vout_menu;
1397 }
1398
1399 - (id)getEyeTVController
1400 {
1401     if( o_eyetv )
1402         return o_eyetv;
1403
1404     return nil;
1405 }
1406
1407 #pragma mark -
1408 #pragma mark Polling
1409
1410 /*****************************************************************************
1411  * ManageThread: An ugly thread that polls
1412  *****************************************************************************/
1413 static void * ManageThread( void *user_data )
1414 {
1415     id self = user_data;
1416
1417     [self manage];
1418
1419     return NULL;
1420 }
1421
1422 struct manage_cleanup_stack {
1423     intf_thread_t * p_intf;
1424     input_thread_t ** p_input;
1425     playlist_t * p_playlist;
1426     id self;
1427 };
1428
1429 static void manage_cleanup( void * args )
1430 {
1431     struct manage_cleanup_stack * manage_cleanup_stack = args;
1432     intf_thread_t * p_intf = manage_cleanup_stack->p_intf;
1433     input_thread_t * p_input = *manage_cleanup_stack->p_input;
1434     id self = manage_cleanup_stack->self;
1435     playlist_t * p_playlist = manage_cleanup_stack->p_playlist;
1436
1437     var_AddCallback( p_playlist, "item-current", PlaylistChanged, self );
1438     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
1439     var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
1440     var_AddCallback( p_playlist, "playlist-item-append", PlaylistChanged, self );
1441     var_AddCallback( p_playlist, "playlist-item-deleted", PlaylistChanged, self );
1442
1443     pl_Release( p_intf );
1444
1445     if( p_input ) vlc_object_release( p_input );
1446 }
1447
1448 - (void)manage
1449 {
1450     playlist_t * p_playlist;
1451     input_thread_t * p_input = NULL;
1452
1453     /* new thread requires a new pool */
1454
1455     vlc_thread_set_priority( p_intf, VLC_THREAD_PRIORITY_LOW );
1456
1457     p_playlist = pl_Hold( p_intf );
1458
1459     var_AddCallback( p_playlist, "item-current", PlaylistChanged, self );
1460     var_AddCallback( p_playlist, "intf-change", PlaylistChanged, self );
1461     var_AddCallback( p_playlist, "item-change", PlaylistChanged, self );
1462     var_AddCallback( p_playlist, "playlist-item-append", PlaylistChanged, self );
1463     var_AddCallback( p_playlist, "playlist-item-deleted", PlaylistChanged, self );
1464
1465     struct manage_cleanup_stack stack = { p_intf, &p_input, p_playlist, self };
1466     pthread_cleanup_push(manage_cleanup, &stack);
1467
1468     while( true )
1469     {
1470         NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
1471
1472         if( !p_input )
1473         {
1474             p_input = playlist_CurrentInput( p_playlist );
1475
1476             /* Refresh the interface */
1477             if( p_input )
1478             {
1479                 msg_Dbg( p_intf, "input has changed, refreshing interface" );
1480                 p_intf->p_sys->b_input_update = true;
1481             }
1482         }
1483         else if( !vlc_object_alive (p_input) || p_input->b_dead )
1484         {
1485             /* input stopped */
1486             p_intf->p_sys->b_intf_update = true;
1487             p_intf->p_sys->i_play_status = END_S;
1488             msg_Dbg( p_intf, "input has stopped, refreshing interface" );
1489             vlc_object_release( p_input );
1490             p_input = NULL;
1491         }
1492         else if( cachedInputState != input_GetState( p_input ) )
1493         {
1494             p_intf->p_sys->b_intf_update = true;
1495         }
1496
1497         /* Manage volume status */
1498         [self manageVolumeSlider];
1499
1500         msleep( INTF_IDLE_SLEEP );
1501
1502         [pool release];
1503     }
1504
1505     pthread_cleanup_pop(1);
1506
1507     msg_Dbg( p_intf, "Killing the Mac OS X module" );
1508
1509     /* We are dead, terminate */
1510     [NSApp performSelectorOnMainThread: @selector(terminate:) withObject:nil waitUntilDone:NO];
1511 }
1512
1513 - (void)manageVolumeSlider
1514 {
1515     audio_volume_t i_volume;
1516     aout_VolumeGet( p_intf, &i_volume );
1517
1518     if( i_volume != i_lastShownVolume )
1519     {
1520         i_lastShownVolume = i_volume;
1521         p_intf->p_sys->b_volume_update = TRUE;
1522     }
1523 }
1524
1525 - (void)manageIntf:(NSTimer *)o_timer
1526 {
1527     vlc_value_t val;
1528     playlist_t * p_playlist;
1529     input_thread_t * p_input;
1530
1531     if( p_intf->p_sys->b_input_update )
1532     {
1533         /* Called when new input is opened */
1534         p_intf->p_sys->b_current_title_update = true;
1535         p_intf->p_sys->b_intf_update = true;
1536         p_intf->p_sys->b_input_update = false;
1537         [self setupMenus]; /* Make sure input menu is up to date */
1538     }
1539     if( p_intf->p_sys->b_intf_update )
1540     {
1541         bool b_input = false;
1542         bool b_plmul = false;
1543         bool b_control = false;
1544         bool b_seekable = false;
1545         bool b_chapters = false;
1546
1547         playlist_t * p_playlist = pl_Hold( p_intf );
1548
1549         /* TODO: fix i_size use */
1550         b_plmul = p_playlist->items.i_size > 1;
1551
1552         p_input = playlist_CurrentInput( p_playlist );
1553         bool b_buffering = NO;
1554     
1555         if( ( b_input = ( p_input != NULL ) ) )
1556         {
1557             /* seekable streams */
1558             cachedInputState = input_GetState( p_input );
1559             if ( cachedInputState == INIT_S ||
1560                  cachedInputState == OPENING_S )
1561             {
1562                 b_buffering = YES;
1563             }
1564
1565             /* update our info-panel to reflect the new item, if we don't show
1566              * the playlist or the selection is empty */
1567             if( [self isPlaylistCollapsed] == YES )
1568             {
1569                 PL_LOCK;
1570                 [[self getInfo] updatePanelWithItem: playlist_CurrentPlayingItem( p_playlist )->p_input];
1571                 PL_UNLOCK;
1572             }
1573
1574             /* seekable streams */
1575             b_seekable = var_GetBool( p_input, "can-seek" );
1576
1577             /* check whether slow/fast motion is possible */
1578             b_control = var_GetBool( p_input, "can-rate" );
1579
1580             /* chapters & titles */
1581             //b_chapters = p_input->stream.i_area_nb > 1;
1582             vlc_object_release( p_input );
1583         }
1584         pl_Release( p_intf );
1585
1586         if( b_buffering )
1587         {
1588             [o_main_pgbar startAnimation:self];
1589             [o_main_pgbar setIndeterminate:YES];
1590             [o_main_pgbar setHidden:NO];
1591         }
1592         else
1593         {
1594             [o_main_pgbar stopAnimation:self];
1595             [o_main_pgbar setHidden:YES];
1596         }
1597
1598         [o_btn_stop setEnabled: b_input];
1599         [o_btn_ff setEnabled: b_seekable];
1600         [o_btn_rewind setEnabled: b_seekable];
1601         [o_btn_prev setEnabled: (b_plmul || b_chapters)];
1602         [o_btn_next setEnabled: (b_plmul || b_chapters)];
1603
1604         [o_timeslider setFloatValue: 0.0];
1605         [o_timeslider setEnabled: b_seekable];
1606         [o_timefield setStringValue: @"00:00"];
1607         [[[self getControls] getFSPanel] setStreamPos: 0 andTime: @"00:00"];
1608         [[[self getControls] getFSPanel] setSeekable: b_seekable];
1609
1610         [o_embedded_window setSeekable: b_seekable];
1611
1612         p_intf->p_sys->b_current_title_update = true;
1613         
1614         p_intf->p_sys->b_intf_update = false;
1615     }
1616
1617     if( p_intf->p_sys->b_playmode_update )
1618     {
1619         [o_playlist playModeUpdated];
1620         p_intf->p_sys->b_playmode_update = false;
1621     }
1622     if( p_intf->p_sys->b_playlist_update )
1623     {
1624         [o_playlist playlistUpdated];
1625         p_intf->p_sys->b_playlist_update = false;
1626     }
1627
1628     if( p_intf->p_sys->b_fullscreen_update )
1629     {
1630         p_intf->p_sys->b_fullscreen_update = false;
1631     }
1632
1633     if( p_intf->p_sys->b_intf_show )
1634     {
1635         if( [[o_controls voutView] isFullscreen] && config_GetInt( VLCIntf, "macosx-fspanel" ) )
1636             [[o_controls getFSPanel] fadeIn];
1637         else
1638             [o_window makeKeyAndOrderFront: self];
1639
1640         p_intf->p_sys->b_intf_show = false;
1641     }
1642
1643     p_input = pl_CurrentInput( p_intf );
1644     if( p_input && vlc_object_alive (p_input) )
1645     {
1646         vlc_value_t val;
1647
1648         if( p_intf->p_sys->b_current_title_update )
1649         {
1650             NSString *aString;
1651             input_item_t * p_item = input_GetItem( p_input );
1652             char * name = input_item_GetNowPlaying( p_item );
1653
1654             if( !name )
1655                 name = input_item_GetName( p_item );
1656
1657             aString = [NSString stringWithUTF8String:name];
1658
1659             free(name);
1660
1661             [self setScrollField: aString stopAfter:-1];
1662             [[[self getControls] getFSPanel] setStreamTitle: aString];
1663
1664             [[o_controls voutView] updateTitle];
1665  
1666             [o_playlist updateRowSelection];
1667             p_intf->p_sys->b_current_title_update = FALSE;
1668         }
1669
1670         if( [o_timeslider isEnabled] )
1671         {
1672             /* Update the slider */
1673             vlc_value_t time;
1674             NSString * o_time;
1675             vlc_value_t pos;
1676             char psz_time[MSTRTIME_MAX_SIZE];
1677             float f_updated;
1678
1679             var_Get( p_input, "position", &pos );
1680             f_updated = 10000. * pos.f_float;
1681             [o_timeslider setFloatValue: f_updated];
1682
1683             var_Get( p_input, "time", &time );
1684
1685             mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1686             if( b_time_remaining && dur != -1 )
1687             {
1688                 o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000))];
1689             }
1690             else
1691                 o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1692
1693             [o_timefield setStringValue: o_time];
1694             [[[self getControls] getFSPanel] setStreamPos: f_updated andTime: o_time];
1695             [o_embedded_window setTime: o_time position: f_updated];
1696         }
1697
1698         /* Manage Playing status */
1699         var_Get( p_input, "state", &val );
1700         if( p_intf->p_sys->i_play_status != val.i_int )
1701         {
1702             p_intf->p_sys->i_play_status = val.i_int;
1703             [self playStatusUpdated: p_intf->p_sys->i_play_status];
1704             [o_embedded_window playStatusUpdated: p_intf->p_sys->i_play_status];
1705         }
1706         vlc_object_release( p_input );
1707     }
1708     else if( p_input )
1709     {
1710         vlc_object_release( p_input );
1711     }
1712     else
1713     {
1714         p_intf->p_sys->i_play_status = END_S;
1715         [self playStatusUpdated: p_intf->p_sys->i_play_status];
1716         [o_embedded_window playStatusUpdated: p_intf->p_sys->i_play_status];
1717         [self setSubmenusEnabled: FALSE];
1718     }
1719
1720     if( p_intf->p_sys->b_volume_update )
1721     {
1722         NSString *o_text;
1723         int i_volume_step = 0;
1724         o_text = [NSString stringWithFormat: _NS("Volume: %d%%"), i_lastShownVolume * 400 / AOUT_VOLUME_MAX];
1725         if( i_lastShownVolume != -1 )
1726         [self setScrollField:o_text stopAfter:1000000];
1727         i_volume_step = config_GetInt( p_intf->p_libvlc, "volume-step" );
1728         [o_volumeslider setFloatValue: (float)i_lastShownVolume / i_volume_step];
1729         [o_volumeslider setEnabled: TRUE];
1730         [[[self getControls] getFSPanel] setVolumeLevel: (float)i_lastShownVolume / i_volume_step];
1731         p_intf->p_sys->b_mute = ( i_lastShownVolume == 0 );
1732         p_intf->p_sys->b_volume_update = FALSE;
1733     }
1734
1735 end:
1736     [self updateMessageDisplay];
1737
1738     if( ((i_end_scroll != -1) && (mdate() > i_end_scroll)) || !p_input )
1739         [self resetScrollField];
1740
1741     [interfaceTimer autorelease];
1742
1743     interfaceTimer = [[NSTimer scheduledTimerWithTimeInterval: 0.3
1744         target: self selector: @selector(manageIntf:)
1745         userInfo: nil repeats: FALSE] retain];
1746 }
1747
1748 #pragma mark -
1749 #pragma mark Interface update
1750
1751 - (void)setupMenus
1752 {
1753     playlist_t * p_playlist = pl_Hold( p_intf );
1754     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1755     if( p_input != NULL )
1756     {
1757         [o_controls setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
1758             var: "program" selector: @selector(toggleVar:)];
1759
1760         [o_controls setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
1761             var: "title" selector: @selector(toggleVar:)];
1762
1763         [o_controls setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
1764             var: "chapter" selector: @selector(toggleVar:)];
1765
1766         [o_controls setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
1767             var: "audio-es" selector: @selector(toggleVar:)];
1768
1769         [o_controls setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
1770             var: "video-es" selector: @selector(toggleVar:)];
1771
1772         [o_controls setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
1773             var: "spu-es" selector: @selector(toggleVar:)];
1774
1775         /* special case for "Open File" inside the subtitles menu item */
1776         if( [o_mi_videotrack isEnabled] == YES )
1777             [o_mi_subtitle setEnabled: YES];
1778
1779         aout_instance_t * p_aout = input_GetAout( p_input );
1780         if( p_aout != NULL )
1781         {
1782             [o_controls setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
1783                 var: "audio-channels" selector: @selector(toggleVar:)];
1784
1785             [o_controls setupVarMenuItem: o_mi_device target: (vlc_object_t *)p_aout
1786                 var: "audio-device" selector: @selector(toggleVar:)];
1787
1788             [o_controls setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
1789                 var: "visual" selector: @selector(toggleVar:)];
1790             vlc_object_release( (vlc_object_t *)p_aout );
1791         }
1792
1793         vout_thread_t * p_vout = input_GetVout( p_input );
1794
1795         if( p_vout != NULL )
1796         {
1797             vlc_object_t * p_dec_obj;
1798
1799             [o_controls setupVarMenuItem: o_mi_aspect_ratio target: (vlc_object_t *)p_vout
1800                 var: "aspect-ratio" selector: @selector(toggleVar:)];
1801
1802             [o_controls setupVarMenuItem: o_mi_crop target: (vlc_object_t *) p_vout
1803                 var: "crop" selector: @selector(toggleVar:)];
1804
1805             [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
1806                 var: "video-device" selector: @selector(toggleVar:)];
1807
1808             [o_controls setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
1809                 var: "deinterlace" selector: @selector(toggleVar:)];
1810
1811 #if 0
1812 /* FIXME Post processing. */
1813             p_dec_obj = (vlc_object_t *)vlc_object_find(
1814                                                  (vlc_object_t *)p_vout,
1815                                                  VLC_OBJECT_DECODER,
1816                                                  FIND_PARENT );
1817             if( p_dec_obj != NULL )
1818             {
1819                [o_controls setupVarMenuItem: o_mi_ffmpeg_pp target:
1820                     (vlc_object_t *)p_dec_obj var:"ffmpeg-pp-q" selector:
1821                     @selector(toggleVar:)];
1822
1823                 vlc_object_release(p_dec_obj);
1824             }
1825 #endif
1826             vlc_object_release( (vlc_object_t *)p_vout );
1827         }
1828         vlc_object_release( p_input );
1829     }
1830     pl_Release( p_intf );
1831 }
1832
1833 - (void)refreshVoutDeviceMenu:(NSNotification *)o_notification
1834 {
1835     int x,y = 0;
1836     vout_thread_t * p_vout = vlc_object_find( p_intf, VLC_OBJECT_VOUT,
1837                                               FIND_ANYWHERE );
1838  
1839     if(! p_vout )
1840         return;
1841  
1842     /* clean the menu before adding new entries */
1843     if( [o_mi_screen hasSubmenu] )
1844     {
1845         y = [[o_mi_screen submenu] numberOfItems] - 1;
1846         msg_Dbg( VLCIntf, "%i items in submenu", y );
1847         while( x != y )
1848         {
1849             msg_Dbg( VLCIntf, "removing item %i of %i", x, y );
1850             [[o_mi_screen submenu] removeItemAtIndex: x];
1851             x++;
1852         }
1853     }
1854
1855     [o_controls setupVarMenuItem: o_mi_screen target: (vlc_object_t *)p_vout
1856                              var: "video-device" selector: @selector(toggleVar:)];
1857     vlc_object_release( (vlc_object_t *)p_vout );
1858 }
1859
1860 - (void)setScrollField:(NSString *)o_string stopAfter:(int)timeout
1861 {
1862     if( timeout != -1 )
1863         i_end_scroll = mdate() + timeout;
1864     else
1865         i_end_scroll = -1;
1866     [o_scrollfield setStringValue: o_string];
1867 }
1868
1869 - (void)resetScrollField
1870 {
1871     playlist_t * p_playlist = pl_Hold( p_intf );
1872     input_thread_t * p_input = playlist_CurrentInput( p_playlist );
1873
1874     i_end_scroll = -1;
1875     if( p_input && vlc_object_alive (p_input) )
1876     {
1877         NSString *o_temp;
1878         PL_LOCK;
1879         playlist_item_t * p_item = playlist_CurrentPlayingItem( p_playlist );
1880         if( input_item_GetNowPlaying( p_item->p_input ) )
1881             o_temp = [NSString stringWithUTF8String:input_item_GetNowPlaying( p_item->p_input )];
1882         else
1883             o_temp = [NSString stringWithUTF8String:p_item->p_input->psz_name];
1884         PL_UNLOCK;
1885         [self setScrollField: o_temp stopAfter:-1];
1886         [[[self getControls] getFSPanel] setStreamTitle: o_temp];
1887         vlc_object_release( p_input );
1888         pl_Release( p_intf );
1889         return;
1890     }
1891     pl_Release( p_intf );
1892     [self setScrollField: _NS("VLC media player") stopAfter:-1];
1893 }
1894
1895 - (void)playStatusUpdated:(int)i_status
1896 {
1897     if( i_status == PLAYING_S )
1898     {
1899         [[[self getControls] getFSPanel] setPause];
1900         [o_btn_play setImage: o_img_pause];
1901         [o_btn_play setAlternateImage: o_img_pause_pressed];
1902         [o_btn_play setToolTip: _NS("Pause")];
1903         [o_mi_play setTitle: _NS("Pause")];
1904         [o_dmi_play setTitle: _NS("Pause")];
1905         [o_vmi_play setTitle: _NS("Pause")];
1906     }
1907     else
1908     {
1909         [[[self getControls] getFSPanel] setPlay];
1910         [o_btn_play setImage: o_img_play];
1911         [o_btn_play setAlternateImage: o_img_play_pressed];
1912         [o_btn_play setToolTip: _NS("Play")];
1913         [o_mi_play setTitle: _NS("Play")];
1914         [o_dmi_play setTitle: _NS("Play")];
1915         [o_vmi_play setTitle: _NS("Play")];
1916     }
1917 }
1918
1919 - (void)setSubmenusEnabled:(BOOL)b_enabled
1920 {
1921     [o_mi_program setEnabled: b_enabled];
1922     [o_mi_title setEnabled: b_enabled];
1923     [o_mi_chapter setEnabled: b_enabled];
1924     [o_mi_audiotrack setEnabled: b_enabled];
1925     [o_mi_visual setEnabled: b_enabled];
1926     [o_mi_videotrack setEnabled: b_enabled];
1927     [o_mi_subtitle setEnabled: b_enabled];
1928     [o_mi_channels setEnabled: b_enabled];
1929     [o_mi_deinterlace setEnabled: b_enabled];
1930     [o_mi_ffmpeg_pp setEnabled: b_enabled];
1931     [o_mi_device setEnabled: b_enabled];
1932     [o_mi_screen setEnabled: b_enabled];
1933     [o_mi_aspect_ratio setEnabled: b_enabled];
1934     [o_mi_crop setEnabled: b_enabled];
1935     [o_mi_teletext setEnabled: b_enabled];
1936 }
1937
1938 - (IBAction)timesliderUpdate:(id)sender
1939 {
1940     float f_updated;
1941     playlist_t * p_playlist;
1942     input_thread_t * p_input;
1943
1944     switch( [[NSApp currentEvent] type] )
1945     {
1946         case NSLeftMouseUp:
1947         case NSLeftMouseDown:
1948         case NSLeftMouseDragged:
1949             f_updated = [sender floatValue];
1950             break;
1951
1952         default:
1953             return;
1954     }
1955     p_playlist = pl_Hold( p_intf );
1956     p_input = playlist_CurrentInput( p_playlist );
1957     if( p_input != NULL )
1958     {
1959         vlc_value_t time;
1960         vlc_value_t pos;
1961         NSString * o_time;
1962         char psz_time[MSTRTIME_MAX_SIZE];
1963
1964         pos.f_float = f_updated / 10000.;
1965         var_Set( p_input, "position", pos );
1966         [o_timeslider setFloatValue: f_updated];
1967
1968         var_Get( p_input, "time", &time );
1969
1970         mtime_t dur = input_item_GetDuration( input_GetItem( p_input ) );
1971         if( b_time_remaining && dur != -1 )
1972         {
1973             o_time = [NSString stringWithFormat: @"-%s", secstotimestr( psz_time, ((dur - time.i_time) / 1000000) )];
1974         }
1975         else
1976             o_time = [NSString stringWithUTF8String: secstotimestr( psz_time, (time.i_time / 1000000) )];
1977
1978         [o_timefield setStringValue: o_time];
1979         [[[self getControls] getFSPanel] setStreamPos: f_updated andTime: o_time];
1980         [o_embedded_window setTime: o_time position: f_updated];
1981         vlc_object_release( p_input );
1982     }
1983     pl_Release( p_intf );
1984 }
1985
1986 - (IBAction)timeFieldWasClicked:(id)sender
1987 {
1988     b_time_remaining = !b_time_remaining;
1989 }
1990     
1991
1992 #pragma mark -
1993 #pragma mark Recent Items
1994
1995 - (IBAction)clearRecentItems:(id)sender
1996 {
1997     [[NSDocumentController sharedDocumentController]
1998                           clearRecentDocuments: nil];
1999 }
2000
2001 - (void)openRecentItem:(id)sender
2002 {
2003     [self application: nil openFile: [sender title]];
2004 }
2005
2006 #pragma mark -
2007 #pragma mark Panels
2008
2009 - (IBAction)intfOpenFile:(id)sender
2010 {
2011     if( !nib_open_loaded )
2012     {
2013         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
2014         [o_open awakeFromNib];
2015         [o_open openFile];
2016     } else {
2017         [o_open openFile];
2018     }
2019 }
2020
2021 - (IBAction)intfOpenFileGeneric:(id)sender
2022 {
2023     if( !nib_open_loaded )
2024     {
2025         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
2026         [o_open awakeFromNib];
2027         [o_open openFileGeneric];
2028     } else {
2029         [o_open openFileGeneric];
2030     }
2031 }
2032
2033 - (IBAction)intfOpenDisc:(id)sender
2034 {
2035     if( !nib_open_loaded )
2036     {
2037         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
2038         [o_open awakeFromNib];
2039         [o_open openDisc];
2040     } else {
2041         [o_open openDisc];
2042     }
2043 }
2044
2045 - (IBAction)intfOpenNet:(id)sender
2046 {
2047     if( !nib_open_loaded )
2048     {
2049         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
2050         [o_open awakeFromNib];
2051         [o_open openNet];
2052     } else {
2053         [o_open openNet];
2054     }
2055 }
2056
2057 - (IBAction)intfOpenCapture:(id)sender
2058 {
2059     if( !nib_open_loaded )
2060     {
2061         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
2062         [o_open awakeFromNib];
2063         [o_open openCapture];
2064     } else {
2065         [o_open openCapture];
2066     }
2067 }
2068
2069 - (IBAction)showWizard:(id)sender
2070 {
2071     if( !nib_wizard_loaded )
2072     {
2073         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp];
2074         [o_wizard initStrings];
2075         [o_wizard resetWizard];
2076         [o_wizard showWizard];
2077     } else {
2078         [o_wizard resetWizard];
2079         [o_wizard showWizard];
2080     }
2081 }
2082
2083 - (IBAction)showVLM:(id)sender
2084 {
2085     if( !nib_vlm_loaded )
2086         nib_vlm_loaded = [NSBundle loadNibNamed:@"VLM" owner: NSApp];
2087
2088     [o_vlm showVLMWindow];
2089 }
2090
2091 - (IBAction)showExtended:(id)sender
2092 {
2093     if( o_extended == nil )
2094         o_extended = [[VLCExtended alloc] init];
2095
2096     if( !nib_extended_loaded )
2097         nib_extended_loaded = [NSBundle loadNibNamed:@"Extended" owner: NSApp];
2098
2099     [o_extended showPanel];
2100 }
2101
2102 - (IBAction)showBookmarks:(id)sender
2103 {
2104     dialog_Fatal( p_intf, _("Video Settings not saved"),
2105                  _("An error occured while saving your settings via SimplePrefs.") );
2106
2107     /* we need the wizard-nib for the bookmarks's extract functionality */
2108     if( !nib_wizard_loaded )
2109     {
2110         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp];
2111         [o_wizard initStrings];
2112     }
2113  
2114     if( !nib_bookmarks_loaded )
2115         nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner: NSApp];
2116
2117     [o_bookmarks showBookmarks];
2118 }
2119
2120 - (IBAction)viewPreferences:(id)sender
2121 {
2122     if( !nib_prefs_loaded )
2123     {
2124         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
2125         o_sprefs = [[VLCSimplePrefs alloc] init];
2126         o_prefs= [[VLCPrefs alloc] init];
2127     }
2128
2129     [o_sprefs showSimplePrefs];
2130 }
2131
2132 #pragma mark -
2133 #pragma mark Update
2134
2135 - (IBAction)checkForUpdate:(id)sender
2136 {
2137 #ifdef UPDATE_CHECK
2138     if( !nib_update_loaded )
2139         nib_update_loaded = [NSBundle loadNibNamed:@"Update" owner: NSApp];
2140     [o_update showUpdateWindow];
2141 #else
2142     msg_Err( VLCIntf, "Update checker wasn't enabled in this build" );
2143     dialog_Fatal( VLCIntf, _("Update check failed"), _("Checking for updates was not enabled in this build.") );
2144 #endif
2145 }
2146
2147 #pragma mark -
2148 #pragma mark Help and Docs
2149
2150 - (IBAction)viewAbout:(id)sender
2151 {
2152     if( !nib_about_loaded )
2153         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner: NSApp];
2154
2155     [o_about showAbout];
2156 }
2157
2158 - (IBAction)showLicense:(id)sender
2159 {
2160     if( !nib_about_loaded )
2161         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner: NSApp];
2162
2163     [o_about showGPL: sender];
2164 }
2165     
2166 - (IBAction)viewHelp:(id)sender
2167 {
2168     if( !nib_about_loaded )
2169     {
2170         nib_about_loaded = [NSBundle loadNibNamed:@"About" owner: NSApp];
2171         [o_about showHelp];
2172     }
2173     else
2174         [o_about showHelp];
2175 }
2176
2177 - (IBAction)openReadMe:(id)sender
2178 {
2179     NSString * o_path = [[NSBundle mainBundle]
2180         pathForResource: @"README.MacOSX" ofType: @"rtf"];
2181
2182     [[NSWorkspace sharedWorkspace] openFile: o_path
2183                                    withApplication: @"TextEdit"];
2184 }
2185
2186 - (IBAction)openDocumentation:(id)sender
2187 {
2188     NSURL * o_url = [NSURL URLWithString:
2189         @"http://www.videolan.org/doc/"];
2190
2191     [[NSWorkspace sharedWorkspace] openURL: o_url];
2192 }
2193
2194 - (IBAction)openWebsite:(id)sender
2195 {
2196     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
2197
2198     [[NSWorkspace sharedWorkspace] openURL: o_url];
2199 }
2200
2201 - (IBAction)openForum:(id)sender
2202 {
2203     NSURL * o_url = [NSURL URLWithString: @"http://forum.videolan.org/"];
2204
2205     [[NSWorkspace sharedWorkspace] openURL: o_url];
2206 }
2207
2208 - (IBAction)openDonate:(id)sender
2209 {
2210     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/contribute.html#paypal"];
2211
2212     [[NSWorkspace sharedWorkspace] openURL: o_url];
2213 }
2214
2215 #pragma mark -
2216 #pragma mark Crash Log
2217 - (void)sendCrashLog:(NSString *)crashLog withUserComment:(NSString *)userComment
2218 {
2219     NSString *urlStr = @"http://jones.videolan.org/crashlog/sendcrashreport.php";
2220     NSURL *url = [NSURL URLWithString:urlStr];
2221
2222     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
2223     [req setHTTPMethod:@"POST"];
2224
2225     NSString * email;
2226     if( [o_crashrep_includeEmail_ckb state] == NSOnState )
2227     {
2228         ABPerson * contact = [[ABAddressBook sharedAddressBook] me];
2229         ABMultiValue *emails = [contact valueForProperty:kABEmailProperty];
2230         email = [emails valueAtIndex:[emails indexForIdentifier:
2231                     [emails primaryIdentifier]]];
2232     }
2233     else
2234         email = [NSString string];
2235
2236     NSString *postBody;
2237     postBody = [NSString stringWithFormat:@"CrashLog=%@&Comment=%@&Email=%@\r\n",
2238             [crashLog stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
2239             [userComment stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
2240             [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
2241
2242     [req setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
2243
2244     /* Released from delegate */
2245     crashLogURLConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
2246 }
2247
2248 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
2249 {
2250     NSRunInformationalAlertPanel(_NS("Crash Report successfully sent"),
2251                 _NS("Thanks for your report!"),
2252                 _NS("OK"), nil, nil, nil);
2253     [crashLogURLConnection release];
2254     crashLogURLConnection = nil;
2255 }
2256
2257 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
2258 {
2259     NSRunCriticalAlertPanel(_NS("Error when sending the Crash Report"), [error localizedDescription], @"OK", nil, nil);
2260     [crashLogURLConnection release];
2261     crashLogURLConnection = nil;
2262 }
2263
2264 - (NSString *)latestCrashLogPathPreviouslySeen:(BOOL)previouslySeen
2265 {
2266     NSString * crashReporter = [@"~/Library/Logs/CrashReporter" stringByExpandingTildeInPath];
2267     NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:crashReporter];
2268     NSString *fname;
2269     NSString * latestLog = nil;
2270     int year  = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportYear"] : 0;
2271     int month = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportMonth"]: 0;
2272     int day   = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportDay"]  : 0;
2273     int hours = !previouslySeen ? [[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportHours"]: 0;
2274
2275     while (fname = [direnum nextObject])
2276     {
2277         [direnum skipDescendents];
2278         if([fname hasPrefix:@"VLC"] && [fname hasSuffix:@"crash"])
2279         {
2280             NSArray * compo = [fname componentsSeparatedByString:@"_"];
2281             if( [compo count] < 3 ) continue;
2282             compo = [[compo objectAtIndex:1] componentsSeparatedByString:@"-"];
2283             if( [compo count] < 4 ) continue;
2284
2285             // Dooh. ugly.
2286             if( year < [[compo objectAtIndex:0] intValue] ||
2287                 (year ==[[compo objectAtIndex:0] intValue] && 
2288                  (month < [[compo objectAtIndex:1] intValue] ||
2289                   (month ==[[compo objectAtIndex:1] intValue] &&
2290                    (day   < [[compo objectAtIndex:2] intValue] ||
2291                     (day   ==[[compo objectAtIndex:2] intValue] &&
2292                       hours < [[compo objectAtIndex:3] intValue] ))))))
2293             {
2294                 year  = [[compo objectAtIndex:0] intValue];
2295                 month = [[compo objectAtIndex:1] intValue];
2296                 day   = [[compo objectAtIndex:2] intValue];
2297                 hours = [[compo objectAtIndex:3] intValue];
2298                 latestLog = [crashReporter stringByAppendingPathComponent:fname];
2299             }
2300         }
2301     }
2302
2303     if(!(latestLog && [[NSFileManager defaultManager] fileExistsAtPath:latestLog]))
2304         return nil;
2305
2306     if( !previouslySeen )
2307     {
2308         [[NSUserDefaults standardUserDefaults] setInteger:year  forKey:@"LatestCrashReportYear"];
2309         [[NSUserDefaults standardUserDefaults] setInteger:month forKey:@"LatestCrashReportMonth"];
2310         [[NSUserDefaults standardUserDefaults] setInteger:day   forKey:@"LatestCrashReportDay"];
2311         [[NSUserDefaults standardUserDefaults] setInteger:hours forKey:@"LatestCrashReportHours"];
2312     }
2313     return latestLog;
2314 }
2315
2316 - (NSString *)latestCrashLogPath
2317 {
2318     return [self latestCrashLogPathPreviouslySeen:YES];
2319 }
2320
2321 - (void)lookForCrashLog
2322 {
2323     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
2324     // This pref key doesn't exists? this VLC is an upgrade, and this crash log come from previous version
2325     BOOL areCrashLogsTooOld = ![[NSUserDefaults standardUserDefaults] integerForKey:@"LatestCrashReportYear"];
2326     NSString * latestLog = [self latestCrashLogPathPreviouslySeen:NO];
2327     if( latestLog && !areCrashLogsTooOld )
2328         [NSApp runModalForWindow: o_crashrep_win];
2329     [o_pool release];
2330 }
2331
2332 - (IBAction)crashReporterAction:(id)sender
2333 {
2334     if( sender == o_crashrep_send_btn )
2335         [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]];
2336
2337     [NSApp stopModal];
2338     [o_crashrep_win orderOut: sender];
2339 }
2340
2341 - (IBAction)openCrashLog:(id)sender
2342 {
2343     NSString * latestLog = [self latestCrashLogPath];
2344     if( latestLog )
2345     {
2346         [[NSWorkspace sharedWorkspace] openFile: latestLog withApplication: @"Console"];
2347     }
2348     else
2349     {
2350         NSBeginInformationalAlertSheet(_NS("No CrashLog found"), _NS("Continue"), nil, nil, o_msgs_panel, self, NULL, NULL, nil, _NS("Couldn't find any trace of a previous crash.") );
2351     }
2352 }
2353
2354 #pragma mark -
2355 #pragma mark Remove old prefs
2356
2357 - (void)_removeOldPreferences
2358 {
2359     static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion";
2360     static const int kCurrentPreferencesVersion = 1;
2361     int version = [[NSUserDefaults standardUserDefaults] integerForKey:kVLCPreferencesVersion];
2362     if( version >= kCurrentPreferencesVersion ) return;
2363
2364     NSArray *libraries = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, 
2365         NSUserDomainMask, YES);
2366     if( !libraries || [libraries count] == 0) return;
2367     NSString * preferences = [[libraries objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
2368
2369     /* File not found, don't attempt anything */
2370     if(![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"VLC"]] &&
2371        ![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc.plist"]] )
2372     {
2373         [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
2374         return;
2375     }
2376
2377     int res = NSRunInformationalAlertPanel(_NS("Remove old preferences?"),
2378                 _NS("We just found an older version of VLC's preferences files."),
2379                 _NS("Move To Trash and Relaunch VLC"), _NS("Ignore"), nil, nil);
2380     if( res != NSOKButton )
2381     {
2382         [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
2383         return;
2384     }
2385
2386     NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc.plist", @"VLC", nil];
2387
2388     /* Move the file to trash so that user can find them later */
2389     [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:nil files:ourPreferences tag:0];
2390
2391     /* really reset the defaults from now on */
2392     [NSUserDefaults resetStandardUserDefaults];
2393
2394     [[NSUserDefaults standardUserDefaults] setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
2395     [[NSUserDefaults standardUserDefaults] synchronize];
2396
2397     /* Relaunch now */
2398     const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];
2399
2400     /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
2401     if(fork() != 0)
2402     {
2403         exit(0);
2404         return;
2405     }
2406     execl(path, path, NULL);
2407 }
2408
2409 #pragma mark -
2410 #pragma mark Errors, warnings and messages
2411
2412 - (IBAction)viewErrorsAndWarnings:(id)sender
2413 {
2414     [[[self getInteractionList] getErrorPanel] showPanel];
2415 }
2416
2417 - (IBAction)showMessagesPanel:(id)sender
2418 {
2419     [o_msgs_panel makeKeyAndOrderFront: sender];
2420 }
2421
2422 - (IBAction)showInformationPanel:(id)sender
2423 {
2424     if(! nib_info_loaded )
2425         nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: NSApp];
2426     
2427     [o_info initPanel];
2428 }
2429
2430 - (void)windowDidBecomeKey:(NSNotification *)o_notification
2431 {
2432     if( [o_notification object] == o_msgs_panel )
2433         [self updateMessageDisplay];
2434 }
2435
2436 - (void)updateMessageDisplay
2437 {
2438     if( [o_msgs_panel isVisible] && b_msg_arr_changed )
2439     {
2440         id o_msg;
2441         NSEnumerator * o_enum;
2442
2443         [o_messages setString: @""];
2444
2445         [o_msg_lock lock];
2446
2447         o_enum = [o_msg_arr objectEnumerator];
2448
2449         while( ( o_msg = [o_enum nextObject] ) != nil )
2450         {
2451             [o_messages insertText: o_msg];
2452         }
2453
2454         b_msg_arr_changed = NO;
2455         [o_msg_lock unlock];
2456     }
2457 }
2458
2459 - (void)libvlcMessageReceived: (NSNotification *)o_notification
2460 {
2461     NSColor *o_white = [NSColor whiteColor];
2462     NSColor *o_red = [NSColor redColor];
2463     NSColor *o_yellow = [NSColor yellowColor];
2464     NSColor *o_gray = [NSColor grayColor];
2465
2466     NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
2467     static const char * ppsz_type[4] = { ": ", " error: ",
2468     " warning: ", " debug: " };
2469
2470     NSString *o_msg;
2471     NSDictionary *o_attr;
2472     NSAttributedString *o_msg_color;
2473
2474     int i_type = [[[o_notification userInfo] objectForKey: @"Type"] intValue];
2475
2476     [o_msg_lock lock];
2477
2478     if( [o_msg_arr count] + 2 > 400 )
2479     {
2480         unsigned rid[] = { 0, 1 };
2481         [o_msg_arr removeObjectsFromIndices: (unsigned *)&rid
2482                                  numIndices: sizeof(rid)/sizeof(rid[0])];
2483     }
2484
2485     o_attr = [NSDictionary dictionaryWithObject: o_gray
2486                                          forKey: NSForegroundColorAttributeName];
2487     o_msg = [NSString stringWithFormat: @"%@%s",
2488              [[o_notification userInfo] objectForKey: @"Module"],
2489              ppsz_type[i_type]];
2490     o_msg_color = [[NSAttributedString alloc]
2491                    initWithString: o_msg attributes: o_attr];
2492     [o_msg_arr addObject: [o_msg_color autorelease]];
2493
2494     o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]
2495                                          forKey: NSForegroundColorAttributeName];
2496     o_msg = [[[o_notification userInfo] objectForKey: @"Message"] stringByAppendingString: @"\n"];
2497     o_msg_color = [[NSAttributedString alloc]
2498                    initWithString: o_msg attributes: o_attr];
2499     [o_msg_arr addObject: [o_msg_color autorelease]];
2500
2501     b_msg_arr_changed = YES;
2502     [o_msg_lock unlock];
2503 }
2504
2505 - (IBAction)saveDebugLog:(id)sender
2506 {
2507     NSOpenPanel * saveFolderPanel = [[NSSavePanel alloc] init];
2508     
2509     [saveFolderPanel setCanChooseDirectories: NO];
2510     [saveFolderPanel setCanChooseFiles: YES];
2511     [saveFolderPanel setCanSelectHiddenExtension: NO];
2512     [saveFolderPanel setCanCreateDirectories: YES];
2513     [saveFolderPanel setRequiredFileType: @"rtfd"];
2514     [saveFolderPanel beginSheetForDirectory:nil file: [NSString stringWithFormat: _NS("VLC Debug Log (%s).rtfd"), VLC_Version()] modalForWindow: o_msgs_panel modalDelegate:self didEndSelector:@selector(saveDebugLogAsRTF:returnCode:contextInfo:) contextInfo:nil];
2515 }
2516
2517 - (void)saveDebugLogAsRTF: (NSSavePanel *)sheet returnCode: (int)returnCode contextInfo: (void *)contextInfo
2518 {
2519     BOOL b_returned;
2520     if( returnCode == NSOKButton )
2521     {
2522         b_returned = [o_messages writeRTFDToFile: [sheet filename] atomically: YES];
2523         if(! b_returned )
2524             msg_Warn( p_intf, "Error while saving the debug log" );
2525     }
2526 }
2527
2528 #pragma mark -
2529 #pragma mark Playlist toggling
2530
2531 - (IBAction)togglePlaylist:(id)sender
2532 {
2533     NSRect contentRect = [o_window contentRectForFrameRect:[o_window frame]];
2534     NSRect o_rect = [o_window contentRectForFrameRect:[o_window frame]];
2535     /*First, check if the playlist is visible*/
2536     if( contentRect.size.height <= 169. )
2537     {
2538         o_restore_rect = contentRect;
2539         b_restore_size = true;
2540         b_small_window = YES; /* we know we are small, make sure this is actually set (see case below) */
2541
2542         /* make large */
2543         if( o_size_with_playlist.height > 169. )
2544             o_rect.size.height = o_size_with_playlist.height;
2545         else
2546             o_rect.size.height = 500.;
2547  
2548         if( o_size_with_playlist.width >= [o_window contentMinSize].width )
2549             o_rect.size.width = o_size_with_playlist.width;
2550         else
2551             o_rect.size.width = [o_window contentMinSize].width;
2552
2553         o_rect.origin.x = contentRect.origin.x;
2554         o_rect.origin.y = contentRect.origin.y - o_rect.size.height +
2555             [o_window contentMinSize].height;
2556
2557         o_rect = [o_window frameRectForContentRect:o_rect];
2558
2559         NSRect screenRect = [[o_window screen] visibleFrame];
2560         if( !NSContainsRect( screenRect, o_rect ) ) {
2561             if( NSMaxX(o_rect) > NSMaxX(screenRect) )
2562                 o_rect.origin.x = ( NSMaxX(screenRect) - o_rect.size.width );
2563             if( NSMinY(o_rect) < NSMinY(screenRect) )
2564                 o_rect.origin.y = ( NSMinY(screenRect) );
2565         }
2566
2567         [o_btn_playlist setState: YES];
2568     }
2569     else
2570     {
2571         NSSize curSize = o_rect.size;
2572         if( b_restore_size )
2573         {
2574             o_rect = o_restore_rect;
2575             if( o_rect.size.height < [o_window contentMinSize].height )
2576                 o_rect.size.height = [o_window contentMinSize].height;
2577             if( o_rect.size.width < [o_window contentMinSize].width )
2578                 o_rect.size.width = [o_window contentMinSize].width;
2579         }
2580         else
2581         {
2582             NSRect contentRect = [o_window contentRectForFrameRect:[o_window frame]];
2583             /* make small */
2584             o_rect.size.height = [o_window contentMinSize].height;
2585             o_rect.size.width = [o_window contentMinSize].width;
2586             o_rect.origin.x = contentRect.origin.x;
2587             /* Calculate the position of the lower right corner after resize */
2588             o_rect.origin.y = contentRect.origin.y +
2589                 contentRect.size.height - [o_window contentMinSize].height;
2590         }
2591
2592         [o_playlist_view setAutoresizesSubviews: NO];
2593         [o_playlist_view removeFromSuperview];
2594         [o_btn_playlist setState: NO];
2595         b_small_window = NO; /* we aren't small here just yet. we are doing an animated resize after this */
2596         o_rect = [o_window frameRectForContentRect:o_rect];
2597     }
2598
2599     [o_window setFrame: o_rect display:YES animate: YES];
2600 }
2601
2602 - (void)updateTogglePlaylistState
2603 {
2604     if( [o_window contentRectForFrameRect:[o_window frame]].size.height <= 169. )
2605         [o_btn_playlist setState: NO];
2606     else
2607         [o_btn_playlist setState: YES];
2608
2609     [[self getPlaylist] outlineViewSelectionDidChange: NULL];
2610 }
2611
2612 - (NSSize)windowWillResize:(NSWindow *)sender toSize:(NSSize)proposedFrameSize
2613 {
2614
2615     /* Not triggered on a window resize or maxification of the window. only by window mouse dragging resize */
2616
2617    /*Stores the size the controller one resize, to be able to restore it when
2618      toggling the playlist*/
2619     o_size_with_playlist = proposedFrameSize;
2620
2621     NSRect rect;
2622     rect.size = proposedFrameSize;
2623     if( [o_window contentRectForFrameRect:rect].size.height <= 169. )
2624     {
2625         if( b_small_window == NO )
2626         {
2627             /* if large and going to small then hide */
2628             b_small_window = YES;
2629             [o_playlist_view setAutoresizesSubviews: NO];
2630             [o_playlist_view removeFromSuperview];
2631         }
2632         return NSMakeSize( proposedFrameSize.width, [o_window minSize].height);
2633     }
2634     return proposedFrameSize;
2635 }
2636
2637 - (void)windowDidMove:(NSNotification *)notif
2638 {
2639     b_restore_size = false;
2640 }
2641
2642 - (void)windowDidResize:(NSNotification *)notif
2643 {
2644     if( [o_window contentRectForFrameRect:[o_window frame]].size.height > 169. && b_small_window )
2645     {
2646         /* If large and coming from small then show */
2647         [o_playlist_view setAutoresizesSubviews: YES];
2648         NSRect contentRect = [o_window contentRectForFrameRect:[o_window frame]];
2649         [o_playlist_view setFrame: NSMakeRect( 0, 0, contentRect.size.width, contentRect.size.height - [o_window contentMinSize].height )];
2650         [o_playlist_view setNeedsDisplay:YES];
2651         [[o_window contentView] addSubview: o_playlist_view];
2652         b_small_window = NO;
2653     }
2654     [self updateTogglePlaylistState];
2655 }
2656
2657 #pragma mark -
2658
2659 @end
2660
2661 @implementation VLCMain (NSMenuValidation)
2662
2663 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
2664 {
2665     NSString *o_title = [o_mi title];
2666     BOOL bEnabled = TRUE;
2667
2668     /* Recent Items Menu */
2669     if( [o_title isEqualToString: _NS("Clear Menu")] )
2670     {
2671         NSMenu * o_menu = [o_mi_open_recent submenu];
2672         int i_nb_items = [o_menu numberOfItems];
2673         NSArray * o_docs = [[NSDocumentController sharedDocumentController]
2674                                                        recentDocumentURLs];
2675         UInt32 i_nb_docs = [o_docs count];
2676
2677         if( i_nb_items > 1 )
2678         {
2679             while( --i_nb_items )
2680             {
2681                 [o_menu removeItemAtIndex: 0];
2682             }
2683         }
2684
2685         if( i_nb_docs > 0 )
2686         {
2687             NSURL * o_url;
2688             NSString * o_doc;
2689
2690             [o_menu insertItem: [NSMenuItem separatorItem] atIndex: 0];
2691
2692             while( TRUE )
2693             {
2694                 i_nb_docs--;
2695
2696                 o_url = [o_docs objectAtIndex: i_nb_docs];
2697
2698                 if( [o_url isFileURL] )
2699                 {
2700                     o_doc = [o_url path];
2701                 }
2702                 else
2703                 {
2704                     o_doc = [o_url absoluteString];
2705                 }
2706
2707                 [o_menu insertItemWithTitle: o_doc
2708                     action: @selector(openRecentItem:)
2709                     keyEquivalent: @"" atIndex: 0];
2710
2711                 if( i_nb_docs == 0 )
2712                 {
2713                     break;
2714                 }
2715             }
2716         }
2717         else
2718         {
2719             bEnabled = FALSE;
2720         }
2721     }
2722     return( bEnabled );
2723 }
2724
2725 @end
2726
2727 @implementation VLCMain (Internal)
2728
2729 - (void)handlePortMessage:(NSPortMessage *)o_msg
2730 {
2731     id ** val;
2732     NSData * o_data;
2733     NSValue * o_value;
2734     NSInvocation * o_inv;
2735     NSConditionLock * o_lock;
2736
2737     o_data = [[o_msg components] lastObject];
2738     o_inv = *((NSInvocation **)[o_data bytes]);
2739     [o_inv getArgument: &o_value atIndex: 2];
2740     val = (id **)[o_value pointerValue];
2741     [o_inv setArgument: val[1] atIndex: 2];
2742     o_lock = *(val[0]);
2743
2744     [o_lock lock];
2745     [o_inv invoke];
2746     [o_lock unlockWithCondition: 1];
2747 }
2748
2749 @end