]> git.sesse.net Git - vlc/blob - modules/gui/macosx/intf.m
macosx: move vout initialisation to vout controller
[vlc] / modules / gui / macosx / intf.m
1 /*****************************************************************************
2  * intf.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2002-2012 VLC authors and VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Christophe Massiot <massiot@via.ecp.fr>
9  *          Derk-Jan Hartman <hartman at videolan.org>
10  *          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 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <stdlib.h>                                      /* malloc(), free() */
35 #include <sys/param.h>                                    /* for MAXPATHLEN */
36 #include <string.h>
37 #include <vlc_common.h>
38 #include <vlc_keys.h>
39 #include <vlc_dialog.h>
40 #include <vlc_url.h>
41 #include <vlc_modules.h>
42 #include <vlc_plugin.h>
43 #include <vlc_aout_intf.h>
44 #include <vlc_vout_display.h>
45 #include <unistd.h> /* execl() */
46
47 #import "CompatibilityFixes.h"
48 #import "intf.h"
49 #import "StringUtility.h"
50 #import "MainMenu.h"
51 #import "VideoView.h"
52 #import "prefs.h"
53 #import "playlist.h"
54 #import "playlistinfo.h"
55 #import "controls.h"
56 #import "open.h"
57 #import "wizard.h"
58 #import "bookmarks.h"
59 #import "coredialogs.h"
60 #import "AppleRemote.h"
61 #import "eyetv.h"
62 #import "simple_prefs.h"
63 #import "CoreInteraction.h"
64 #import "TrackSynchronization.h"
65 #import "VLCVoutWindowController.h"
66
67 #import <AddressBook/AddressBook.h>         /* for crashlog send mechanism */
68 #import <Sparkle/Sparkle.h>                 /* we're the update delegate */
69
70 /*****************************************************************************
71  * Local prototypes.
72  *****************************************************************************/
73 static void Run (intf_thread_t *p_intf);
74
75 static void updateProgressPanel (void *, const char *, float);
76 static bool checkProgressPanel (void *);
77 static void destroyProgressPanel (void *);
78
79 static void MsgCallback(void *data, int type, const msg_item_t *item, const char *format, va_list ap);
80
81 static int InputEvent(vlc_object_t *, const char *,
82                       vlc_value_t, vlc_value_t, void *);
83 static int PLItemChanged(vlc_object_t *, const char *,
84                          vlc_value_t, vlc_value_t, void *);
85 static int PlaylistUpdated(vlc_object_t *, const char *,
86                            vlc_value_t, vlc_value_t, void *);
87 static int PlaybackModeUpdated(vlc_object_t *, const char *,
88                                vlc_value_t, vlc_value_t, void *);
89 static int VolumeUpdated(vlc_object_t *, const char *,
90                          vlc_value_t, vlc_value_t, void *);
91
92 #pragma mark -
93 #pragma mark VLC Interface Object Callbacks
94
95 /*****************************************************************************
96  * OpenIntf: initialize interface
97  *****************************************************************************/
98 int OpenIntf (vlc_object_t *p_this)
99 {
100     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
101     [VLCApplication sharedApplication];
102
103     intf_thread_t *p_intf = (intf_thread_t*) p_this;
104
105     p_intf->p_sys = malloc(sizeof(intf_sys_t));
106     if (p_intf->p_sys == NULL)
107         return VLC_ENOMEM;
108
109     memset(p_intf->p_sys, 0, sizeof(*p_intf->p_sys));
110
111     /* subscribe to LibVLCCore's messages */
112     vlc_Subscribe(&p_intf->p_sys->sub, MsgCallback, NULL);
113
114     Run(p_intf);
115
116     [o_pool release];
117     return VLC_SUCCESS;
118 }
119
120 /*****************************************************************************
121  * CloseIntf: destroy interface
122  *****************************************************************************/
123 void CloseIntf (vlc_object_t *p_this)
124 {
125     intf_thread_t *p_intf = (intf_thread_t*) p_this;
126
127     free(p_intf->p_sys);
128 }
129
130 static int WindowControl(vout_window_t *, int i_query, va_list);
131
132 int WindowOpen(vout_window_t *p_wnd, const vout_window_cfg_t *cfg)
133 {
134     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
135     intf_thread_t *p_intf = VLCIntf;
136     if (!p_intf) {
137         msg_Err(p_wnd, "Mac OS X interface not found");
138         return VLC_EGENERIC;
139     }
140
141     int i_x = cfg->x;
142     int i_y = cfg->y;
143     unsigned i_width = cfg->width;
144     unsigned i_height = cfg->height;
145     p_wnd->handle.nsobject = [[VLCMain sharedInstance] getVideoViewAtPositionX: &i_x Y: &i_y withWidth: &i_width andHeight: &i_height forWindow: p_wnd];
146
147     if (!p_wnd->handle.nsobject) {
148         msg_Err(p_wnd, "got no video view from the interface");
149         [o_pool release];
150         return VLC_EGENERIC;
151     }
152
153     // TODO: this seems to be strange. Why not just allocating in the right size?
154     // This could avoid strange resize-animations...
155     NSSize newSize = NSMakeSize(cfg->width, cfg->height);
156     SEL sel = @selector(setNativeVideoSize:forWindow:);
157     NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]];
158     [inv setTarget:[[VLCMain sharedInstance] voutController]];
159     [inv setSelector:sel];
160     [inv setArgument:&newSize atIndex:2]; // starting at 2!
161     [inv setArgument:&p_wnd atIndex:3];
162     [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
163                        waitUntilDone:NO];
164
165     // TODO: find a cleaner way for "start in fullscreen"
166     if (var_GetBool(pl_Get(VLCIntf), "fullscreen")) {
167         int i_full = 1;
168
169         SEL sel = @selector(setFullscreen:forWindow:);
170         NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[VLCMain sharedInstance] methodSignatureForSelector:sel]];
171         [inv setTarget:[VLCMain sharedInstance]];
172         [inv setSelector:sel];
173         [inv setArgument:&i_full atIndex:2];
174         [inv setArgument:&p_wnd atIndex:3];
175         [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
176                            waitUntilDone:NO];
177     }
178
179     [[VLCMain sharedInstance] setActiveVideoPlayback: YES];
180     p_wnd->control = WindowControl;
181     p_wnd->sys = (vout_window_sys_t *)VLCIntf;
182     [o_pool release];
183     return VLC_SUCCESS;
184 }
185
186 static int WindowControl(vout_window_t *p_wnd, int i_query, va_list args)
187 {
188     switch(i_query) {
189         case VOUT_WINDOW_SET_STATE:
190         {
191             unsigned i_state = va_arg(args, unsigned);
192             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(setWindowLevel:) withObject:[NSNumber numberWithUnsignedInt:i_state] waitUntilDone:NO];
193             return VLC_SUCCESS;
194         }
195         case VOUT_WINDOW_SET_SIZE:
196         {
197             NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
198
199             unsigned int i_width  = va_arg(args, unsigned int);
200             unsigned int i_height = va_arg(args, unsigned int);
201
202             NSSize newSize = NSMakeSize(i_width, i_height);            
203             SEL sel = @selector(setNativeVideoSize:forWindow:);
204             NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[[VLCMain sharedInstance] voutController] methodSignatureForSelector:sel]];
205             [inv setTarget:[[VLCMain sharedInstance] voutController]];
206             [inv setSelector:sel];
207             [inv setArgument:&newSize atIndex:2]; // starting at 2!
208             [inv setArgument:&p_wnd atIndex:3];
209             [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
210                                waitUntilDone:NO];
211
212             [o_pool release];
213             return VLC_SUCCESS;
214         }
215         case VOUT_WINDOW_SET_FULLSCREEN:
216         {
217             NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
218             int i_full = va_arg(args, int);
219
220             SEL sel = @selector(setFullscreen:forWindow:);
221             NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[[VLCMain sharedInstance] methodSignatureForSelector:sel]];
222             [inv setTarget:[VLCMain sharedInstance]];
223             [inv setSelector:sel];
224             [inv setArgument:&i_full atIndex:2]; // starting at 2!
225             [inv setArgument:&p_wnd atIndex:3];
226             [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
227                                waitUntilDone:NO];
228
229
230             //[[VLCMain sharedInstance] performSelectorOnMainThread:@selector(fullscreenChanged:) withObject:[NSValue valueWithPointer:p_wnd] waitUntilDone:NO];
231             [o_pool release];
232             return VLC_SUCCESS;
233         }
234         default:
235             msg_Warn(p_wnd, "unsupported control query");
236             return VLC_EGENERIC;
237     }
238 }
239
240 void WindowClose(vout_window_t *p_wnd)
241 {
242     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
243     [[VLCMain sharedInstance] setActiveVideoPlayback:NO];
244
245     [[[VLCMain sharedInstance] voutController] performSelectorOnMainThread:@selector(removeVoutforDisplay:) withObject:[NSValue valueWithPointer:p_wnd] waitUntilDone:NO];
246
247     [o_pool release];
248 }
249
250 /*****************************************************************************
251  * Run: main loop
252  *****************************************************************************/
253 static NSLock * o_appLock = nil;    // controls access to f_appExit
254
255 static void Run(intf_thread_t *p_intf)
256 {
257     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
258     [VLCApplication sharedApplication];
259
260     o_appLock = [[NSLock alloc] init];
261
262     [[VLCMain sharedInstance] setIntf: p_intf];
263     [NSBundle loadNibNamed: @"MainMenu" owner: NSApp];
264
265     [NSApp run];
266     [[VLCMain sharedInstance] applicationWillTerminate:nil];
267     [o_appLock release];
268     [o_pool release];
269
270     raise(SIGTERM);
271 }
272
273 #pragma mark -
274 #pragma mark Variables Callback
275
276 /*****************************************************************************
277  * MsgCallback: Callback triggered by the core once a new debug message is
278  * ready to be displayed. We store everything in a NSArray in our Cocoa part
279  * of this file.
280  *****************************************************************************/
281 static void MsgCallback(void *data, int type, const msg_item_t *item, const char *format, va_list ap)
282 {
283     int canc = vlc_savecancel();
284     char *str;
285
286     if (vasprintf(&str, format, ap) == -1) {
287         vlc_restorecancel(canc);
288         return;
289     }
290
291     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
292     [[VLCMain sharedInstance] processReceivedlibvlcMessage: item ofType: type withStr: str];
293     [o_pool release];
294
295     vlc_restorecancel(canc);
296     free(str);
297 }
298
299 static int InputEvent(vlc_object_t *p_this, const char *psz_var,
300                        vlc_value_t oldval, vlc_value_t new_val, void *param)
301 {
302     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
303     switch (new_val.i_int) {
304         case INPUT_EVENT_STATE:
305             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackStatusUpdated) withObject: nil waitUntilDone:NO];
306             break;
307         case INPUT_EVENT_RATE:
308             [[[VLCMain sharedInstance] mainMenu] performSelectorOnMainThread:@selector(updatePlaybackRate) withObject: nil waitUntilDone:NO];
309             break;
310         case INPUT_EVENT_POSITION:
311             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject: nil waitUntilDone:NO];
312             break;
313         case INPUT_EVENT_TITLE:
314         case INPUT_EVENT_CHAPTER:
315             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
316             break;
317         case INPUT_EVENT_CACHE:
318             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
319             break;
320         case INPUT_EVENT_STATISTICS:
321             [[[VLCMain sharedInstance] info] performSelectorOnMainThread:@selector(updateStatistics) withObject: nil waitUntilDone: NO];
322             break;
323         case INPUT_EVENT_ES:
324             break;
325         case INPUT_EVENT_TELETEXT:
326             break;
327         case INPUT_EVENT_AOUT:
328             break;
329         case INPUT_EVENT_VOUT:
330             break;
331         case INPUT_EVENT_ITEM_META:
332         case INPUT_EVENT_ITEM_INFO:
333             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
334             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
335             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateInfoandMetaPanel) withObject: nil waitUntilDone:NO];
336             break;
337         case INPUT_EVENT_BOOKMARK:
338             break;
339         case INPUT_EVENT_RECORD:
340             [[VLCMain sharedInstance] updateRecordState: var_GetBool(p_this, "record")];
341             break;
342         case INPUT_EVENT_PROGRAM:
343             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainMenu) withObject: nil waitUntilDone:NO];
344             break;
345         case INPUT_EVENT_ITEM_EPG:
346             break;
347         case INPUT_EVENT_SIGNAL:
348             break;
349
350         case INPUT_EVENT_ITEM_NAME:
351             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
352             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playlistUpdated) withObject: nil waitUntilDone:NO];
353             break;
354
355         case INPUT_EVENT_AUDIO_DELAY:
356         case INPUT_EVENT_SUBTITLE_DELAY:
357             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateDelays) withObject:nil waitUntilDone:NO];
358             break;
359
360         case INPUT_EVENT_DEAD:
361             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
362             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO];
363             break;
364
365         case INPUT_EVENT_ABORT:
366             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateName) withObject: nil waitUntilDone:NO];
367             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updatePlaybackPosition) withObject:nil waitUntilDone:NO];
368             break;
369
370         default:
371             //msg_Warn(p_this, "unhandled input event (%lld)", new_val.i_int);
372             break;
373     }
374
375     [o_pool release];
376     return VLC_SUCCESS;
377 }
378
379 static int PLItemChanged(vlc_object_t *p_this, const char *psz_var,
380                          vlc_value_t oldval, vlc_value_t new_val, void *param)
381 {
382     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
383     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(PlaylistItemChanged) withObject:nil waitUntilDone:NO];
384
385     [o_pool release];
386     return VLC_SUCCESS;
387 }
388
389 static int PlaylistUpdated(vlc_object_t *p_this, const char *psz_var,
390                          vlc_value_t oldval, vlc_value_t new_val, void *param)
391 {
392     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
393     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playlistUpdated) withObject:nil waitUntilDone:NO];
394
395     [o_pool release];
396     return VLC_SUCCESS;
397 }
398
399 static int PlaybackModeUpdated(vlc_object_t *p_this, const char *psz_var,
400                          vlc_value_t oldval, vlc_value_t new_val, void *param)
401 {
402     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
403     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(playbackModeUpdated) withObject:nil waitUntilDone:NO];
404
405     [o_pool release];
406     return VLC_SUCCESS;
407 }
408
409 static int VolumeUpdated(vlc_object_t *p_this, const char *psz_var,
410                          vlc_value_t oldval, vlc_value_t new_val, void *param)
411 {
412     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
413     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateVolume) withObject:nil waitUntilDone:NO];
414
415     [o_pool release];
416     return VLC_SUCCESS;
417 }
418
419 /*****************************************************************************
420  * ShowController: Callback triggered by the show-intf playlist variable
421  * through the ShowIntf-control-intf, to let us show the controller-win;
422  * usually when in fullscreen-mode
423  *****************************************************************************/
424 static int ShowController(vlc_object_t *p_this, const char *psz_variable,
425                      vlc_value_t old_val, vlc_value_t new_val, void *param)
426 {
427     intf_thread_t * p_intf = VLCIntf;
428     if (p_intf && p_intf->p_sys) {
429         playlist_t * p_playlist = pl_Get(p_intf);
430         BOOL b_fullscreen = var_GetBool(p_playlist, "fullscreen");
431         if (strcmp(psz_variable, "intf-toggle-fscontrol") || b_fullscreen)
432             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
433         else
434             [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(showMainWindow) withObject:nil waitUntilDone:NO];
435     }
436     return VLC_SUCCESS;
437 }
438
439 /*****************************************************************************
440  * DialogCallback: Callback triggered by the "dialog-*" variables
441  * to let the intf display error and interaction dialogs
442  *****************************************************************************/
443 static int DialogCallback(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
444 {
445     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
446     VLCMain *interface = (VLCMain *)data;
447
448     if ([[NSString stringWithUTF8String: type] isEqualToString: @"dialog-progress-bar"]) {
449         /* the progress panel needs to update itself and therefore wants special treatment within this context */
450         dialog_progress_bar_t *p_dialog = (dialog_progress_bar_t *)value.p_address;
451
452         p_dialog->pf_update = updateProgressPanel;
453         p_dialog->pf_check = checkProgressPanel;
454         p_dialog->pf_destroy = destroyProgressPanel;
455         p_dialog->p_sys = VLCIntf->p_libvlc;
456     }
457
458     NSValue *o_value = [NSValue valueWithPointer:value.p_address];
459     [[VLCCoreDialogProvider sharedInstance] performEventWithObject: o_value ofType: type];
460
461     [o_pool release];
462     return VLC_SUCCESS;
463 }
464
465 void updateProgressPanel (void *priv, const char *text, float value)
466 {
467     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
468
469     NSString *o_txt;
470     if (text != NULL)
471         o_txt = [NSString stringWithUTF8String: text];
472     else
473         o_txt = @"";
474
475     [[[VLCMain sharedInstance] coreDialogProvider] updateProgressPanelWithText: o_txt andNumber: (double)(value * 1000.)];
476
477     [o_pool release];
478 }
479
480 void destroyProgressPanel (void *priv)
481 {
482     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
483     [[[VLCMain sharedInstance] coreDialogProvider] performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];
484     [o_pool release];
485 }
486
487 bool checkProgressPanel (void *priv)
488 {
489     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
490     return [[[VLCMain sharedInstance] coreDialogProvider] progressCancelled];
491     [o_pool release];
492 }
493
494 #pragma mark -
495 #pragma mark Helpers
496
497 input_thread_t *getInput(void)
498 {
499     intf_thread_t *p_intf = VLCIntf;
500     if (!p_intf)
501         return NULL;
502     return pl_CurrentInput(p_intf);
503 }
504
505 vout_thread_t *getVout(void)
506 {
507     input_thread_t *p_input = getInput();
508     if (!p_input)
509         return NULL;
510     vout_thread_t *p_vout = input_GetVout(p_input);
511     vlc_object_release(p_input);
512     return p_vout;
513 }
514
515 vout_thread_t *getVoutForActiveWindow(void)
516 {
517     vout_thread_t *p_vout = nil;
518
519     id currentWindow = [NSApp keyWindow];
520     if ([currentWindow respondsToSelector:@selector(videoView)]) {
521         VLCVoutView *videoView = [currentWindow videoView];
522         if (videoView) {
523             p_vout = [videoView voutThread];
524         }
525     }
526
527     if (!p_vout)
528         p_vout = getVout();
529
530     return p_vout;
531 }
532
533 audio_output_t *getAout(void)
534 {
535     input_thread_t *p_input = getInput();
536     if (!p_input)
537         return NULL;
538     audio_output_t *p_aout = input_GetAout(p_input);
539     vlc_object_release(p_input);
540     return p_aout;
541 }
542
543 #pragma mark -
544 #pragma mark Private
545
546 @interface VLCMain ()
547 - (void)_removeOldPreferences;
548 @end
549
550 /*****************************************************************************
551  * VLCMain implementation
552  *****************************************************************************/
553 @implementation VLCMain
554
555 @synthesize voutController=o_vout_controller;
556 @synthesize nativeFullscreenMode=b_nativeFullscreenMode;
557
558 #pragma mark -
559 #pragma mark Initialization
560
561 static VLCMain *_o_sharedMainInstance = nil;
562
563 + (VLCMain *)sharedInstance
564 {
565     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
566 }
567
568 - (id)init
569 {
570     if (_o_sharedMainInstance) {
571         [self dealloc];
572         return _o_sharedMainInstance;
573     } else
574         _o_sharedMainInstance = [super init];
575
576     p_intf = NULL;
577     p_current_input = NULL;
578
579     o_msg_lock = [[NSLock alloc] init];
580     o_msg_arr = [[NSMutableArray arrayWithCapacity: 600] retain];
581
582     o_open = [[VLCOpen alloc] init];
583     o_coredialogs = [[VLCCoreDialogProvider alloc] init];
584     o_info = [[VLCInfo alloc] init];
585     o_mainmenu = [[VLCMainMenu alloc] init];
586     o_coreinteraction = [[VLCCoreInteraction alloc] init];
587     o_eyetv = [[VLCEyeTVController alloc] init];
588     o_mainwindow = [[VLCMainWindow alloc] init];
589
590     /* announce our launch to a potential eyetv plugin */
591     [[NSDistributedNotificationCenter defaultCenter] postNotificationName: @"VLCOSXGUIInit"
592                                                                    object: @"VLCEyeTVSupport"
593                                                                  userInfo: NULL
594                                                        deliverImmediately: YES];
595
596     NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
597     NSDictionary *appDefaults = [NSDictionary dictionaryWithObject:@"NO" forKey:@"LiveUpdateTheMessagesPanel"];
598     [defaults registerDefaults:appDefaults];
599
600     o_vout_controller = [[VLCVoutWindowController alloc] init];
601
602     return _o_sharedMainInstance;
603 }
604
605 - (void)setIntf: (intf_thread_t *)p_mainintf
606 {
607     p_intf = p_mainintf;
608 }
609
610 - (intf_thread_t *)intf
611 {
612     return p_intf;
613 }
614
615 - (void)awakeFromNib
616 {
617     playlist_t *p_playlist;
618     vlc_value_t val;
619     if (!p_intf) return;
620     var_Create(p_intf, "intf-change", VLC_VAR_BOOL);
621
622     /* Check if we already did this once. Opening the other nibs calls it too,
623      because VLCMain is the owner */
624     if (nib_main_loaded)
625         return;
626
627     [o_msgs_panel setExcludedFromWindowsMenu: YES];
628     [o_msgs_panel setDelegate: self];
629
630     p_playlist = pl_Get(p_intf);
631
632     val.b_bool = false;
633
634     var_AddCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
635     var_AddCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
636     //    var_AddCallback(p_playlist, "item-change", PLItemChanged, self);
637     var_AddCallback(p_playlist, "item-current", PLItemChanged, self);
638     var_AddCallback(p_playlist, "activity", PLItemChanged, self);
639     var_AddCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self);
640     var_AddCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self);
641     var_AddCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self);
642     var_AddCallback(p_playlist, "random", PlaybackModeUpdated, self);
643     var_AddCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
644     var_AddCallback(p_playlist, "loop", PlaybackModeUpdated, self);
645     var_AddCallback(p_playlist, "volume", VolumeUpdated, self);
646     var_AddCallback(p_playlist, "mute", VolumeUpdated, self);
647
648     if (!OSX_SNOW_LEOPARD) {
649         if ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)
650             var_SetBool(p_playlist, "fullscreen", YES);
651     }
652
653     /* load our Core and Shared Dialogs nibs */
654     nib_coredialogs_loaded = [NSBundle loadNibNamed:@"CoreDialogs" owner: NSApp];
655     [NSBundle loadNibNamed:@"SharedDialogs" owner: NSApp];
656
657     /* subscribe to various interactive dialogues */
658     var_Create(p_intf, "dialog-error", VLC_VAR_ADDRESS);
659     var_AddCallback(p_intf, "dialog-error", DialogCallback, self);
660     var_Create(p_intf, "dialog-critical", VLC_VAR_ADDRESS);
661     var_AddCallback(p_intf, "dialog-critical", DialogCallback, self);
662     var_Create(p_intf, "dialog-login", VLC_VAR_ADDRESS);
663     var_AddCallback(p_intf, "dialog-login", DialogCallback, self);
664     var_Create(p_intf, "dialog-question", VLC_VAR_ADDRESS);
665     var_AddCallback(p_intf, "dialog-question", DialogCallback, self);
666     var_Create(p_intf, "dialog-progress-bar", VLC_VAR_ADDRESS);
667     var_AddCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
668     dialog_Register(p_intf);
669
670     /* init Apple Remote support */
671     o_remote = [[AppleRemote alloc] init];
672     [o_remote setClickCountEnabledButtons: kRemoteButtonPlay];
673     [o_remote setDelegate: _o_sharedMainInstance];
674
675     [o_msgs_refresh_btn setImage: [NSImage imageNamed: NSImageNameRefreshTemplate]];
676
677     /* yeah, we are done */
678     b_nativeFullscreenMode = NO;
679 #ifdef MAC_OS_X_VERSION_10_7
680     if (!OSX_SNOW_LEOPARD)
681         b_nativeFullscreenMode = var_InheritBool(p_intf, "macosx-nativefullscreenmode");
682 #endif
683
684     /* recover stored audio device, if set
685      * in case it was unplugged in the meantime, auhal will fall back on the default */
686     int i_value = config_GetInt(p_intf, "macosx-audio-device");
687     if (i_value > 0)
688         var_SetInteger(pl_Get(VLCIntf), "audio-device", i_value);
689
690     if (config_GetInt(VLCIntf, "macosx-icon-change")) {
691         /* After day 354 of the year, the usual VLC cone is replaced by another cone
692          * wearing a Father Xmas hat.
693          * Note: this icon doesn't represent an endorsement of The Coca-Cola Company.
694          */
695         NSCalendar *gregorian =
696         [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
697         NSUInteger dayOfYear = [gregorian ordinalityOfUnit:NSDayCalendarUnit inUnit:NSYearCalendarUnit forDate:[NSDate date]];
698         [gregorian release];
699
700         if (dayOfYear >= 354)
701             [[VLCApplication sharedApplication] setApplicationIconImage: [NSImage imageNamed:@"vlc-xmas"]];
702     }
703
704     [self initStrings];
705
706     nib_main_loaded = TRUE;
707 }
708
709 - (void)applicationDidFinishLaunching:(NSNotification *)aNotification
710 {
711     if (!p_intf)
712         return;
713
714     [self updateCurrentlyUsedHotkeys];
715
716     /* init media key support */
717     b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys");
718     if (b_mediaKeySupport) {
719         o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
720         [o_mediaKeyController startWatchingMediaKeys];
721         [[NSUserDefaults standardUserDefaults] registerDefaults:[NSDictionary dictionaryWithObjectsAndKeys:
722                                                                  [SPMediaKeyTap defaultMediaKeyUserBundleIdentifiers], kMediaKeyUsingBundleIdentifiersDefaultsKey,
723                                                                  nil]];
724     }
725     [[NSNotificationCenter defaultCenter] addObserver: self selector: @selector(coreChangedMediaKeySupportSetting:) name: @"VLCMediaKeySupportSettingChanged" object: nil];
726
727     [self _removeOldPreferences];
728
729     /* Handle sleep notification */
730     [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:self selector:@selector(computerWillSleep:)
731            name:NSWorkspaceWillSleepNotification object:nil];
732
733     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(lookForCrashLog) withObject:nil waitUntilDone:NO];
734
735     /* we will need this, so let's load it here so the interface appears to be more responsive */
736     nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
737
738     /* update the main window */
739     [o_mainwindow updateWindow];
740     [o_mainwindow updateTimeSlider];
741     [o_mainwindow updateVolumeSlider];
742 }
743
744 - (void)initStrings
745 {
746     if (!p_intf)
747         return;
748
749     /* messages panel */
750     [o_msgs_panel setTitle: _NS("Messages")];
751     [o_msgs_crashlog_btn setTitle: _NS("Open CrashLog...")];
752     [o_msgs_save_btn setTitle: _NS("Save this Log...")];
753
754     /* crash reporter panel */
755     [o_crashrep_send_btn setTitle: _NS("Send")];
756     [o_crashrep_dontSend_btn setTitle: _NS("Don't Send")];
757     [o_crashrep_title_txt setStringValue: _NS("VLC crashed previously")];
758     [o_crashrep_win setTitle: _NS("VLC crashed previously")];
759     [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, ...")];
760     [o_crashrep_includeEmail_ckb setTitle: _NS("I agree to be possibly contacted about this bugreport.")];
761     [o_crashrep_includeEmail_txt setStringValue: _NS("Only your default E-Mail address will be submitted, including no further information.")];
762     [o_crashrep_dontaskagain_ckb setTitle: _NS("Don't ask again")];
763 }
764
765 #pragma mark -
766 #pragma mark Termination
767
768 - (void)applicationWillTerminate:(NSNotification *)notification
769 {
770     /* don't allow a double termination call. If the user has
771      * already invoked the quit then simply return this time. */
772     static bool f_appExit = false;
773     bool isTerminating;
774
775     [o_appLock lock];
776     isTerminating = f_appExit;
777     f_appExit = true;
778     [o_appLock unlock];
779
780     if (isTerminating)
781         return;
782
783     if (notification == nil)
784         [[NSNotificationCenter defaultCenter] postNotificationName: NSApplicationWillTerminateNotification object: nil];
785
786     playlist_t * p_playlist = pl_Get(p_intf);
787     int returnedValue = 0;
788
789     /* always exit fullscreen on quit, otherwise we get ugly artifacts on the next launch */
790     if (b_nativeFullscreenMode) {
791         [o_mainwindow toggleFullScreen: self];
792         [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
793     }
794
795     /* Save some interface state in configuration, at module quit */
796     config_PutInt(p_intf, "random", var_GetBool(p_playlist, "random"));
797     config_PutInt(p_intf, "loop", var_GetBool(p_playlist, "loop"));
798     config_PutInt(p_intf, "repeat", var_GetBool(p_playlist, "repeat"));
799
800     msg_Dbg(p_intf, "Terminating");
801
802     /* unsubscribe from the interactive dialogues */
803     dialog_Unregister(p_intf);
804     var_DelCallback(p_intf, "dialog-error", DialogCallback, self);
805     var_DelCallback(p_intf, "dialog-critical", DialogCallback, self);
806     var_DelCallback(p_intf, "dialog-login", DialogCallback, self);
807     var_DelCallback(p_intf, "dialog-question", DialogCallback, self);
808     var_DelCallback(p_intf, "dialog-progress-bar", DialogCallback, self);
809     //var_DelCallback(p_playlist, "item-change", PLItemChanged, self);
810     var_DelCallback(p_playlist, "item-current", PLItemChanged, self);
811     var_DelCallback(p_playlist, "activity", PLItemChanged, self);
812     var_DelCallback(p_playlist, "leaf-to-parent", PlaylistUpdated, self);
813     var_DelCallback(p_playlist, "playlist-item-append", PlaylistUpdated, self);
814     var_DelCallback(p_playlist, "playlist-item-deleted", PlaylistUpdated, self);
815     var_DelCallback(p_playlist, "random", PlaybackModeUpdated, self);
816     var_DelCallback(p_playlist, "repeat", PlaybackModeUpdated, self);
817     var_DelCallback(p_playlist, "loop", PlaybackModeUpdated, self);
818     var_DelCallback(p_playlist, "volume", VolumeUpdated, self);
819     var_DelCallback(p_playlist, "mute", VolumeUpdated, self);
820     var_DelCallback(p_intf->p_libvlc, "intf-toggle-fscontrol", ShowController, self);
821     var_DelCallback(p_intf->p_libvlc, "intf-show", ShowController, self);
822
823     if (p_current_input) {
824         var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
825         vlc_object_release(p_current_input);
826         p_current_input = NULL;
827     }
828
829     /* remove global observer watching for vout device changes correctly */
830     [[NSNotificationCenter defaultCenter] removeObserver: self];
831
832     /* release some other objects here, because it isn't sure whether dealloc
833      * will be called later on */
834     if (o_sprefs)
835         [o_sprefs release];
836
837     if (o_prefs)
838         [o_prefs release];
839
840     [o_open release];
841
842     if (o_info)
843         [o_info release];
844
845     if (o_wizard)
846         [o_wizard release];
847
848     [crashLogURLConnection cancel];
849     [crashLogURLConnection release];
850
851     [o_coredialogs release];
852     [o_eyetv release];
853
854     /* unsubscribe from libvlc's debug messages */
855     vlc_Unsubscribe(&p_intf->p_sys->sub);
856
857     [o_msg_arr removeAllObjects];
858     [o_msg_arr release];
859     o_msg_arr = NULL;
860     [o_usedHotkeys release];
861     o_usedHotkeys = NULL;
862
863     [o_msg_lock release];
864
865     /* write cached user defaults to disk */
866     [[NSUserDefaults standardUserDefaults] synchronize];
867
868     [o_mainmenu release];
869
870     [o_vout_controller release];
871     o_vout_controller = nil;
872
873     libvlc_Quit(p_intf->p_libvlc);
874
875     [o_mainwindow release];
876     o_mainwindow = NULL;
877
878     [self setIntf:nil];
879 }
880
881 #pragma mark -
882 #pragma mark Sparkle delegate
883 /* received directly before the update gets installed, so let's shut down a bit */
884 - (void)updater:(SUUpdater *)updater willInstallUpdate:(SUAppcastItem *)update
885 {
886     [NSApp activateIgnoringOtherApps:YES];
887     [o_remote stopListening: self];
888     [[VLCCoreInteraction sharedInstance] stop];
889 }
890
891 #pragma mark -
892 #pragma mark Media Key support
893
894 -(void)mediaKeyTap:(SPMediaKeyTap*)keyTap receivedMediaKeyEvent:(NSEvent*)event
895 {
896     if (b_mediaKeySupport) {
897         assert([event type] == NSSystemDefined && [event subtype] == SPSystemDefinedEventMediaKeys);
898
899         int keyCode = (([event data1] & 0xFFFF0000) >> 16);
900         int keyFlags = ([event data1] & 0x0000FFFF);
901         int keyState = (((keyFlags & 0xFF00) >> 8)) == 0xA;
902         int keyRepeat = (keyFlags & 0x1);
903
904         if (keyCode == NX_KEYTYPE_PLAY && keyState == 0)
905             [[VLCCoreInteraction sharedInstance] play];
906
907         if ((keyCode == NX_KEYTYPE_FAST || keyCode == NX_KEYTYPE_NEXT) && !b_mediakeyJustJumped) {
908             if (keyState == 0 && keyRepeat == 0)
909                 [[VLCCoreInteraction sharedInstance] next];
910             else if (keyRepeat == 1) {
911                 [[VLCCoreInteraction sharedInstance] forwardShort];
912                 b_mediakeyJustJumped = YES;
913                 [self performSelector:@selector(resetMediaKeyJump)
914                            withObject: NULL
915                            afterDelay:0.25];
916             }
917         }
918
919         if ((keyCode == NX_KEYTYPE_REWIND || keyCode == NX_KEYTYPE_PREVIOUS) && !b_mediakeyJustJumped) {
920             if (keyState == 0 && keyRepeat == 0)
921                 [[VLCCoreInteraction sharedInstance] previous];
922             else if (keyRepeat == 1) {
923                 [[VLCCoreInteraction sharedInstance] backwardShort];
924                 b_mediakeyJustJumped = YES;
925                 [self performSelector:@selector(resetMediaKeyJump)
926                            withObject: NULL
927                            afterDelay:0.25];
928             }
929         }
930     }
931 }
932
933 #pragma mark -
934 #pragma mark Other notification
935
936 /* Listen to the remote in exclusive mode, only when VLC is the active
937    application */
938 - (void)applicationDidBecomeActive:(NSNotification *)aNotification
939 {
940     if (!p_intf)
941         return;
942     if (var_InheritBool(p_intf, "macosx-appleremote") == YES)
943         [o_remote startListening: self];
944 }
945 - (void)applicationDidResignActive:(NSNotification *)aNotification
946 {
947     if (!p_intf)
948         return;
949     [o_remote stopListening: self];
950 }
951
952 /* Triggered when the computer goes to sleep */
953 - (void)computerWillSleep: (NSNotification *)notification
954 {
955     [[VLCCoreInteraction sharedInstance] pause];
956 }
957
958 #pragma mark -
959 #pragma mark File opening over dock icon
960
961 - (void)application:(NSApplication *)o_app openFiles:(NSArray *)o_names
962 {
963     BOOL b_autoplay = config_GetInt(VLCIntf, "macosx-autoplay");
964     char *psz_uri = vlc_path2uri([[o_names objectAtIndex:0] UTF8String], "file");
965
966     // try to add file as subtitle
967     if ([o_names count] == 1 && psz_uri) {
968         input_thread_t * p_input = pl_CurrentInput(VLCIntf);
969         if (p_input) {
970             BOOL b_returned = NO;
971             b_returned = input_AddSubtitle(p_input, psz_uri, true);
972             vlc_object_release(p_input);
973             if (!b_returned) {
974                 free(psz_uri);
975                 return;
976             }
977         }
978     }
979     free(psz_uri);
980
981     NSArray *o_sorted_names = [o_names sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)];
982     NSMutableArray *o_result = [NSMutableArray arrayWithCapacity: [o_sorted_names count]];
983     for (int i = 0; i < [o_sorted_names count]; i++) {
984         psz_uri = vlc_path2uri([[o_sorted_names objectAtIndex: i] UTF8String], "file");
985         if (!psz_uri)
986             continue;
987
988         NSDictionary *o_dic = [NSDictionary dictionaryWithObject:[NSString stringWithCString:psz_uri encoding:NSUTF8StringEncoding] forKey:@"ITEM_URL"];
989         free(psz_uri);
990         [o_result addObject: o_dic];
991     }
992
993     if (b_autoplay)
994         [o_playlist appendArray: o_result atPos: -1 enqueue: NO];
995     else
996         [o_playlist appendArray: o_result atPos: -1 enqueue: YES];
997
998     return;
999 }
1000
1001 /* When user click in the Dock icon our double click in the finder */
1002 - (BOOL)applicationShouldHandleReopen:(NSApplication *)theApplication hasVisibleWindows:(BOOL)hasVisibleWindows
1003 {
1004     if (!hasVisibleWindows)
1005         [o_mainwindow makeKeyAndOrderFront:self];
1006
1007     return YES;
1008 }
1009
1010 #pragma mark -
1011 #pragma mark Apple Remote Control
1012
1013 /* Helper method for the remote control interface in order to trigger forward/backward and volume
1014    increase/decrease as long as the user holds the left/right, plus/minus button */
1015 - (void) executeHoldActionForRemoteButton: (NSNumber*) buttonIdentifierNumber
1016 {
1017     if (b_remote_button_hold) {
1018         switch([buttonIdentifierNumber intValue]) {
1019             case kRemoteButtonRight_Hold:
1020                 [[VLCCoreInteraction sharedInstance] forward];
1021                 break;
1022             case kRemoteButtonLeft_Hold:
1023                 [[VLCCoreInteraction sharedInstance] backward];
1024                 break;
1025             case kRemoteButtonVolume_Plus_Hold:
1026                 if (p_intf)
1027                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_UP);
1028                 break;
1029             case kRemoteButtonVolume_Minus_Hold:
1030                 if (p_intf)
1031                     var_SetInteger(p_intf->p_libvlc, "key-action", ACTIONID_VOL_DOWN);
1032                 break;
1033         }
1034         if (b_remote_button_hold) {
1035             /* trigger event */
1036             [self performSelector:@selector(executeHoldActionForRemoteButton:)
1037                          withObject:buttonIdentifierNumber
1038                          afterDelay:0.25];
1039         }
1040     }
1041 }
1042
1043 /* Apple Remote callback */
1044 - (void) appleRemoteButton: (AppleRemoteEventIdentifier)buttonIdentifier
1045                pressedDown: (BOOL) pressedDown
1046                 clickCount: (unsigned int) count
1047 {
1048     switch(buttonIdentifier) {
1049         case k2009RemoteButtonFullscreen:
1050             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1051             break;
1052         case k2009RemoteButtonPlay:
1053             [[VLCCoreInteraction sharedInstance] play];
1054             break;
1055         case kRemoteButtonPlay:
1056             if (count >= 2)
1057                 [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1058             else
1059                 [[VLCCoreInteraction sharedInstance] play];
1060             break;
1061         case kRemoteButtonVolume_Plus:
1062             if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol"))
1063                 [NSSound increaseSystemVolume];
1064             else
1065                 [[VLCCoreInteraction sharedInstance] volumeUp];
1066             break;
1067         case kRemoteButtonVolume_Minus:
1068             if (config_GetInt(VLCIntf, "macosx-appleremote-sysvol"))
1069                 [NSSound decreaseSystemVolume];
1070             else
1071                 [[VLCCoreInteraction sharedInstance] volumeDown];
1072             break;
1073         case kRemoteButtonRight:
1074             if (config_GetInt(VLCIntf, "macosx-appleremote-prevnext"))
1075                 [[VLCCoreInteraction sharedInstance] forward];
1076             else
1077                 [[VLCCoreInteraction sharedInstance] next];
1078             break;
1079         case kRemoteButtonLeft:
1080             if (config_GetInt(VLCIntf, "macosx-appleremote-prevnext"))
1081                 [[VLCCoreInteraction sharedInstance] backward];
1082             else
1083                 [[VLCCoreInteraction sharedInstance] previous];
1084             break;
1085         case kRemoteButtonRight_Hold:
1086         case kRemoteButtonLeft_Hold:
1087         case kRemoteButtonVolume_Plus_Hold:
1088         case kRemoteButtonVolume_Minus_Hold:
1089             /* simulate an event as long as the user holds the button */
1090             b_remote_button_hold = pressedDown;
1091             if (pressedDown) {
1092                 NSNumber* buttonIdentifierNumber = [NSNumber numberWithInt: buttonIdentifier];
1093                 [self performSelector:@selector(executeHoldActionForRemoteButton:)
1094                            withObject:buttonIdentifierNumber];
1095             }
1096             break;
1097         case kRemoteButtonMenu:
1098             [o_controls showPosition: self]; //FIXME
1099             break;
1100         case kRemoteButtonPlay_Sleep:
1101         {
1102             NSAppleScript * script = [[NSAppleScript alloc] initWithSource:@"tell application \"System Events\" to sleep"];
1103             [script executeAndReturnError:nil];
1104             [script release];
1105             break;
1106         }
1107         default:
1108             /* Add here whatever you want other buttons to do */
1109             break;
1110     }
1111 }
1112
1113 #pragma mark -
1114 #pragma mark Key Shortcuts
1115
1116 /*****************************************************************************
1117  * hasDefinedShortcutKey: Check to see if the key press is a defined VLC
1118  * shortcut key.  If it is, pass it off to VLC for handling and return YES,
1119  * otherwise ignore it and return NO (where it will get handled by Cocoa).
1120  *****************************************************************************/
1121 - (BOOL)hasDefinedShortcutKey:(NSEvent *)o_event force:(BOOL)b_force
1122 {
1123     unichar key = 0;
1124     vlc_value_t val;
1125     unsigned int i_pressed_modifiers = 0;
1126
1127     val.i_int = 0;
1128     i_pressed_modifiers = [o_event modifierFlags];
1129
1130     if (i_pressed_modifiers & NSControlKeyMask)
1131         val.i_int |= KEY_MODIFIER_CTRL;
1132
1133     if (i_pressed_modifiers & NSAlternateKeyMask)
1134         val.i_int |= KEY_MODIFIER_ALT;
1135
1136     if (i_pressed_modifiers & NSShiftKeyMask)
1137         val.i_int |= KEY_MODIFIER_SHIFT;
1138
1139     if (i_pressed_modifiers & NSCommandKeyMask)
1140         val.i_int |= KEY_MODIFIER_COMMAND;
1141
1142     NSString * characters = [o_event charactersIgnoringModifiers];
1143     if ([characters length] > 0) {
1144         key = [[characters lowercaseString] characterAtIndex: 0];
1145
1146         /* handle Lion's default key combo for fullscreen-toggle in addition to our own hotkeys */
1147         if (key == 'f' && i_pressed_modifiers & NSControlKeyMask && i_pressed_modifiers & NSCommandKeyMask) {
1148             [[VLCCoreInteraction sharedInstance] toggleFullscreen];
1149             return YES;
1150         }
1151
1152         if (!b_force) {
1153             switch(key) {
1154                 case NSDeleteCharacter:
1155                 case NSDeleteFunctionKey:
1156                 case NSDeleteCharFunctionKey:
1157                 case NSBackspaceCharacter:
1158                 case NSUpArrowFunctionKey:
1159                 case NSDownArrowFunctionKey:
1160                 case NSRightArrowFunctionKey:
1161                 case NSLeftArrowFunctionKey:
1162                 case NSEnterCharacter:
1163                 case NSCarriageReturnCharacter:
1164                     return NO;
1165             }
1166         }
1167
1168         if (key == 0x0020) { // space key
1169             [[VLCCoreInteraction sharedInstance] play];
1170             return YES;
1171         }
1172
1173         val.i_int |= CocoaKeyToVLC(key);
1174
1175         BOOL b_found_key = NO;
1176         for (int i = 0; i < [o_usedHotkeys count]; i++) {
1177             NSString *str = [o_usedHotkeys objectAtIndex: i];
1178             unsigned int i_keyModifiers = [[VLCStringUtility sharedInstance] VLCModifiersToCocoa: str];
1179
1180             if ([[characters lowercaseString] isEqualToString: [[VLCStringUtility sharedInstance] VLCKeyToString: str]] &&
1181                (i_keyModifiers & NSShiftKeyMask)     == (i_pressed_modifiers & NSShiftKeyMask) &&
1182                (i_keyModifiers & NSControlKeyMask)   == (i_pressed_modifiers & NSControlKeyMask) &&
1183                (i_keyModifiers & NSAlternateKeyMask) == (i_pressed_modifiers & NSAlternateKeyMask) &&
1184                (i_keyModifiers & NSCommandKeyMask)   == (i_pressed_modifiers & NSCommandKeyMask)) {
1185                 b_found_key = YES;
1186                 break;
1187             }
1188         }
1189
1190         if (b_found_key) {
1191             var_SetInteger(p_intf->p_libvlc, "key-pressed", val.i_int);
1192             return YES;
1193         }
1194     }
1195
1196     return NO;
1197 }
1198
1199 - (void)updateCurrentlyUsedHotkeys
1200 {
1201     NSMutableArray *o_tempArray = [[NSMutableArray alloc] init];
1202     /* Get the main Module */
1203     module_t *p_main = module_get_main();
1204     assert(p_main);
1205     unsigned confsize;
1206     module_config_t *p_config;
1207
1208     p_config = module_config_get (p_main, &confsize);
1209
1210     for (size_t i = 0; i < confsize; i++) {
1211         module_config_t *p_item = p_config + i;
1212
1213         if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
1214            && !strncmp(p_item->psz_name , "key-", 4)
1215            && !EMPTY_STR(p_item->psz_text)) {
1216             if (p_item->value.psz)
1217                 [o_tempArray addObject: [NSString stringWithUTF8String:p_item->value.psz]];
1218         }
1219     }
1220     module_config_free (p_config);
1221
1222     if (o_usedHotkeys)
1223         [o_usedHotkeys release];
1224     o_usedHotkeys = [[NSArray alloc] initWithArray: o_tempArray copyItems: YES];
1225     [o_tempArray release];
1226 }
1227
1228 #pragma mark -
1229 #pragma mark Interface updaters
1230 - (void)setFullscreen:(int)i_full forWindow:(vout_window_t *)p_wnd
1231 {
1232     if (!p_intf || (!b_nativeFullscreenMode && !p_wnd))
1233         return;
1234     playlist_t * p_playlist = pl_Get(p_intf);
1235     BOOL b_fullscreen = i_full;
1236
1237     if (!var_GetBool(p_playlist, "fullscreen") != !b_fullscreen) {
1238         var_SetBool(p_playlist, "fullscreen", b_fullscreen);
1239     }
1240
1241     if (b_nativeFullscreenMode) {
1242         // this is called twice in certain situations, so only toogle if we really need to
1243         if ((b_fullscreen && !([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)) ||
1244             (!b_fullscreen &&  ([NSApp currentSystemPresentationOptions] & NSApplicationPresentationFullScreen)))
1245             [o_mainwindow toggleFullScreen: self];
1246
1247         if (b_fullscreen)
1248             [NSApp setPresentationOptions:(NSApplicationPresentationFullScreen | NSApplicationPresentationAutoHideDock | NSApplicationPresentationAutoHideMenuBar)];
1249         else
1250             [NSApp setPresentationOptions:(NSApplicationPresentationDefault)];
1251     } else {
1252         assert(p_wnd);
1253
1254         if (b_fullscreen) {
1255             input_thread_t * p_input = pl_CurrentInput(p_intf);
1256             if (p_input != NULL && [self activeVideoPlayback]) {
1257                 // activate app, as method can also be triggered from outside the app (prevents nasty window layout)
1258                 [NSApp activateIgnoringOtherApps:YES];
1259                 [o_vout_controller updateWindow:p_wnd withSelector:@selector(enterFullscreen)];
1260
1261             }
1262             if (p_input)
1263                 vlc_object_release(p_input);
1264         } else {
1265             // leaving fullscreen is always allowed
1266             [o_vout_controller updateWindow:p_wnd withSelector:@selector(leaveFullscreen)];
1267         }
1268     }
1269 }
1270
1271 - (void)PlaylistItemChanged
1272 {
1273     if (p_current_input && (p_current_input->b_dead || !vlc_object_alive(p_current_input))) {
1274         var_DelCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
1275         vlc_object_release(p_current_input);
1276         p_current_input = NULL;
1277
1278         [o_mainmenu setRateControlsEnabled: NO];
1279     }
1280     else if (!p_current_input) {
1281         // object is hold here and released then it is dead
1282         p_current_input = playlist_CurrentInput(pl_Get(VLCIntf));
1283         if (p_current_input) {
1284             var_AddCallback(p_current_input, "intf-event", InputEvent, [VLCMain sharedInstance]);
1285             [self playbackStatusUpdated];
1286             [o_mainmenu setRateControlsEnabled: YES];
1287             if ([self activeVideoPlayback] && [[o_mainwindow videoView] isHidden])
1288                 [o_mainwindow performSelectorOnMainThread:@selector(togglePlaylist:) withObject: nil waitUntilDone:NO];
1289         }
1290     }
1291
1292     [o_playlist updateRowSelection];
1293     [o_mainwindow updateWindow];
1294     [self updateDelays];
1295     [self updateMainMenu];
1296 }
1297
1298 - (void)updateMainMenu
1299 {
1300     [o_mainmenu setupMenus];
1301     [o_mainmenu updatePlaybackRate];
1302 }
1303
1304 - (void)updateMainWindow
1305 {
1306     [o_mainwindow updateWindow];
1307 }
1308
1309 - (void)showMainWindow
1310 {
1311     [o_mainwindow performSelectorOnMainThread:@selector(makeKeyAndOrderFront:) withObject:nil waitUntilDone:NO];
1312 }
1313
1314 - (void)showFullscreenController
1315 {
1316     [o_mainwindow performSelectorOnMainThread:@selector(showFullscreenController) withObject:nil waitUntilDone:NO];
1317 }
1318
1319 - (void)updateDelays
1320 {
1321     [[VLCTrackSynchronization sharedInstance] performSelectorOnMainThread: @selector(updateValues) withObject: nil waitUntilDone:NO];
1322 }
1323
1324 - (void)updateName
1325 {
1326     [o_mainwindow updateName];
1327 }
1328
1329 - (void)updatePlaybackPosition
1330 {
1331     [o_mainwindow updateTimeSlider];
1332 }
1333
1334 - (void)updateVolume
1335 {
1336     [o_mainwindow updateVolumeSlider];
1337 }
1338
1339 - (void)playlistUpdated
1340 {
1341     [self playbackStatusUpdated];
1342     [o_playlist playlistUpdated];
1343     [o_mainwindow updateWindow];
1344     [o_mainwindow updateName];
1345 }
1346
1347 - (void)updateRecordState: (BOOL)b_value
1348 {
1349     [o_mainmenu updateRecordState:b_value];
1350 }
1351
1352 - (void)updateInfoandMetaPanel
1353 {
1354     [o_playlist outlineViewSelectionDidChange:nil];
1355 }
1356
1357 - (void)playbackStatusUpdated
1358 {
1359     input_thread_t * p_input;
1360
1361     p_input = pl_CurrentInput(p_intf);
1362     if (p_input) {
1363         int state = var_GetInteger(p_input, "state");
1364         if (state == PLAYING_S) {
1365             /* prevent the system from sleeping */
1366             IOReturn success;
1367             /* work-around a bug in 10.7.4 and 10.7.5, so check for 10.7.x < 10.7.4, 10.8 and 10.6 */
1368             if ((NSAppKitVersionNumber >= 1115.2 && NSAppKitVersionNumber < 1138.45) || OSX_MOUNTAIN_LION || OSX_SNOW_LEOPARD) {
1369                 CFStringRef reasonForActivity= CFStringCreateWithCString(kCFAllocatorDefault, _("VLC media playback"), kCFStringEncodingUTF8);
1370                 if ([self activeVideoPlayback])
1371                     success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
1372                 else
1373                     success = IOPMAssertionCreateWithName(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, reasonForActivity, &systemSleepAssertionID);
1374                 CFRelease(reasonForActivity);
1375             } else {
1376                 /* fall-back on the 10.5 mode, which also works on 10.7.4 and 10.7.5 */
1377                 if ([self activeVideoPlayback])
1378                     success = IOPMAssertionCreate(kIOPMAssertionTypeNoDisplaySleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
1379                 else
1380                     success = IOPMAssertionCreate(kIOPMAssertionTypeNoIdleSleep, kIOPMAssertionLevelOn, &systemSleepAssertionID);
1381             }
1382
1383             if (success == kIOReturnSuccess)
1384                 msg_Dbg(VLCIntf, "prevented sleep through IOKit (%i)", systemSleepAssertionID);
1385             else
1386                 msg_Warn(VLCIntf, "failed to prevent system sleep through IOKit");
1387
1388             [[self mainMenu] setPause];
1389             [o_mainwindow setPause];
1390         } else {
1391             if (state == END_S)
1392                 [o_mainmenu setSubmenusEnabled: FALSE];
1393             [[self mainMenu] setPlay];
1394             [o_mainwindow setPlay];
1395
1396             /* allow the system to sleep again */
1397             msg_Dbg(VLCIntf, "releasing sleep blocker (%i)" , systemSleepAssertionID);
1398             IOPMAssertionRelease(systemSleepAssertionID);
1399         }
1400         vlc_object_release(p_input);
1401     }
1402
1403     [[VLCMain sharedInstance] performSelectorOnMainThread:@selector(updateMainWindow) withObject: nil waitUntilDone: NO];
1404     [self performSelectorOnMainThread:@selector(sendDistributedNotificationWithUpdatedPlaybackStatus) withObject: nil waitUntilDone: NO];
1405 }
1406
1407 - (void)sendDistributedNotificationWithUpdatedPlaybackStatus
1408 {
1409     [[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"VLCPlayerStateDidChange"
1410                                                                    object:nil
1411                                                                  userInfo:nil
1412                                                        deliverImmediately:YES];
1413 }
1414
1415 - (void)playbackModeUpdated
1416 {
1417     vlc_value_t looping,repeating;
1418     playlist_t * p_playlist = pl_Get(VLCIntf);
1419
1420     bool loop = var_GetBool(p_playlist, "loop");
1421     bool repeat = var_GetBool(p_playlist, "repeat");
1422     if (repeat) {
1423         [[o_mainwindow controlsBar] setRepeatOne];
1424         [o_mainmenu setRepeatOne];
1425     } else if (loop) {
1426         [[o_mainwindow controlsBar] setRepeatAll];
1427         [o_mainmenu setRepeatAll];
1428     } else {
1429         [[o_mainwindow controlsBar] setRepeatOff];
1430         [o_mainmenu setRepeatOff];
1431     }
1432
1433     [[o_mainwindow controlsBar] setShuffle];
1434     [o_mainmenu setShuffle];
1435 }
1436
1437
1438 #pragma mark -
1439 #pragma mark Window updater
1440
1441 - (void)setWindowLevel:(NSNumber*)state
1442 {
1443     if (var_InheritBool(p_intf, "video-wallpaper") || [[[[VLCMainWindow sharedInstance] videoView] window] level] < NSNormalWindowLevel)
1444         return;
1445
1446     if ([state unsignedIntValue] & VOUT_WINDOW_STATE_ABOVE)
1447         [[[[VLCMainWindow sharedInstance] videoView] window] setLevel: NSStatusWindowLevel];
1448     else
1449         [[[[VLCMainWindow sharedInstance] videoView] window] setLevel: NSNormalWindowLevel];
1450 }
1451
1452 - (void)setActiveVideoPlayback:(BOOL)b_value
1453 {
1454     b_active_videoplayback = b_value;
1455     if (o_mainwindow) {
1456         [o_mainwindow performSelectorOnMainThread:@selector(setVideoplayEnabled) withObject:nil waitUntilDone:YES];
1457         [o_mainwindow performSelectorOnMainThread:@selector(togglePlaylist:) withObject:nil waitUntilDone:NO];
1458     }
1459 }
1460
1461 #pragma mark -
1462 #pragma mark Other objects getters
1463
1464 - (id)mainMenu
1465 {
1466     return o_mainmenu;
1467 }
1468
1469 - (VLCMainWindow *)mainWindow
1470 {
1471     return o_mainwindow;
1472 }
1473
1474 - (id)controls
1475 {
1476     if (o_controls)
1477         return o_controls;
1478
1479     return nil;
1480 }
1481
1482 - (id)bookmarks
1483 {
1484     if (!o_bookmarks)
1485         o_bookmarks = [[VLCBookmarks alloc] init];
1486
1487     if (!nib_bookmarks_loaded)
1488         nib_bookmarks_loaded = [NSBundle loadNibNamed:@"Bookmarks" owner: NSApp];
1489
1490     return o_bookmarks;
1491 }
1492
1493 - (id)open
1494 {
1495     if (!o_open)
1496         return nil;
1497
1498     if (!nib_open_loaded)
1499         nib_open_loaded = [NSBundle loadNibNamed:@"Open" owner: NSApp];
1500
1501     return o_open;
1502 }
1503
1504 - (id)simplePreferences
1505 {
1506     if (!o_sprefs)
1507         o_sprefs = [[VLCSimplePrefs alloc] init];
1508
1509     if (!nib_prefs_loaded)
1510         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1511
1512     return o_sprefs;
1513 }
1514
1515 - (id)preferences
1516 {
1517     if (!o_prefs)
1518         o_prefs = [[VLCPrefs alloc] init];
1519
1520     if (!nib_prefs_loaded)
1521         nib_prefs_loaded = [NSBundle loadNibNamed:@"Preferences" owner: NSApp];
1522
1523     return o_prefs;
1524 }
1525
1526 - (id)playlist
1527 {
1528     if (o_playlist)
1529         return o_playlist;
1530
1531     return nil;
1532 }
1533
1534 - (id)info
1535 {
1536     if (! nib_info_loaded)
1537         nib_info_loaded = [NSBundle loadNibNamed:@"MediaInfo" owner: NSApp];
1538
1539     if (o_info)
1540         return o_info;
1541
1542     return nil;
1543 }
1544
1545 - (id)wizard
1546 {
1547     if (!o_wizard)
1548         o_wizard = [[VLCWizard alloc] init];
1549
1550     if (!nib_wizard_loaded) {
1551         nib_wizard_loaded = [NSBundle loadNibNamed:@"Wizard" owner: NSApp];
1552         [o_wizard initStrings];
1553     }
1554     return o_wizard;
1555 }
1556
1557 - (id)getVideoViewAtPositionX: (int *)pi_x Y: (int *)pi_y withWidth: (unsigned int*)pi_width andHeight: (unsigned int*)pi_height forWindow:(vout_window_t *)p_wnd
1558 {
1559     SEL sel = @selector(setupVout:);
1560     NSInvocation *inv = [NSInvocation invocationWithMethodSignature:[o_vout_controller methodSignatureForSelector:sel]];
1561     [inv setTarget:o_vout_controller];
1562     [inv setSelector:sel];
1563     [inv setArgument:&p_wnd atIndex:2]; // starting at 2!
1564
1565     [inv performSelectorOnMainThread:@selector(invoke) withObject:nil
1566                        waitUntilDone:YES];
1567
1568     VLCVoutView *videoView;
1569     [inv getReturnValue:&videoView];
1570
1571     NSRect videoRect = [videoView frame];
1572     int i_x = (int)videoRect.origin.x;
1573     int i_y = (int)videoRect.origin.y;
1574     unsigned int i_width = (int)videoRect.size.width;
1575     unsigned int i_height = (int)videoRect.size.height;
1576     pi_x = &i_x;
1577     pi_y = &i_y;
1578     pi_width = &i_width;
1579     pi_height = &i_height;
1580     msg_Dbg(VLCIntf, "returning videoview with x=%i, y=%i, width=%i, height=%i", i_x, i_y, i_width, i_height);
1581     return videoView;
1582 }
1583
1584 - (id)coreDialogProvider
1585 {
1586     if (o_coredialogs)
1587         return o_coredialogs;
1588
1589     return nil;
1590 }
1591
1592 - (id)eyeTVController
1593 {
1594     if (o_eyetv)
1595         return o_eyetv;
1596
1597     return nil;
1598 }
1599
1600 - (id)appleRemoteController
1601 {
1602     return o_remote;
1603 }
1604
1605 - (BOOL)activeVideoPlayback
1606 {
1607     return b_active_videoplayback;
1608 }
1609
1610 #pragma mark -
1611 #pragma mark Crash Log
1612 - (void)sendCrashLog:(NSString *)crashLog withUserComment:(NSString *)userComment
1613 {
1614     NSString *urlStr = @"http://crash.videolan.org/crashlog/sendcrashreport.php";
1615     NSURL *url = [NSURL URLWithString:urlStr];
1616
1617     NSMutableURLRequest *req = [NSMutableURLRequest requestWithURL:url];
1618     [req setHTTPMethod:@"POST"];
1619
1620     NSString * email;
1621     if ([o_crashrep_includeEmail_ckb state] == NSOnState) {
1622         ABPerson * contact = [[ABAddressBook sharedAddressBook] me];
1623         ABMultiValue *emails = [contact valueForProperty:kABEmailProperty];
1624         email = [emails valueAtIndex:[emails indexForIdentifier:
1625                     [emails primaryIdentifier]]];
1626     }
1627     else
1628         email = [NSString string];
1629
1630     NSString *postBody;
1631     postBody = [NSString stringWithFormat:@"CrashLog=%@&Comment=%@&Email=%@\r\n",
1632             [crashLog stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
1633             [userComment stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding],
1634             [email stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
1635
1636     [req setHTTPBody:[postBody dataUsingEncoding:NSUTF8StringEncoding]];
1637
1638     /* Released from delegate */
1639     crashLogURLConnection = [[NSURLConnection alloc] initWithRequest:req delegate:self];
1640 }
1641
1642 - (void)connectionDidFinishLoading:(NSURLConnection *)connection
1643 {
1644     msg_Dbg(p_intf, "crash report successfully sent");
1645     [crashLogURLConnection release];
1646     crashLogURLConnection = nil;
1647 }
1648
1649 - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error
1650 {
1651     msg_Warn (p_intf, "Error when sending the crash report: %s (%li)", [[error localizedDescription] UTF8String], [error code]);
1652     [crashLogURLConnection release];
1653     crashLogURLConnection = nil;
1654 }
1655
1656 - (NSString *)latestCrashLogPathPreviouslySeen:(BOOL)previouslySeen
1657 {
1658     NSString * crashReporter;
1659     if (OSX_MOUNTAIN_LION)
1660         crashReporter = [@"~/Library/Logs/DiagnosticReports" stringByExpandingTildeInPath];
1661     else
1662         crashReporter = [@"~/Library/Logs/CrashReporter" stringByExpandingTildeInPath];
1663     NSDirectoryEnumerator *direnum = [[NSFileManager defaultManager] enumeratorAtPath:crashReporter];
1664     NSString *fname;
1665     NSString * latestLog = nil;
1666     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
1667     int year  = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportYear"] : 0;
1668     int month = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportMonth"]: 0;
1669     int day   = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportDay"]  : 0;
1670     int hours = !previouslySeen ? [defaults integerForKey:@"LatestCrashReportHours"]: 0;
1671
1672     while (fname = [direnum nextObject]) {
1673         [direnum skipDescendents];
1674         if ([fname hasPrefix:@"VLC"] && [fname hasSuffix:@"crash"]) {
1675             NSArray * compo = [fname componentsSeparatedByString:@"_"];
1676             if ([compo count] < 3)
1677                 continue;
1678             compo = [[compo objectAtIndex:1] componentsSeparatedByString:@"-"];
1679             if ([compo count] < 4)
1680                 continue;
1681
1682             // Dooh. ugly.
1683             if (year < [[compo objectAtIndex:0] intValue] ||
1684                 (year ==[[compo objectAtIndex:0] intValue] &&
1685                  (month < [[compo objectAtIndex:1] intValue] ||
1686                   (month ==[[compo objectAtIndex:1] intValue] &&
1687                    (day   < [[compo objectAtIndex:2] intValue] ||
1688                     (day   ==[[compo objectAtIndex:2] intValue] &&
1689                       hours < [[compo objectAtIndex:3] intValue])))))) {
1690                 year  = [[compo objectAtIndex:0] intValue];
1691                 month = [[compo objectAtIndex:1] intValue];
1692                 day   = [[compo objectAtIndex:2] intValue];
1693                 hours = [[compo objectAtIndex:3] intValue];
1694                 latestLog = [crashReporter stringByAppendingPathComponent:fname];
1695             }
1696         }
1697     }
1698
1699     if (!(latestLog && [[NSFileManager defaultManager] fileExistsAtPath:latestLog]))
1700         return nil;
1701
1702     if (!previouslySeen) {
1703         [defaults setInteger:year  forKey:@"LatestCrashReportYear"];
1704         [defaults setInteger:month forKey:@"LatestCrashReportMonth"];
1705         [defaults setInteger:day   forKey:@"LatestCrashReportDay"];
1706         [defaults setInteger:hours forKey:@"LatestCrashReportHours"];
1707     }
1708     return latestLog;
1709 }
1710
1711 - (NSString *)latestCrashLogPath
1712 {
1713     return [self latestCrashLogPathPreviouslySeen:YES];
1714 }
1715
1716 - (void)lookForCrashLog
1717 {
1718     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
1719     // This pref key doesn't exists? this VLC is an upgrade, and this crash log come from previous version
1720     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
1721     BOOL areCrashLogsTooOld = ![defaults integerForKey:@"LatestCrashReportYear"];
1722     NSString * latestLog = [self latestCrashLogPathPreviouslySeen:NO];
1723     if (latestLog && !areCrashLogsTooOld) {
1724         if ([defaults integerForKey:@"AlwaysSendCrashReports"] > 0)
1725             [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]];
1726         else if ([defaults integerForKey:@"AlwaysSendCrashReports"] == 0)
1727             [NSApp runModalForWindow: o_crashrep_win];
1728         // bail out, the user doesn't want us to send reports
1729     }
1730
1731     [o_pool release];
1732 }
1733
1734 - (IBAction)crashReporterAction:(id)sender
1735 {
1736     if (sender == o_crashrep_send_btn) {
1737         [self sendCrashLog:[NSString stringWithContentsOfFile: [self latestCrashLogPath] encoding: NSUTF8StringEncoding error: NULL] withUserComment: [o_crashrep_fld string]];
1738         if ([o_crashrep_dontaskagain_ckb state])
1739             [[NSUserDefaults standardUserDefaults] setInteger:1 forKey:@"AlwaysSendCrashReports"];
1740     } else {
1741         if ([o_crashrep_dontaskagain_ckb state])
1742             [[NSUserDefaults standardUserDefaults] setInteger:-1 forKey:@"AlwaysSendCrashReports"];
1743     }
1744
1745     [NSApp stopModal];
1746     [o_crashrep_win orderOut: sender];
1747 }
1748
1749 - (IBAction)openCrashLog:(id)sender
1750 {
1751     NSString * latestLog = [self latestCrashLogPath];
1752     if (latestLog) {
1753         [[NSWorkspace sharedWorkspace] openFile: latestLog withApplication: @"Console"];
1754     } else {
1755         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."));
1756     }
1757 }
1758
1759 #pragma mark -
1760 #pragma mark Remove old prefs
1761
1762 - (void)_removeOldPreferences
1763 {
1764     static NSString * kVLCPreferencesVersion = @"VLCPreferencesVersion";
1765     static const int kCurrentPreferencesVersion = 2;
1766     NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
1767     int version = [defaults integerForKey:kVLCPreferencesVersion];
1768     if (version >= kCurrentPreferencesVersion)
1769         return;
1770
1771     if (version == 1) {
1772         [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1773         [defaults synchronize];
1774
1775         if (![[VLCCoreInteraction sharedInstance] fixPreferences])
1776             return;
1777         else
1778             config_SaveConfigFile(VLCIntf); // we need to do manually, since we won't quit libvlc cleanly
1779     } else {
1780         NSArray *libraries = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory,
1781             NSUserDomainMask, YES);
1782         if (!libraries || [libraries count] == 0) return;
1783         NSString * preferences = [[libraries objectAtIndex:0] stringByAppendingPathComponent:@"Preferences"];
1784
1785         /* File not found, don't attempt anything */
1786         if (![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc"]] &&
1787            ![[NSFileManager defaultManager] fileExistsAtPath:[preferences stringByAppendingPathComponent:@"org.videolan.vlc.plist"]]) {
1788             [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1789             return;
1790         }
1791
1792         int res = NSRunInformationalAlertPanel(_NS("Remove old preferences?"),
1793                     _NS("We just found an older version of VLC's preferences files."),
1794                     _NS("Move To Trash and Relaunch VLC"), _NS("Ignore"), nil, nil);
1795         if (res != NSOKButton) {
1796             [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1797             return;
1798         }
1799
1800         NSArray * ourPreferences = [NSArray arrayWithObjects:@"org.videolan.vlc.plist", @"VLC", @"org.videolan.vlc", nil];
1801
1802         /* Move the file to trash so that user can find them later */
1803         [[NSWorkspace sharedWorkspace] performFileOperation:NSWorkspaceRecycleOperation source:preferences destination:nil files:ourPreferences tag:0];
1804
1805         /* really reset the defaults from now on */
1806         [NSUserDefaults resetStandardUserDefaults];
1807
1808         [defaults setInteger:kCurrentPreferencesVersion forKey:kVLCPreferencesVersion];
1809         [defaults synchronize];
1810     }
1811
1812     /* Relaunch now */
1813     const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];
1814
1815     /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
1816     if (fork() != 0) {
1817         exit(0);
1818         return;
1819     }
1820     execl(path, path, NULL);
1821 }
1822
1823 #pragma mark -
1824 #pragma mark Errors, warnings and messages
1825 - (IBAction)updateMessagesPanel:(id)sender
1826 {
1827     [self windowDidBecomeKey:nil];
1828 }
1829
1830 - (IBAction)showMessagesPanel:(id)sender
1831 {
1832     [o_msgs_panel makeKeyAndOrderFront: sender];
1833 }
1834
1835 - (void)windowDidBecomeKey:(NSNotification *)o_notification
1836 {
1837     [o_msgs_table reloadData];
1838     [o_msgs_table scrollRowToVisible: [o_msg_arr count] - 1];
1839 }
1840
1841 - (NSInteger)numberOfRowsInTableView:(NSTableView *)aTableView
1842 {
1843     if (aTableView == o_msgs_table)
1844         return [o_msg_arr count];
1845     return 0;
1846 }
1847
1848 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(NSInteger)rowIndex
1849 {
1850     NSMutableAttributedString *result = NULL;
1851
1852     [o_msg_lock lock];
1853     if (rowIndex < [o_msg_arr count])
1854         result = [o_msg_arr objectAtIndex: rowIndex];
1855     [o_msg_lock unlock];
1856
1857     if (result != NULL)
1858         return result;
1859     else
1860         return @"";
1861 }
1862
1863 - (void)processReceivedlibvlcMessage:(const msg_item_t *) item ofType: (int)i_type withStr: (char *)str
1864 {
1865     if (o_msg_arr) {
1866         NSColor *o_white = [NSColor whiteColor];
1867         NSColor *o_red = [NSColor redColor];
1868         NSColor *o_yellow = [NSColor yellowColor];
1869         NSColor *o_gray = [NSColor grayColor];
1870         NSString * firstString, * secondString;
1871
1872         NSColor * pp_color[4] = { o_white, o_red, o_yellow, o_gray };
1873         static const char * ppsz_type[4] = { ": ", " error: ", " warning: ", " debug: " };
1874
1875         NSDictionary *o_attr;
1876         NSMutableAttributedString *o_msg_color;
1877
1878         [o_msg_lock lock];
1879
1880         if ([o_msg_arr count] > 600) {
1881             [o_msg_arr removeObjectAtIndex: 0];
1882             [o_msg_arr removeObjectAtIndex: 1];
1883         }
1884         firstString = [NSString stringWithFormat:@"%s%s", item->psz_module, ppsz_type[i_type]];
1885         secondString = [NSString stringWithFormat:@"%@%s\n", firstString, str];
1886
1887         o_attr = [NSDictionary dictionaryWithObject: pp_color[i_type]  forKey: NSForegroundColorAttributeName];
1888         o_msg_color = [[NSMutableAttributedString alloc] initWithString: secondString attributes: o_attr];
1889         o_attr = [NSDictionary dictionaryWithObject: pp_color[3] forKey: NSForegroundColorAttributeName];
1890         [o_msg_color setAttributes: o_attr range: NSMakeRange(0, [firstString length])];
1891         [o_msg_arr addObject: [o_msg_color autorelease]];
1892
1893         b_msg_arr_changed = YES;
1894         [o_msg_lock unlock];
1895     }
1896 }
1897
1898 - (IBAction)saveDebugLog:(id)sender
1899 {
1900     NSSavePanel * saveFolderPanel = [[NSSavePanel alloc] init];
1901
1902     [saveFolderPanel setCanSelectHiddenExtension: NO];
1903     [saveFolderPanel setCanCreateDirectories: YES];
1904     [saveFolderPanel setAllowedFileTypes: [NSArray arrayWithObject:@"rtf"]];
1905     [saveFolderPanel setNameFieldStringValue:[NSString stringWithFormat: _NS("VLC Debug Log (%s).rtf"), VERSION_MESSAGE]];
1906     [saveFolderPanel beginSheetModalForWindow: o_msgs_panel completionHandler:^(NSInteger returnCode) {
1907         if (returnCode == NSOKButton) {
1908             NSUInteger count = [o_msg_arr count];
1909             NSMutableAttributedString * string = [[NSMutableAttributedString alloc] init];
1910             for (NSUInteger i = 0; i < count; i++)
1911                 [string appendAttributedString: [o_msg_arr objectAtIndex: i]];
1912
1913             NSData *data = [string RTFFromRange:NSMakeRange(0, [string length])
1914                              documentAttributes:[NSDictionary dictionaryWithObject: NSRTFTextDocumentType forKey: NSDocumentTypeDocumentAttribute]];
1915
1916             if ([data writeToFile: [[saveFolderPanel URL] path] atomically: YES] == NO)
1917                 msg_Warn(p_intf, "Error while saving the debug log");
1918
1919             [string release];
1920         }
1921     }];
1922     [saveFolderPanel release];
1923 }
1924
1925 #pragma mark -
1926 #pragma mark Playlist toggling
1927
1928 - (void)updateTogglePlaylistState
1929 {
1930     [[self playlist] outlineViewSelectionDidChange: NULL];
1931 }
1932
1933 #pragma mark -
1934
1935 @end
1936
1937 @implementation VLCMain (Internal)
1938
1939 - (void)handlePortMessage:(NSPortMessage *)o_msg
1940 {
1941     id ** val;
1942     NSData * o_data;
1943     NSValue * o_value;
1944     NSInvocation * o_inv;
1945     NSConditionLock * o_lock;
1946
1947     o_data = [[o_msg components] lastObject];
1948     o_inv = *((NSInvocation **)[o_data bytes]);
1949     [o_inv getArgument: &o_value atIndex: 2];
1950     val = (id **)[o_value pointerValue];
1951     [o_inv setArgument: val[1] atIndex: 2];
1952     o_lock = *(val[0]);
1953
1954     [o_lock lock];
1955     [o_inv invoke];
1956     [o_lock unlockWithCondition: 1];
1957 }
1958
1959 - (void)resetMediaKeyJump
1960 {
1961     b_mediakeyJustJumped = NO;
1962 }
1963
1964 - (void)coreChangedMediaKeySupportSetting: (NSNotification *)o_notification
1965 {
1966     b_mediaKeySupport = var_InheritBool(VLCIntf, "macosx-mediakeys");
1967     if (b_mediaKeySupport) {
1968         if (!o_mediaKeyController)
1969             o_mediaKeyController = [[SPMediaKeyTap alloc] initWithDelegate:self];
1970         [o_mediaKeyController startWatchingMediaKeys];
1971     }
1972     else if (!b_mediaKeySupport && o_mediaKeyController)
1973         [o_mediaKeyController stopWatchingMediaKeys];
1974 }
1975
1976 @end
1977
1978 /*****************************************************************************
1979  * VLCApplication interface
1980  *****************************************************************************/
1981
1982 @implementation VLCApplication
1983 // when user selects the quit menu from dock it sends a terminate:
1984 // but we need to send a stop: to properly exits libvlc.
1985 // However, we are not able to change the action-method sent by this standard menu item.
1986 // thus we override terminate: to send a stop:
1987 // see [af97f24d528acab89969d6541d83f17ce1ecd580] that introduced the removal of setjmp() and longjmp()
1988 - (void)terminate:(id)sender
1989 {
1990     [self activateIgnoringOtherApps:YES];
1991     [self stop:sender];
1992 }
1993
1994 @end