]> git.sesse.net Git - vlc/blob - modules/gui/macosx/MainMenu.m
Correct widespread misuse of the word 'subtitle'
[vlc] / modules / gui / macosx / MainMenu.m
1 /*****************************************************************************
2  * MainMenu.m: MacOS X interface module
3  *****************************************************************************
4  * Copyright (C) 2011-2013 Felix Paul Kühne
5  * $Id$
6  *
7  * Authors: Felix Paul Kühne <fkuehne -at- videolan -dot- org>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22  *****************************************************************************/
23
24 #import "MainMenu.h"
25 #import <vlc_common.h>
26 #import <vlc_playlist.h>
27 #import <CoreAudio/CoreAudio.h>
28
29 #import "intf.h"
30 #import "open.h"
31 #import "wizard.h"
32 #import "about.h"
33 #import "AudioEffects.h"
34 #import "TrackSynchronization.h"
35 #import "VideoEffects.h"
36 #import "bookmarks.h"
37 #import "simple_prefs.h"
38 #import "coredialogs.h"
39 #import "controls.h"
40 #import "playlist.h"
41 #import "playlistinfo.h"
42 #import "VideoView.h"
43 #import "CoreInteraction.h"
44 #import "MainWindow.h"
45 #import "ControlsBar.h"
46 #import "ExtensionsManager.h"
47 #import "ConvertAndSave.h"
48
49 static OSStatus HardwareListener        (AudioObjectID, UInt32, const AudioObjectPropertyAddress *, void *);
50
51 static OSStatus HardwareListener(AudioObjectID inObjectID,  UInt32 inNumberAddresses, const AudioObjectPropertyAddress inAddresses[], void*inClientData)
52 {
53     VLC_UNUSED(inObjectID);
54     VLC_UNUSED(inNumberAddresses);
55     VLC_UNUSED(inAddresses);
56     // give the core some time update its internal structure for the new device setup
57     [[VLCMainMenu sharedInstance] performSelector:@selector(refreshAudioDeviceList) withObject:nil afterDelay:.5];
58
59     return noErr;
60 }
61
62 @implementation VLCMainMenu
63 static VLCMainMenu *_o_sharedInstance = nil;
64
65 + (VLCMainMenu *)sharedInstance
66 {
67     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
68 }
69
70 #pragma mark -
71 #pragma mark Initialization
72
73 - (id)init
74 {
75     if (_o_sharedInstance) {
76         [self dealloc];
77         return _o_sharedInstance;
78     } else {
79         _o_sharedInstance = [super init];
80
81         o_ptc_translation_dict = [NSDictionary dictionaryWithObjectsAndKeys:
82                       _NS("Track Number"),  TRACKNUM_COLUMN,
83                       _NS("Title"),         TITLE_COLUMN,
84                       _NS("Author"),        ARTIST_COLUMN,
85                       _NS("Duration"),      DURATION_COLUMN,
86                       _NS("Genre"),         GENRE_COLUMN,
87                       _NS("Album"),         ALBUM_COLUMN,
88                       _NS("Description"),   DESCRIPTION_COLUMN,
89                       _NS("Date"),          DATE_COLUMN,
90                       _NS("Language"),      LANGUAGE_COLUMN,
91                       _NS("URI"),           URI_COLUMN,
92                       nil];
93         // this array also assigns tags (index) to type of menu item
94         o_ptc_menuorder = [NSArray arrayWithObjects:TRACKNUM_COLUMN, TITLE_COLUMN, ARTIST_COLUMN, DURATION_COLUMN,
95                        GENRE_COLUMN, ALBUM_COLUMN, DESCRIPTION_COLUMN, DATE_COLUMN, LANGUAGE_COLUMN, URI_COLUMN, nil];
96     }
97
98     return _o_sharedInstance;
99 }
100
101 - (void)dealloc
102 {
103     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices, kAudioObjectPropertyScopeGlobal, kAudioObjectPropertyElementMaster };
104     OSStatus err = AudioObjectRemovePropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, nil);
105     if (err != noErr)
106         msg_Err(p_intf, "failed to add audio hardware listener (%i)", err);
107
108     [[NSNotificationCenter defaultCenter] removeObserver: self];
109
110     if (b_nib_about_loaded)
111         [o_about release];
112
113     if (b_nib_videoeffects_loaded)
114         [o_videoeffects release];
115
116     if (b_nib_audioeffects_loaded)
117         [o_audioeffects release];
118
119     if (b_nib_tracksynchro_loaded)
120         [o_trackSynchronization release];
121
122     if (b_nib_convertandsave_loaded)
123         [o_convertandsave release];
124
125     [o_extMgr release];
126
127     if (o_mu_playlistTableColumnsContextMenu)
128         [o_mu_playlistTableColumnsContextMenu release];
129
130     [self releaseRepresentedObjects:[NSApp mainMenu]];
131
132     [super dealloc];
133 }
134
135 - (void)awakeFromNib
136 {
137     [[NSNotificationCenter defaultCenter] addObserver: self
138                                              selector: @selector(applicationWillFinishLaunching:)
139                                                  name: NSApplicationWillFinishLaunchingNotification
140                                                object: nil];
141
142     /* check whether the user runs OSX with a RTL language */
143     NSArray* languages = [NSLocale preferredLanguages];
144     NSString* preferredLanguage = [languages objectAtIndex:0];
145
146     if ([NSLocale characterDirectionForLanguage:preferredLanguage] == NSLocaleLanguageDirectionRightToLeft) {
147         msg_Dbg(VLCIntf, "adapting interface since '%s' is a RTL language", [preferredLanguage UTF8String]);
148         [o_mi_rate_fld setAlignment: NSLeftTextAlignment];
149     }
150 }
151
152 - (void)applicationWillFinishLaunching:(NSNotification *)o_notification
153 {
154     p_intf = VLCIntf;
155
156     NSString* o_key;
157     playlist_t *p_playlist;
158     vlc_value_t val;
159     id o_vlcstringutility = [VLCStringUtility sharedInstance];
160     char * key;
161
162     /* Check if we already did this once. Opening the other nibs calls it too,
163      because VLCMain is the owner */
164     if (b_mainMenu_setup)
165         return;
166
167     /* Get ExtensionsManager */
168     o_extMgr = [ExtensionsManager getInstance:p_intf];
169     [o_extMgr retain];
170
171     [self initStrings];
172
173     key = config_GetPsz(p_intf, "key-quit");
174     o_key = [NSString stringWithFormat:@"%s", key];
175     [o_mi_quit setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
176     [o_mi_quit setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
177     FREENULL(key);
178
179     key = config_GetPsz(p_intf, "key-play-pause");
180     o_key = [NSString stringWithFormat:@"%s", key];
181     [o_mi_play setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
182     [o_mi_play setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
183     FREENULL(key);
184
185     key = config_GetPsz(p_intf, "key-stop");
186     o_key = [NSString stringWithFormat:@"%s", key];
187     [o_mi_stop setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
188     [o_mi_stop setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
189     FREENULL(key);
190
191     key = config_GetPsz(p_intf, "key-prev");
192     o_key = [NSString stringWithFormat:@"%s", key];
193     [o_mi_previous setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
194     [o_mi_previous setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
195     FREENULL(key);
196
197     key = config_GetPsz(p_intf, "key-next");
198     o_key = [NSString stringWithFormat:@"%s", key];
199     [o_mi_next setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
200     [o_mi_next setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
201     FREENULL(key);
202
203     key = config_GetPsz(p_intf, "key-jump+short");
204     o_key = [NSString stringWithFormat:@"%s", key];
205     [o_mi_fwd setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
206     [o_mi_fwd setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
207     FREENULL(key);
208
209     key = config_GetPsz(p_intf, "key-jump-short");
210     o_key = [NSString stringWithFormat:@"%s", key];
211     [o_mi_bwd setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
212     [o_mi_bwd setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
213     FREENULL(key);
214
215     key = config_GetPsz(p_intf, "key-vol-up");
216     o_key = [NSString stringWithFormat:@"%s", key];
217     [o_mi_vol_up setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
218     [o_mi_vol_up setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
219     FREENULL(key);
220
221     key = config_GetPsz(p_intf, "key-vol-down");
222     o_key = [NSString stringWithFormat:@"%s", key];
223     [o_mi_vol_down setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
224     [o_mi_vol_down setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
225     FREENULL(key);
226
227     key = config_GetPsz(p_intf, "key-vol-mute");
228     o_key = [NSString stringWithFormat:@"%s", key];
229     [o_mi_mute setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
230     [o_mi_mute setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
231     FREENULL(key);
232
233     key = config_GetPsz(p_intf, "key-toggle-fullscreen");
234     o_key = [NSString stringWithFormat:@"%s", key];
235     [o_mi_fullscreen setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
236     [o_mi_fullscreen setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
237     FREENULL(key);
238
239     key = config_GetPsz(p_intf, "key-snapshot");
240     o_key = [NSString stringWithFormat:@"%s", key];
241     [o_mi_snapshot setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
242     [o_mi_snapshot setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
243     FREENULL(key);
244
245     key = config_GetPsz(p_intf, "key-random");
246     o_key = [NSString stringWithFormat:@"%s", key];
247     [o_mi_random setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
248     [o_mi_random setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
249     FREENULL(key);
250
251     key = config_GetPsz(p_intf, "key-zoom-half");
252     o_key = [NSString stringWithFormat:@"%s", key];
253     [o_mi_half_window setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
254     [o_mi_half_window setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
255     FREENULL(key);
256
257     key = config_GetPsz(p_intf, "key-zoom-original");
258     o_key = [NSString stringWithFormat:@"%s", key];
259     [o_mi_normal_window setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
260     [o_mi_normal_window setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
261     FREENULL(key);
262
263     key = config_GetPsz(p_intf, "key-zoom-double");
264     o_key = [NSString stringWithFormat:@"%s", key];
265     [o_mi_double_window setKeyEquivalent: [o_vlcstringutility VLCKeyToString: o_key]];
266     [o_mi_double_window setKeyEquivalentModifierMask: [o_vlcstringutility VLCModifiersToCocoa:o_key]];
267     FREENULL(key);
268
269     [self setSubmenusEnabled: FALSE];
270
271     [[NSNotificationCenter defaultCenter] addObserver: self
272                                              selector: @selector(refreshVoutDeviceMenu:)
273                                                  name: NSApplicationDidChangeScreenParametersNotification
274                                                object: nil];
275
276     /* we're done */
277     b_mainMenu_setup = YES;
278
279     [self setupVarMenuItem: o_mi_add_intf target: (vlc_object_t *)p_intf
280                              var: "intf-add" selector: @selector(toggleVar:)];
281
282     [self setupExtensionsMenu];
283
284     [self refreshAudioDeviceList];
285
286     AudioObjectPropertyAddress audioDevicesAddress = { kAudioHardwarePropertyDevices,
287                                                        kAudioObjectPropertyScopeGlobal,
288                                                        kAudioObjectPropertyElementMaster };
289     OSStatus err = AudioObjectAddPropertyListener(kAudioObjectSystemObject, &audioDevicesAddress, HardwareListener, nil);
290     if (err != noErr)
291         msg_Err(p_intf, "failed to add audio hardware listener (%i)", err);
292 }
293
294 - (void)initStrings
295 {
296     /* main menu */
297     [o_mi_about setTitle: [_NS("About VLC media player") \
298                            stringByAppendingString: @"..."]];
299     [o_mi_checkForUpdate setTitle: _NS("Check for Update...")];
300     [o_mi_prefs setTitle: _NS("Preferences...")];
301     [o_mi_extensions setTitle: _NS("Extensions")];
302     [o_mu_extensions setTitle: _NS("Extensions")];
303     [o_mi_add_intf setTitle: _NS("Add Interface")];
304     [o_mu_add_intf setTitle: _NS("Add Interface")];
305     [o_mi_services setTitle: _NS("Services")];
306     [o_mi_hide setTitle: _NS("Hide VLC")];
307     [o_mi_hide_others setTitle: _NS("Hide Others")];
308     [o_mi_show_all setTitle: _NS("Show All")];
309     [o_mi_quit setTitle: _NS("Quit VLC")];
310
311     [o_mu_file setTitle: _ANS("1:File")];
312     [o_mi_open_generic setTitle: _NS("Advanced Open File...")];
313     [o_mi_open_file setTitle: _NS("Open File...")];
314     [o_mi_open_disc setTitle: _NS("Open Disc...")];
315     [o_mi_open_net setTitle: _NS("Open Network...")];
316     [o_mi_open_capture setTitle: _NS("Open Capture Device...")];
317     [o_mi_open_recent setTitle: _NS("Open Recent")];
318     [o_mi_open_wizard setTitle: _NS("Streaming/Exporting Wizard...")];
319     [o_mi_convertandsave setTitle: _NS("Convert / Stream...")];
320
321     [o_mu_edit setTitle: _NS("Edit")];
322     [o_mi_cut setTitle: _NS("Cut")];
323     [o_mi_copy setTitle: _NS("Copy")];
324     [o_mi_paste setTitle: _NS("Paste")];
325     [o_mi_clear setTitle: _NS("Clear")];
326     [o_mi_select_all setTitle: _NS("Select All")];
327
328     [o_mu_view setTitle: _NS("View")];
329     [o_mi_toggleJumpButtons setTitle: _NS("Show Previous & Next Buttons")];
330     [o_mi_toggleJumpButtons setState: config_GetInt(VLCIntf, "macosx-show-playback-buttons")];
331     [o_mi_togglePlaymodeButtons setTitle: _NS("Show Shuffle & Repeat Buttons")];
332     [o_mi_togglePlaymodeButtons setState: config_GetInt(VLCIntf, "macosx-show-playmode-buttons")];
333     [o_mi_toggleSidebar setTitle: _NS("Show Sidebar")];
334     [o_mi_toggleSidebar setState: config_GetInt(VLCIntf, "macosx-show-sidebar")];
335     [o_mu_playlistTableColumns setTitle: _NS("Playlist Table Columns")];
336
337     [o_mu_controls setTitle: _NS("Playback")];
338     [o_mi_play setTitle: _NS("Play")];
339     [o_mi_stop setTitle: _NS("Stop")];
340     [o_mi_record setTitle: _NS("Record")];
341     [o_mi_rate setView: o_mi_rate_view];
342     [o_mi_rate_lbl setStringValue: _NS("Playback Speed")];
343     [o_mi_rate_lbl_gray setStringValue: _NS("Playback Speed")];
344     [o_mi_rate_slower_lbl setStringValue: _NS("Slower")];
345     [o_mi_rate_normal_lbl setStringValue: _NS("Normal")];
346     [o_mi_rate_faster_lbl setStringValue: _NS("Faster")];
347     [o_mi_trackSynchronization setTitle: _NS("Track Synchronization")];
348     [o_mi_previous setTitle: _NS("Previous")];
349     [o_mi_next setTitle: _NS("Next")];
350     [o_mi_random setTitle: _NS("Random")];
351     [o_mi_repeat setTitle: _NS("Repeat One")];
352     [o_mi_loop setTitle: _NS("Repeat All")];
353     [o_mi_AtoBloop setTitle: _NS("A→B Loop")];
354     [o_mi_quitAfterPB setTitle: _NS("Quit after Playback")];
355     [o_mi_fwd setTitle: _NS("Step Forward")];
356     [o_mi_bwd setTitle: _NS("Step Backward")];
357
358     [o_mi_program setTitle: _NS("Program")];
359     [o_mu_program setTitle: _NS("Program")];
360     [o_mi_title setTitle: _NS("Title")];
361     [o_mu_title setTitle: _NS("Title")];
362     [o_mi_chapter setTitle: _NS("Chapter")];
363     [o_mu_chapter setTitle: _NS("Chapter")];
364
365     [o_mu_audio setTitle: _NS("Audio")];
366     [o_mi_vol_up setTitle: _NS("Increase Volume")];
367     [o_mi_vol_down setTitle: _NS("Decrease Volume")];
368     [o_mi_mute setTitle: _NS("Mute")];
369     [o_mi_audiotrack setTitle: _NS("Audio Track")];
370     [o_mu_audiotrack setTitle: _NS("Audio Track")];
371     [o_mi_channels setTitle: _NS("Audio Channels")];
372     [o_mu_channels setTitle: _NS("Audio Channels")];
373     [o_mi_device setTitle: _NS("Audio Device")];
374     [o_mu_device setTitle: _NS("Audio Device")];
375     [o_mi_visual setTitle: _NS("Visualizations")];
376     [o_mu_visual setTitle: _NS("Visualizations")];
377
378     [o_mu_video setTitle: _NS("Video")];
379     [o_mi_half_window setTitle: _NS("Half Size")];
380     [o_mi_normal_window setTitle: _NS("Normal Size")];
381     [o_mi_double_window setTitle: _NS("Double Size")];
382     [o_mi_fittoscreen setTitle: _NS("Fit to Screen")];
383     [o_mi_fullscreen setTitle: _NS("Fullscreen")];
384     [o_mi_floatontop setTitle: _NS("Float on Top")];
385     [o_mi_snapshot setTitle: _NS("Snapshot")];
386     [o_mi_videotrack setTitle: _NS("Video Track")];
387     [o_mu_videotrack setTitle: _NS("Video Track")];
388     [o_mi_aspect_ratio setTitle: _NS("Aspect-ratio")];
389     [o_mu_aspect_ratio setTitle: _NS("Aspect-ratio")];
390     [o_mi_crop setTitle: _NS("Crop")];
391     [o_mu_crop setTitle: _NS("Crop")];
392     [o_mi_screen setTitle: _NS("Fullscreen Video Device")];
393     [o_mu_screen setTitle: _NS("Fullscreen Video Device")];
394     [o_mi_subtitle setTitle: _NS("Subtitle Track")];
395     [o_mu_subtitle setTitle: _NS("Subtitle Track")];
396     [o_mi_addSub setTitle: _NS("Open File...")];
397     [o_mi_deinterlace setTitle: _NS("Deinterlace")];
398     [o_mu_deinterlace setTitle: _NS("Deinterlace")];
399     [o_mi_deinterlace_mode setTitle: _NS("Deinterlace mode")];
400     [o_mu_deinterlace_mode setTitle: _NS("Deinterlace mode")];
401     [o_mi_ffmpeg_pp setTitle: _NS("Post processing")];
402     [o_mu_ffmpeg_pp setTitle: _NS("Post processing")];
403     [o_mi_teletext setTitle: _NS("Teletext")];
404     [o_mi_teletext_transparent setTitle: _NS("Transparent")];
405     [o_mi_teletext_index setTitle: _NS("Index")];
406     [o_mi_teletext_red setTitle: _NS("Red")];
407     [o_mi_teletext_green setTitle: _NS("Green")];
408     [o_mi_teletext_yellow setTitle: _NS("Yellow")];
409     [o_mi_teletext_blue setTitle: _NS("Blue")];
410
411     [o_mu_window setTitle: _NS("Window")];
412     [o_mi_minimize setTitle: _NS("Minimize Window")];
413     [o_mi_close_window setTitle: _NS("Close Window")];
414     [o_mi_player setTitle: _NS("Player...")];
415     [o_mi_controller setTitle: _NS("Main Window...")];
416     [o_mi_audioeffects setTitle: _NS("Audio Effects...")];
417     [o_mi_videoeffects setTitle: _NS("Video Effects...")];
418     [o_mi_bookmarks setTitle: _NS("Bookmarks...")];
419     [o_mi_playlist setTitle: _NS("Playlist...")];
420     [o_mi_info setTitle: _NS("Media Information...")];
421     [o_mi_messages setTitle: _NS("Messages...")];
422     [o_mi_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
423
424     [o_mi_bring_atf setTitle: _NS("Bring All to Front")];
425
426     [o_mu_help setTitle: _NS("Help")];
427     [o_mi_help setTitle: _NS("VLC media player Help...")];
428     [o_mi_readme setTitle: _NS("ReadMe / FAQ...")];
429     [o_mi_license setTitle: _NS("License")];
430     [o_mi_documentation setTitle: _NS("Online Documentation...")];
431     [o_mi_website setTitle: _NS("VideoLAN Website...")];
432     [o_mi_donation setTitle: _NS("Make a donation...")];
433     [o_mi_forum setTitle: _NS("Online Forum...")];
434
435     /* dock menu */
436     [o_dmi_play setTitle: _NS("Play")];
437     [o_dmi_stop setTitle: _NS("Stop")];
438     [o_dmi_next setTitle: _NS("Next")];
439     [o_dmi_previous setTitle: _NS("Previous")];
440     [o_dmi_mute setTitle: _NS("Mute")];
441
442     /* vout menu */
443     [o_vmi_play setTitle: _NS("Play")];
444     [o_vmi_stop setTitle: _NS("Stop")];
445     [o_vmi_prev setTitle: _NS("Previous")];
446     [o_vmi_next setTitle: _NS("Next")];
447     [o_vmi_volup setTitle: _NS("Volume Up")];
448     [o_vmi_voldown setTitle: _NS("Volume Down")];
449     [o_vmi_mute setTitle: _NS("Mute")];
450     [o_vmi_fullscreen setTitle: _NS("Fullscreen")];
451     [o_vmi_snapshot setTitle: _NS("Snapshot")];
452 }
453
454 - (NSMenu *)setupPlaylistTableColumnsMenu
455 {
456     NSMenu *o_context_menu = [[NSMenu alloc] init];
457
458     NSMenuItem *o_mi_tmp;
459     NSUInteger count = [o_ptc_menuorder count];
460     for (NSUInteger i = 0; i < count; i++) {
461         NSString *o_title = [o_ptc_translation_dict objectForKey:[o_ptc_menuorder objectAtIndex:i]];
462         o_mi_tmp = [o_mu_playlistTableColumns addItemWithTitle:o_title
463                                                         action:@selector(togglePlaylistColumnTable:)
464                                                  keyEquivalent:@""];
465         /* don't set a valid target for the title column selector, since we want it to be disabled */
466         if (![[o_ptc_menuorder objectAtIndex:i] isEqualToString: TITLE_COLUMN])
467             [o_mi_tmp setTarget:self];
468         [o_mi_tmp setTag:i];
469
470         o_mi_tmp = [o_context_menu addItemWithTitle:o_title
471                                              action:@selector(togglePlaylistColumnTable:)
472                                       keyEquivalent:@""];
473         /* don't set a valid target for the title column selector, since we want it to be disabled */
474         if (![[o_ptc_menuorder objectAtIndex:i] isEqualToString: TITLE_COLUMN])
475             [o_mi_tmp setTarget:self];
476         [o_mi_tmp setTag:i];
477     }
478     if (!o_mu_playlistTableColumnsContextMenu)
479         o_mu_playlistTableColumnsContextMenu = [o_context_menu retain];
480     return [o_context_menu autorelease];
481 }
482
483 #pragma mark -
484 #pragma mark Termination
485
486 - (void)releaseRepresentedObjects:(NSMenu *)the_menu
487 {
488     if (!p_intf) return;
489
490     NSArray *menuitems_array = [the_menu itemArray];
491     NSUInteger menuItemCount = [menuitems_array count];
492     for (NSUInteger i=0; i < menuItemCount; i++) {
493         NSMenuItem *one_item = [menuitems_array objectAtIndex: i];
494         if ([one_item hasSubmenu])
495             [self releaseRepresentedObjects: [one_item submenu]];
496
497         [one_item setRepresentedObject:NULL];
498     }
499 }
500
501 #pragma mark -
502 #pragma mark Interface update
503
504 - (void)setupMenus
505 {
506     playlist_t * p_playlist = pl_Get(p_intf);
507     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
508     if (p_input != NULL) {
509         [o_mi_record setEnabled: var_GetBool(p_input, "can-record")];
510
511         [self setupVarMenuItem: o_mi_program target: (vlc_object_t *)p_input
512                                  var: "program" selector: @selector(toggleVar:)];
513
514         [self setupVarMenuItem: o_mi_title target: (vlc_object_t *)p_input
515                                  var: "title" selector: @selector(toggleVar:)];
516
517         [self setupVarMenuItem: o_mi_chapter target: (vlc_object_t *)p_input
518                                  var: "chapter" selector: @selector(toggleVar:)];
519
520         [self setupVarMenuItem: o_mi_audiotrack target: (vlc_object_t *)p_input
521                                  var: "audio-es" selector: @selector(toggleVar:)];
522
523         [self setupVarMenuItem: o_mi_videotrack target: (vlc_object_t *)p_input
524                                  var: "video-es" selector: @selector(toggleVar:)];
525
526         [self setupVarMenuItem: o_mi_subtitle target: (vlc_object_t *)p_input
527                                  var: "spu-es" selector: @selector(toggleVar:)];
528
529         /* special case for "Open File" inside the subtitles menu item */
530         if ([o_mi_videotrack isEnabled] == YES)
531             [o_mi_subtitle setEnabled: YES];
532
533         audio_output_t * p_aout = playlist_GetAout(p_playlist);
534         if (p_aout != NULL) {
535             [self setupVarMenuItem: o_mi_channels target: (vlc_object_t *)p_aout
536                                      var: "stereo-mode" selector: @selector(toggleVar:)];
537
538             [self setupVarMenuItem: o_mi_visual target: (vlc_object_t *)p_aout
539                                      var: "visual" selector: @selector(toggleVar:)];
540             vlc_object_release(p_aout);
541         }
542
543         vout_thread_t * p_vout = getVoutForActiveWindow();
544
545         if (p_vout != NULL) {
546             [self setupVarMenuItem: o_mi_aspect_ratio target: (vlc_object_t *)p_vout
547                                      var: "aspect-ratio" selector: @selector(toggleVar:)];
548
549             [self setupVarMenuItem: o_mi_crop target: (vlc_object_t *) p_vout
550                                      var: "crop" selector: @selector(toggleVar:)];
551
552             [self setupVarMenuItem: o_mi_deinterlace target: (vlc_object_t *)p_vout
553                                      var: "deinterlace" selector: @selector(toggleVar:)];
554
555             [self setupVarMenuItem: o_mi_deinterlace_mode target: (vlc_object_t *)p_vout
556                                      var: "deinterlace-mode" selector: @selector(toggleVar:)];
557
558 #if 1
559             [self setupVarMenuItem: o_mi_ffmpeg_pp target:
560              (vlc_object_t *)p_vout var:"postprocess" selector:
561              @selector(toggleVar:)];
562 #endif
563             vlc_object_release(p_vout);
564
565             [self refreshVoutDeviceMenu:nil];
566         }
567         vlc_object_release(p_input);
568     }
569     else
570         [o_mi_record setEnabled: NO];
571 }
572
573 - (void)refreshVoutDeviceMenu:(NSNotification *)o_notification
574 {
575     NSUInteger count = [o_mu_screen numberOfItems];
576     NSMenu * o_submenu = o_mu_screen;
577     if (count > 0)
578         [o_submenu removeAllItems];
579
580     NSArray * o_screens = [NSScreen screens];
581     NSMenuItem * o_mitem;
582     count = [o_screens count];
583     [o_mi_screen setEnabled: YES];
584     [o_submenu addItemWithTitle: _NS("Default") action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
585     o_mitem = [o_submenu itemAtIndex: 0];
586     [o_mitem setTag: 0];
587     [o_mitem setEnabled: YES];
588     [o_mitem setTarget: self];
589     NSRect s_rect;
590     for (NSUInteger i = 0; i < count; i++) {
591         s_rect = [[o_screens objectAtIndex: i] frame];
592         [o_submenu addItemWithTitle: [NSString stringWithFormat: @"%@ %li (%ix%i)", _NS("Screen"), i+1,
593                                       (int)s_rect.size.width, (int)s_rect.size.height] action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
594         o_mitem = [o_submenu itemAtIndex:i+1];
595         [o_mitem setTag: (int)[[o_screens objectAtIndex: i] displayID]];
596         [o_mitem setEnabled: YES];
597         [o_mitem setTarget: self];
598     }
599     [[o_submenu itemWithTag: var_InheritInteger(VLCIntf, "macosx-vdev")] setState: NSOnState];
600 }
601
602 - (void)setSubmenusEnabled:(BOOL)b_enabled
603 {
604     [o_mi_program setEnabled: b_enabled];
605     [o_mi_title setEnabled: b_enabled];
606     [o_mi_chapter setEnabled: b_enabled];
607     [o_mi_audiotrack setEnabled: b_enabled];
608     [o_mi_visual setEnabled: b_enabled];
609     [o_mi_videotrack setEnabled: b_enabled];
610     [o_mi_subtitle setEnabled: b_enabled];
611     [o_mi_channels setEnabled: b_enabled];
612     [o_mi_deinterlace setEnabled: b_enabled];
613     [o_mi_deinterlace_mode setEnabled: b_enabled];
614     [o_mi_ffmpeg_pp setEnabled: b_enabled];
615     [o_mi_screen setEnabled: b_enabled];
616     [o_mi_aspect_ratio setEnabled: b_enabled];
617     [o_mi_crop setEnabled: b_enabled];
618     [o_mi_teletext setEnabled: b_enabled];
619 }
620
621 - (void)setRateControlsEnabled:(BOOL)b_enabled
622 {
623     NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
624     [o_mi_rate_sld setEnabled: b_enabled];
625     [o_mi_rate_sld setIntValue: [[VLCCoreInteraction sharedInstance] playbackRate]];
626     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
627     double speed =  pow(2, (double)i / 17);
628     [o_mi_rate_fld setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
629     if (b_enabled) {
630         [o_mi_rate_lbl setHidden: NO];
631         [o_mi_rate_lbl_gray setHidden: YES];
632     } else {
633         [o_mi_rate_lbl setHidden: YES];
634         [o_mi_rate_lbl_gray setHidden: NO];
635     }
636     [o_pool release];
637 }
638
639 #pragma mark -
640 #pragma mark Extensions
641
642 - (void)setupExtensionsMenu
643 {
644     /* Load extensions if needed */
645     // TODO: Implement preference for autoloading extensions on mac
646
647     // if (!var_InheritBool(p_intf, "qt-autoload-extensions")
648     //     && ![o_extMgr isLoaded])
649     // {
650     //     return;
651     // }
652
653     if (![o_extMgr isLoaded] && ![o_extMgr cannotLoad]) {
654         [o_extMgr loadExtensions];
655     }
656
657     /* Let the ExtensionsManager itself build the menu */
658     [o_extMgr buildMenu:o_mu_extensions];
659     [o_mi_extensions setEnabled: ([o_mu_extensions numberOfItems] > 0)];
660 }
661
662 #pragma mark -
663 #pragma mark View
664 - (IBAction)toggleJumpButtons:(id)sender
665 {
666     BOOL b_value = !config_GetInt(VLCIntf, "macosx-show-playback-buttons");
667     config_PutInt(VLCIntf, "macosx-show-playback-buttons", b_value);
668     [[[[VLCMain sharedInstance] mainWindow] controlsBar] toggleJumpButtons];
669     [o_mi_toggleJumpButtons setState: b_value];
670 }
671
672 - (IBAction)togglePlaymodeButtons:(id)sender
673 {
674     BOOL b_value = !config_GetInt(VLCIntf, "macosx-show-playmode-buttons");
675     config_PutInt(VLCIntf, "macosx-show-playmode-buttons", b_value);
676     [[[[VLCMain sharedInstance] mainWindow] controlsBar] togglePlaymodeButtons];
677     [o_mi_togglePlaymodeButtons setState: b_value];
678 }
679
680 - (IBAction)toggleSidebar:(id)sender
681 {
682     BOOL b_value = !config_GetInt(VLCIntf, "macosx-show-sidebar");
683     config_PutInt(VLCIntf, "macosx-show-sidebar", b_value);
684     [[[VLCMain sharedInstance] mainWindow] toggleLeftSubSplitView];
685     [o_mi_toggleSidebar setState: b_value];
686 }
687
688 - (void)updateSidebarMenuItem
689 {
690     [o_mi_toggleSidebar setState: config_GetInt(VLCIntf, "macosx-show-sidebar")];
691 }
692
693 - (IBAction)togglePlaylistColumnTable:(id)sender
694 {
695     NSInteger i_new_state = ![sender state];
696     NSInteger i_tag = [sender tag];
697     [[o_mu_playlistTableColumns            itemWithTag: i_tag] setState: i_new_state];
698     [[o_mu_playlistTableColumnsContextMenu itemWithTag: i_tag] setState: i_new_state];
699
700     NSString *o_column = [o_ptc_menuorder objectAtIndex: i_tag];
701     [[[VLCMain sharedInstance] playlist] setColumn: o_column state: i_new_state translationDict: o_ptc_translation_dict];
702 }
703
704 - (void)setPlaylistColumnTableState:(NSInteger)i_state forColumn:(NSString *)o_column
705 {
706     NSInteger i_tag = [o_ptc_menuorder indexOfObject: o_column];
707     [[o_mu_playlistTableColumns            itemWithTag: i_tag] setState: i_state];
708     [[o_mu_playlistTableColumnsContextMenu itemWithTag: i_tag] setState: i_state];
709     [[[VLCMain sharedInstance] playlist] setColumn: o_column state: i_state translationDict: o_ptc_translation_dict];
710 }
711
712 #pragma mark -
713 #pragma mark Playback
714 - (IBAction)toggleRecord:(id)sender
715 {
716     [[VLCCoreInteraction sharedInstance] toggleRecord];
717 }
718
719 - (void)updateRecordState:(BOOL)b_value
720 {
721     [o_mi_record setState:b_value];
722 }
723
724 - (IBAction)setPlaybackRate:(id)sender
725 {
726     [[VLCCoreInteraction sharedInstance] setPlaybackRate: [o_mi_rate_sld intValue]];
727     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
728     double speed =  pow(2, (double)i / 17);
729     [o_mi_rate_fld setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
730 }
731
732 - (void)updatePlaybackRate
733 {
734     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
735     double speed =  pow(2, (double)i / 17);
736     [o_mi_rate_fld setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
737     [o_mi_rate_sld setIntValue: i];
738 }
739
740 - (IBAction)toggleAtoBloop:(id)sender
741 {
742     [[VLCCoreInteraction sharedInstance] setAtoB];
743 }
744
745 #pragma mark -
746 #pragma mark audio menu
747 - (void)refreshAudioDeviceList
748 {
749     char **ids, **names;
750     char *currentDevice;
751
752     [o_mu_device removeAllItems];
753
754     audio_output_t * p_aout = getAout();
755     if (!p_aout)
756         return;
757
758     int n = aout_DevicesList(p_aout, &ids, &names);
759     if (n == -1) {
760         vlc_object_release(p_aout);
761         return;
762     }
763
764     currentDevice = aout_DeviceGet(p_aout);
765     NSMenuItem * o_mi_tmp;
766
767     for (NSUInteger x = 0; x < n; x++) {
768         o_mi_tmp = [o_mu_device addItemWithTitle:[NSString stringWithFormat:@"%s", names[x]] action:@selector(toggleAudioDevice:) keyEquivalent:@""];
769         [o_mi_tmp setTarget:self];
770         [o_mi_tmp setTag:[[NSString stringWithFormat:@"%s", ids[x]] intValue]];
771     }
772     vlc_object_release(p_aout);
773
774     [[o_mu_device itemWithTag:[[NSString stringWithFormat:@"%s", currentDevice] intValue]] setState:NSOnState];
775
776     for (NSUInteger x = 0; x < n; x++) {
777         free(ids[x]);
778         free(names[x]);
779     }
780     free(ids);
781     free(names);
782
783     [o_mu_device setAutoenablesItems:YES];
784     [o_mi_device setEnabled:YES];
785 }
786
787 - (IBAction)toggleAudioDevice:(id)sender
788 {
789     audio_output_t * p_aout = getAout();
790     if (!p_aout)
791         return;
792
793     int returnValue = 0;
794
795     if ([sender tag] > 0)
796         returnValue = aout_DeviceSet(p_aout, [[NSString stringWithFormat:@"%li", [sender tag]] UTF8String]);
797     else
798         returnValue = aout_DeviceSet(p_aout, NULL);
799
800     if (returnValue != 0)
801         msg_Warn(VLCIntf, "failed to set audio device %li", [sender tag]);
802
803     vlc_object_release(p_aout);
804     [self refreshAudioDeviceList];
805 }
806
807 #pragma mark -
808 #pragma mark video menu
809
810 - (IBAction)toggleFullscreen:(id)sender
811 {
812     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
813 }
814
815 - (IBAction)resizeVideoWindow:(id)sender
816 {
817     input_thread_t *p_input = pl_CurrentInput(VLCIntf);
818     if (p_input) {
819         vout_thread_t *p_vout = getVoutForActiveWindow();
820         if (p_vout) {
821             if (sender == o_mi_half_window)
822                 var_SetFloat(p_vout, "zoom", 0.5);
823             else if (sender == o_mi_normal_window)
824                 var_SetFloat(p_vout, "zoom", 1.0);
825             else if (sender == o_mi_double_window)
826                 var_SetFloat(p_vout, "zoom", 2.0);
827             else
828             {
829                 [[NSApp keyWindow] performZoom:sender];
830             }
831             vlc_object_release(p_vout);
832         }
833         vlc_object_release(p_input);
834     }
835 }
836
837 - (IBAction)floatOnTop:(id)sender
838 {
839     input_thread_t *p_input = pl_CurrentInput(VLCIntf);
840     if (p_input) {
841         vout_thread_t *p_vout = getVoutForActiveWindow();
842         if (p_vout) {
843             var_ToggleBool(p_vout, "video-on-top");
844             vlc_object_release(p_vout);
845         }
846         vlc_object_release(p_input);
847     }
848 }
849
850 - (IBAction)createVideoSnapshot:(id)sender
851 {
852     input_thread_t *p_input = pl_CurrentInput(VLCIntf);
853     if (p_input) {
854         vout_thread_t *p_vout = getVoutForActiveWindow();
855         if (p_vout) {
856             var_TriggerCallback(p_vout, "video-snapshot");
857             vlc_object_release(p_vout);
858         }
859         vlc_object_release(p_input);
860     }
861 }
862
863 - (IBAction)toggleFullscreenDevice:(id)sender
864 {
865     config_PutInt(VLCIntf, "macosx-vdev", [sender tag]);
866     [self refreshVoutDeviceMenu: nil];
867 }
868
869 - (id)voutMenu
870 {
871     return o_vout_menu;
872 }
873
874 #pragma mark -
875 #pragma mark Panels
876
877 - (IBAction)intfOpenFile:(id)sender
878 {
879     [[[VLCMain sharedInstance] open] openFile];
880 }
881
882 - (IBAction)intfOpenFileGeneric:(id)sender
883 {
884     [[[VLCMain sharedInstance] open] openFileGeneric];
885 }
886
887 - (IBAction)intfOpenDisc:(id)sender
888 {
889     [[[VLCMain sharedInstance] open] openDisc];
890 }
891
892 - (IBAction)intfOpenNet:(id)sender
893 {
894     [[[VLCMain sharedInstance] open] openNet];
895 }
896
897 - (IBAction)intfOpenCapture:(id)sender
898 {
899     [[[VLCMain sharedInstance] open] openCapture];
900 }
901
902 - (IBAction)showWizard:(id)sender
903 {
904     [[[VLCMain sharedInstance] wizard] resetWizard];
905     [[[VLCMain sharedInstance] wizard] showWizard];
906 }
907
908 - (IBAction)showConvertAndSave:(id)sender
909 {
910     if (o_convertandsave == nil)
911         o_convertandsave = [[VLCConvertAndSave alloc] init];
912
913     if (!b_nib_convertandsave_loaded)
914         b_nib_convertandsave_loaded = [NSBundle loadNibNamed:@"ConvertAndSave" owner: NSApp];
915
916     [o_convertandsave toggleWindow];
917 }
918
919 - (IBAction)showVideoEffects:(id)sender
920 {
921     if (o_videoeffects == nil)
922         o_videoeffects = [[VLCVideoEffects alloc] init];
923
924     if (!b_nib_videoeffects_loaded)
925         b_nib_videoeffects_loaded = [NSBundle loadNibNamed:@"VideoEffects" owner: NSApp];
926
927     [o_videoeffects toggleWindow:sender];
928 }
929
930 - (IBAction)showTrackSynchronization:(id)sender
931 {
932     if (!o_trackSynchronization)
933         o_trackSynchronization = [[VLCTrackSynchronization alloc] init];
934
935     if (!b_nib_tracksynchro_loaded)
936         b_nib_tracksynchro_loaded = [NSBundle loadNibNamed:@"SyncTracks" owner:NSApp];
937
938     [o_trackSynchronization toggleWindow:sender];
939 }
940
941 - (IBAction)showAudioEffects:(id)sender
942 {
943     if (!o_audioeffects)
944         o_audioeffects = [[VLCAudioEffects alloc] init];
945
946     if (!b_nib_audioeffects_loaded)
947         b_nib_audioeffects_loaded = [NSBundle loadNibNamed:@"AudioEffects" owner:NSApp];
948
949     [o_audioeffects toggleWindow:sender];
950 }
951
952 - (IBAction)showBookmarks:(id)sender
953 {
954     [[[VLCMain sharedInstance] bookmarks] showBookmarks];
955 }
956
957 - (IBAction)viewPreferences:(id)sender
958 {
959     NSInteger i_level = [[[VLCMain sharedInstance] voutController] currentWindowLevel];
960     [[[VLCMain sharedInstance] simplePreferences] showSimplePrefsWithLevel:i_level];
961 }
962
963 #pragma mark -
964 #pragma mark Help and Docs
965
966 - (void)initAbout
967 {
968     if (! o_about)
969         o_about = [[VLAboutBox alloc] init];
970
971     if (!b_nib_about_loaded)
972         b_nib_about_loaded = [NSBundle loadNibNamed:@"About" owner: NSApp];
973 }
974
975 - (IBAction)viewAbout:(id)sender
976 {
977     [self initAbout];
978     [o_about showAbout];
979 }
980
981 - (IBAction)showLicense:(id)sender
982 {
983     [self initAbout];
984     [o_about showGPL: sender];
985 }
986
987 - (IBAction)viewHelp:(id)sender
988 {
989     [self initAbout];
990     [o_about showHelp];
991 }
992
993 - (IBAction)openReadMe:(id)sender
994 {
995     NSString * o_path = [[NSBundle mainBundle] pathForResource: @"README.MacOSX" ofType: @"rtf"];
996
997     [[NSWorkspace sharedWorkspace] openFile: o_path withApplication: @"TextEdit"];
998 }
999
1000 - (IBAction)openDocumentation:(id)sender
1001 {
1002     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/doc/"];
1003
1004     [[NSWorkspace sharedWorkspace] openURL: o_url];
1005 }
1006
1007 - (IBAction)openWebsite:(id)sender
1008 {
1009     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/"];
1010
1011     [[NSWorkspace sharedWorkspace] openURL: o_url];
1012 }
1013
1014 - (IBAction)openForum:(id)sender
1015 {
1016     NSURL * o_url = [NSURL URLWithString: @"http://forum.videolan.org/"];
1017
1018     [[NSWorkspace sharedWorkspace] openURL: o_url];
1019 }
1020
1021 - (IBAction)openDonate:(id)sender
1022 {
1023     NSURL * o_url = [NSURL URLWithString: @"http://www.videolan.org/contribute.html#paypal"];
1024
1025     [[NSWorkspace sharedWorkspace] openURL: o_url];
1026 }
1027
1028 #pragma mark -
1029 #pragma mark Errors, warnings and messages
1030
1031 - (IBAction)viewErrorsAndWarnings:(id)sender
1032 {
1033     [[[[VLCMain sharedInstance] coreDialogProvider] errorPanel] showPanel];
1034 }
1035
1036 - (IBAction)showInformationPanel:(id)sender
1037 {
1038     [[[VLCMain sharedInstance] info] initPanel];
1039 }
1040
1041 #pragma mark -
1042 #pragma mark convinience stuff for other objects
1043 - (void)setPlay
1044 {
1045     [o_mi_play setTitle: _NS("Play")];
1046     [o_dmi_play setTitle: _NS("Play")];
1047     [o_vmi_play setTitle: _NS("Play")];
1048 }
1049
1050 - (void)setPause
1051 {
1052     [o_mi_play setTitle: _NS("Pause")];
1053     [o_dmi_play setTitle: _NS("Pause")];
1054     [o_vmi_play setTitle: _NS("Pause")];
1055 }
1056
1057 - (void)setRepeatOne
1058 {
1059     [o_mi_repeat setState: NSOnState];
1060     [o_mi_loop setState: NSOffState];
1061 }
1062
1063 - (void)setRepeatAll
1064 {
1065     [o_mi_repeat setState: NSOffState];
1066     [o_mi_loop setState: NSOnState];
1067 }
1068
1069 - (void)setRepeatOff
1070 {
1071     [o_mi_repeat setState: NSOffState];
1072     [o_mi_loop setState: NSOffState];
1073 }
1074
1075 - (void)setShuffle
1076 {
1077     bool b_value;
1078     playlist_t *p_playlist = pl_Get(VLCIntf);
1079     b_value = var_GetBool(p_playlist, "random");
1080
1081     [o_mi_random setState: b_value];
1082 }
1083
1084 #pragma mark -
1085 #pragma mark Dynamic menu creation and validation
1086
1087 - (void)setupVarMenuItem:(NSMenuItem *)o_mi
1088                   target:(vlc_object_t *)p_object
1089                      var:(const char *)psz_variable
1090                 selector:(SEL)pf_callback
1091 {
1092     vlc_value_t val, text;
1093     int i_type = var_Type(p_object, psz_variable);
1094
1095     switch(i_type & VLC_VAR_TYPE) {
1096         case VLC_VAR_VOID:
1097         case VLC_VAR_BOOL:
1098         case VLC_VAR_VARIABLE:
1099         case VLC_VAR_STRING:
1100         case VLC_VAR_INTEGER:
1101             break;
1102         default:
1103             /* Variable doesn't exist or isn't handled */
1104             return;
1105     }
1106
1107     /* Get the descriptive name of the variable */
1108     var_Change(p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL);
1109     [o_mi setTitle: _NS(text.psz_string ? text.psz_string : psz_variable)];
1110
1111     if (i_type & VLC_VAR_HASCHOICE) {
1112         NSMenu *o_menu = [o_mi submenu];
1113
1114         [self setupVarMenu: o_menu forMenuItem: o_mi target:p_object
1115                        var:psz_variable selector:pf_callback];
1116
1117         free(text.psz_string);
1118         return;
1119     }
1120
1121     if (var_Get(p_object, psz_variable, &val) < 0) {
1122         return;
1123     }
1124
1125     VLCAutoGeneratedMenuContent *o_data;
1126     switch(i_type & VLC_VAR_TYPE) {
1127         case VLC_VAR_VOID:
1128             o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1129                                                                       andValue: val ofType: i_type];
1130             [o_mi setRepresentedObject: [o_data autorelease]];
1131             break;
1132
1133         case VLC_VAR_BOOL:
1134             o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1135                                                                       andValue: val ofType: i_type];
1136             [o_mi setRepresentedObject: [o_data autorelease]];
1137             if (!(i_type & VLC_VAR_ISCOMMAND))
1138                 [o_mi setState: val.b_bool ? TRUE : FALSE ];
1139             break;
1140
1141         default:
1142             break;
1143     }
1144
1145     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1146     free(text.psz_string);
1147 }
1148
1149
1150 - (void)setupVarMenu:(NSMenu *)o_menu
1151          forMenuItem: (NSMenuItem *)o_parent
1152               target:(vlc_object_t *)p_object
1153                  var:(const char *)psz_variable
1154             selector:(SEL)pf_callback
1155 {
1156     vlc_value_t val, val_list, text_list;
1157     int i_type, i;
1158
1159     /* remove previous items */
1160     [o_menu removeAllItems];
1161
1162     /* we disable everything here, and enable it again when needed, below */
1163     [o_parent setEnabled:NO];
1164
1165     /* Aspect Ratio */
1166     if ([[o_parent title] isEqualToString: _NS("Aspect-ratio")] == YES) {
1167         NSMenuItem *o_lmi_tmp2;
1168         o_lmi_tmp2 = [o_menu addItemWithTitle: _NS("Lock Aspect Ratio") action: @selector(lockVideosAspectRatio:) keyEquivalent: @""];
1169         [o_lmi_tmp2 setTarget: [[VLCMain sharedInstance] controls]];
1170         [o_lmi_tmp2 setEnabled: YES];
1171         [o_lmi_tmp2 setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
1172         [o_parent setEnabled: YES];
1173         [o_menu addItem: [NSMenuItem separatorItem]];
1174     }
1175
1176     /* special case for the subtitles item */
1177     if ([[o_parent title] isEqualToString: _NS("Subtitle Track")] == YES) {
1178         NSMenuItem * o_lmi_tmp;
1179         o_lmi_tmp = [o_menu addItemWithTitle: _NS("Open File...") action: @selector(addSubtitleFile:) keyEquivalent: @""];
1180         [o_lmi_tmp setTarget: [[VLCMain sharedInstance] controls]];
1181         [o_lmi_tmp setEnabled: YES];
1182         [o_parent setEnabled: YES];
1183     }
1184
1185     /* Check the type of the object variable */
1186     i_type = var_Type(p_object, psz_variable);
1187
1188     /* Make sure we want to display the variable */
1189     if (i_type & VLC_VAR_HASCHOICE) {
1190         var_Change(p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL);
1191         if (val.i_int == 0)
1192             return;
1193         if ((i_type & VLC_VAR_TYPE) != VLC_VAR_VARIABLE && val.i_int == 1)
1194             return;
1195     }
1196     else
1197         return;
1198
1199     switch(i_type & VLC_VAR_TYPE) {
1200         case VLC_VAR_VOID:
1201         case VLC_VAR_BOOL:
1202         case VLC_VAR_VARIABLE:
1203         case VLC_VAR_STRING:
1204         case VLC_VAR_INTEGER:
1205             break;
1206         default:
1207             /* Variable doesn't exist or isn't handled */
1208             return;
1209     }
1210
1211     if (var_Get(p_object, psz_variable, &val) < 0) {
1212         return;
1213     }
1214
1215     if (var_Change(p_object, psz_variable, VLC_VAR_GETLIST,
1216                    &val_list, &text_list) < 0) {
1217         if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1218         return;
1219     }
1220
1221     /* make (un)sensitive */
1222     [o_parent setEnabled: (val_list.p_list->i_count > 1)];
1223
1224     /* another special case for the subtitles item */
1225     if ([[o_parent title] isEqualToString: _NS("Subtitle Track")] == YES)
1226         [o_menu addItem: [NSMenuItem separatorItem]];
1227
1228     for (i = 0; i < val_list.p_list->i_count; i++) {
1229         NSMenuItem * o_lmi;
1230         NSString *o_title = @"";
1231         VLCAutoGeneratedMenuContent *o_data;
1232
1233         switch(i_type & VLC_VAR_TYPE) {
1234             case VLC_VAR_STRING:
1235
1236                 o_title = _NS(text_list.p_list->p_values[i].psz_string ? text_list.p_list->p_values[i].psz_string : val_list.p_list->p_values[i].psz_string);
1237
1238                 o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
1239                 o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1240                                                                           andValue: val_list.p_list->p_values[i] ofType: i_type];
1241                 [o_lmi setRepresentedObject: [o_data autorelease]];
1242                 [o_lmi setTarget: self];
1243
1244                 if (!strcmp(val.psz_string, val_list.p_list->p_values[i].psz_string) && !(i_type & VLC_VAR_ISCOMMAND))
1245                     [o_lmi setState: TRUE ];
1246
1247                 break;
1248
1249             case VLC_VAR_INTEGER:
1250
1251                 o_title = text_list.p_list->p_values[i].psz_string ?
1252                 _NS(text_list.p_list->p_values[i].psz_string) : [NSString stringWithFormat: @"%"PRId64, val_list.p_list->p_values[i].i_int];
1253
1254                 o_lmi = [o_menu addItemWithTitle: o_title action: pf_callback keyEquivalent: @""];
1255                 o_data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1256                                                                           andValue: val_list.p_list->p_values[i] ofType: i_type];
1257                 [o_lmi setRepresentedObject: [o_data autorelease]];
1258                 [o_lmi setTarget: self];
1259
1260                 if (val_list.p_list->p_values[i].i_int == val.i_int && !(i_type & VLC_VAR_ISCOMMAND))
1261                     [o_lmi setState: TRUE ];
1262                 break;
1263
1264             default:
1265                 break;
1266         }
1267     }
1268
1269     /* special case for the subtitles sub-menu
1270      * In case that we don't have any subs, we don't want a separator item at the end */
1271     if ([[o_parent title] isEqualToString: _NS("Subtitle Track")] == YES) {
1272         if ([o_menu numberOfItems] == 2)
1273             [o_menu removeItemAtIndex: 1];
1274     }
1275
1276     /* clean up everything */
1277     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1278     var_FreeList(&val_list, &text_list);
1279 }
1280
1281 - (IBAction)toggleVar:(id)sender
1282 {
1283     NSMenuItem *o_mi = (NSMenuItem *)sender;
1284     VLCAutoGeneratedMenuContent *o_data = [o_mi representedObject];
1285     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
1286                              toTarget: self withObject: o_data];
1287
1288     return;
1289 }
1290
1291 - (int)toggleVarThread: (id)data
1292 {
1293     vlc_object_t *p_object;
1294     NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
1295
1296     assert([data isKindOfClass:[VLCAutoGeneratedMenuContent class]]);
1297     VLCAutoGeneratedMenuContent *menuContent = (VLCAutoGeneratedMenuContent *)data;
1298
1299     /* Preserve settings across vouts via the playlist object: */
1300     if (!strcmp([menuContent name], "fullscreen") || !strcmp([menuContent name], "video-on-top"))
1301         var_Set(pl_Get(VLCIntf), [menuContent name] , [menuContent value]);
1302
1303     p_object = [menuContent vlcObject];
1304
1305     if (p_object != NULL) {
1306         var_Set(p_object, [menuContent name], [menuContent value]);
1307         vlc_object_release(p_object);
1308         [o_pool release];
1309         return true;
1310     }
1311     [o_pool release];
1312     return VLC_EGENERIC;
1313 }
1314
1315 @end
1316
1317 @implementation VLCMainMenu (NSMenuValidation)
1318
1319 - (BOOL)validateMenuItem:(NSMenuItem *)o_mi
1320 {
1321     NSString *o_title = [o_mi title];
1322     BOOL bEnabled = TRUE;
1323     vlc_value_t val;
1324     playlist_t * p_playlist = pl_Get(p_intf);
1325     input_thread_t * p_input = playlist_CurrentInput(p_playlist);
1326
1327     if ([o_title isEqualToString: _NS("Stop")]) {
1328         if (p_input == NULL) {
1329             bEnabled = FALSE;
1330         }
1331         [self setupMenus]; /* Make sure input menu is up to date */
1332     } else if ([o_title isEqualToString: _NS("Previous")] ||
1333             [o_title isEqualToString: _NS("Next")]) {
1334         PL_LOCK;
1335         bEnabled = playlist_CurrentSize(p_playlist) > 1;
1336         PL_UNLOCK;
1337     } else if ([o_title isEqualToString: _NS("Random")]) {
1338         int i_state;
1339         var_Get(p_playlist, "random", &val);
1340         i_state = val.b_bool ? NSOnState : NSOffState;
1341         [o_mi setState: i_state];
1342     } else if ([o_title isEqualToString: _NS("Repeat One")]) {
1343         int i_state;
1344         var_Get(p_playlist, "repeat", &val);
1345         i_state = val.b_bool ? NSOnState : NSOffState;
1346         [o_mi setState: i_state];
1347     } else if ([o_title isEqualToString: _NS("Repeat All")]) {
1348         int i_state;
1349         var_Get(p_playlist, "loop", &val);
1350         i_state = val.b_bool ? NSOnState : NSOffState;
1351         [o_mi setState: i_state];
1352     } else if ([o_title isEqualToString: _NS("Quit after Playback")]) {
1353         int i_state;
1354         var_Get(p_playlist, "play-and-exit", &val);
1355         i_state = val.b_bool ? NSOnState : NSOffState;
1356         [o_mi setState: i_state];
1357     } else if ([o_title isEqualToString: _NS("Step Forward")] ||
1358                [o_title isEqualToString: _NS("Step Backward")] ||
1359                [o_title isEqualToString: _NS("Jump To Time")]) {
1360         if (p_input != NULL) {
1361             var_Get(p_input, "can-seek", &val);
1362             bEnabled = val.b_bool;
1363         }
1364         else bEnabled = FALSE;
1365     } else if ([o_title isEqualToString: _NS("Mute")]) {
1366         [o_mi setState: [[VLCCoreInteraction sharedInstance] mute] ? NSOnState : NSOffState];
1367         [self setupMenus]; /* Make sure audio menu is up to date */
1368     } else if ([o_title isEqualToString: _NS("Half Size")] ||
1369                [o_title isEqualToString: _NS("Normal Size")] ||
1370                [o_title isEqualToString: _NS("Double Size")] ||
1371                [o_title isEqualToString: _NS("Fit to Screen")] ||
1372                [o_title isEqualToString: _NS("Snapshot")] ||
1373                [o_title isEqualToString: _NS("Fullscreen")] ||
1374                [o_title isEqualToString: _NS("Float on Top")]) {
1375         bEnabled = FALSE;
1376
1377         if (p_input != NULL) {
1378             vout_thread_t *p_vout = getVoutForActiveWindow();
1379             if (p_vout != NULL) {
1380                 if ([o_title isEqualToString: _NS("Float on Top")])
1381                     [o_mi setState: var_GetBool(p_vout, "video-on-top")];
1382
1383                 if ([o_title isEqualToString: _NS("Fullscreen")])
1384                     [o_mi setState: var_GetBool(p_vout, "fullscreen")];
1385
1386                 bEnabled = TRUE;
1387                 vlc_object_release(p_vout);
1388             }
1389         }
1390
1391         [self setupMenus]; /* Make sure video menu is up to date */
1392     }
1393
1394     /* Special case for telx menu */
1395     if ([o_title isEqualToString: _NS("Normal Size")]) {
1396         NSMenuItem *item = [[o_mi menu] itemWithTitle:_NS("Teletext")];
1397         bool b_telx = p_input && var_GetInteger(p_input, "teletext-es") >= 0;
1398
1399         [[item submenu] setAutoenablesItems:NO];
1400
1401         for (int k=0; k < [[item submenu] numberOfItems]; k++)
1402             [[[item submenu] itemAtIndex:k] setEnabled: b_telx];
1403     }
1404
1405     if (p_input)
1406         vlc_object_release(p_input);
1407
1408     return bEnabled ;
1409 }
1410
1411 @end
1412
1413
1414 /*****************************************************************************
1415  * VLCAutoGeneratedMenuContent implementation
1416  *****************************************************************************
1417  * Object connected to a playlistitem which remembers the data belonging to
1418  * the variable of the autogenerated menu
1419  *****************************************************************************/
1420 @implementation VLCAutoGeneratedMenuContent
1421
1422 -(id) initWithVariableName:(const char *)name ofObject:(vlc_object_t *)object
1423                   andValue:(vlc_value_t)val ofType:(int)type
1424 {
1425     self = [super init];
1426
1427     if (self != nil) {
1428         _vlc_object = vlc_object_hold(object);
1429         psz_name = strdup(name);
1430         i_type = type;
1431         value = val;
1432         if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1433             value.psz_string = strdup(val.psz_string);
1434     }
1435
1436     return(self);
1437 }
1438
1439 - (void)dealloc
1440 {
1441     if (_vlc_object)
1442         vlc_object_release(_vlc_object);
1443     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1444         free(value.psz_string);
1445     free(psz_name);
1446     [super dealloc];
1447 }
1448
1449 - (const char *)name
1450 {
1451     return psz_name;
1452 }
1453
1454 - (vlc_value_t)value
1455 {
1456     return value;
1457 }
1458
1459 - (vlc_object_t *)vlcObject
1460 {
1461     return vlc_object_hold(_vlc_object);
1462 }
1463
1464
1465 - (int)type
1466 {
1467     return i_type;
1468 }
1469
1470 @end