gui/macosx: use input vars instead of controls
[vlc.git] / modules / gui / macosx / VLCMainMenu.m
blob7d93b8114647b26e29abcc5dae23604d47a78452
1 /*****************************************************************************
2  *MainMenu.m: MacOS X interface module
3  *****************************************************************************
4  *Copyright (C) 2011-2018 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  *****************************************************************************/
24 #import "VLCMainMenu.h"
25 #import "VLCMain.h"
27 #import <vlc_common.h>
28 #import <vlc_playlist.h>
29 #import <vlc_input.h>
31 #import "VLCAboutWindowController.h"
32 #import "VLCOpenWindowController.h"
33 #import "VLCAudioEffectsWindowController.h"
34 #import "VLCErrorWindowController.h"
35 #import "VLCTrackSynchronizationWindowController.h"
36 #import "VLCHelpWindowController.h"
37 #import "VLCVideoEffectsWindowController.h"
38 #import "VLCBookmarksWindowController.h"
39 #import "VLCSimplePrefsController.h"
40 #import "VLCPlaylist.h"
41 #import "VLCPlaylistInfo.h"
42 #import "VLCVoutView.h"
43 #import "VLCCoreDialogProvider.h"
44 #import "VLCCoreInteraction.h"
45 #import "VLCMainWindow.h"
46 #import "VLCMainWindowControlsBar.h"
47 #import "VLCExtensionsManager.h"
48 #import "VLCConvertAndSaveWindowController.h"
49 #import "VLCLogWindowController.h"
50 #import "VLCAddonsWindowController.h"
51 #import "VLCTimeSelectionPanelController.h"
52 #import "NSScreen+VLCAdditions.h"
53 #import "VLCRendererMenuController.h"
55 #ifdef HAVE_SPARKLE
56 #import <Sparkle/Sparkle.h>
57 #endif
59 @interface VLCMainMenu() <NSMenuDelegate>
61     VLCAboutWindowController *_aboutWindowController;
62     VLCHelpWindowController  *_helpWindowController;
63     VLCAddonsWindowController *_addonsController;
64     VLCRendererMenuController *_rendererMenuController;
65     NSTimer *_cancelRendererDiscoveryTimer;
67     NSMenu *_playlistTableColumnsContextMenu;
69     __strong VLCTimeSelectionPanelController *_timeSelectionPanel;
71 @end
73 @implementation VLCMainMenu
75 #pragma mark - Initialization
77 - (void)dealloc
79     msg_Dbg(getIntf(), "Deinitializing main menu");
80     [[NSNotificationCenter defaultCenter] removeObserver: self];
82     [self releaseRepresentedObjects:[NSApp mainMenu]];
85 - (void)awakeFromNib
87     _timeSelectionPanel = [[VLCTimeSelectionPanelController alloc] init];
89     /* check whether the user runs OSX with a RTL language */
90     NSArray* languages = [NSLocale preferredLanguages];
91     NSString* preferredLanguage = [languages firstObject];
93     if ([NSLocale characterDirectionForLanguage:preferredLanguage] == NSLocaleLanguageDirectionRightToLeft) {
94         msg_Dbg(getIntf(), "adapting interface since '%s' is a RTL language", [preferredLanguage UTF8String]);
95         [_rateTextField setAlignment: NSLeftTextAlignment];
96     }
98     [self setRateControlsEnabled:NO];
100 #ifdef HAVE_SPARKLE
101     [_checkForUpdate setAction:@selector(checkForUpdates:)];
102     [_checkForUpdate setTarget:[SUUpdater sharedUpdater]];
103 #else
104     [_checkForUpdate setEnabled:NO];
105 #endif
107     NSString* keyString;
108     VLCStringUtility *stringUtility = [VLCStringUtility sharedInstance];
109     char *key;
111     /* Get ExtensionsManager */
112     intf_thread_t *p_intf = getIntf();
114     [self initStrings];
116     key = config_GetPsz("key-quit");
117     keyString = [NSString stringWithFormat:@"%s", key];
118     [_quit setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
119     [_quit setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
120     FREENULL(key);
122     // do not assign play/pause key
124     key = config_GetPsz("key-stop");
125     keyString = [NSString stringWithFormat:@"%s", key];
126     [_stop setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
127     [_stop setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
128     FREENULL(key);
130     key = config_GetPsz("key-prev");
131     keyString = [NSString stringWithFormat:@"%s", key];
132     [_previous setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
133     [_previous setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
134     FREENULL(key);
136     key = config_GetPsz("key-next");
137     keyString = [NSString stringWithFormat:@"%s", key];
138     [_next setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
139     [_next setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
140     FREENULL(key);
142     key = config_GetPsz("key-jump+short");
143     keyString = [NSString stringWithFormat:@"%s", key];
144     [_fwd setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
145     [_fwd setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
146     FREENULL(key);
148     key = config_GetPsz("key-jump-short");
149     keyString = [NSString stringWithFormat:@"%s", key];
150     [_bwd setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
151     [_bwd setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
152     FREENULL(key);
154     key = config_GetPsz("key-vol-up");
155     keyString = [NSString stringWithFormat:@"%s", key];
156     [_vol_up setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
157     [_vol_up setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
158     FREENULL(key);
160     key = config_GetPsz("key-vol-down");
161     keyString = [NSString stringWithFormat:@"%s", key];
162     [_vol_down setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
163     [_vol_down setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
164     FREENULL(key);
166     key = config_GetPsz("key-vol-mute");
167     keyString = [NSString stringWithFormat:@"%s", key];
168     [_mute setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
169     [_mute setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
170     FREENULL(key);
172     key = config_GetPsz("key-toggle-fullscreen");
173     keyString = [NSString stringWithFormat:@"%s", key];
174     [_fullscreenItem setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
175     [_fullscreenItem setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
176     FREENULL(key);
178     key = config_GetPsz("key-snapshot");
179     keyString = [NSString stringWithFormat:@"%s", key];
180     [_snapshot setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
181     [_snapshot setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
182     FREENULL(key);
184     key = config_GetPsz("key-random");
185     keyString = [NSString stringWithFormat:@"%s", key];
186     [_random setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
187     [_random setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
188     FREENULL(key);
190     key = config_GetPsz("key-zoom-half");
191     keyString = [NSString stringWithFormat:@"%s", key];
192     [_half_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
193     [_half_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
194     FREENULL(key);
196     key = config_GetPsz("key-zoom-original");
197     keyString = [NSString stringWithFormat:@"%s", key];
198     [_normal_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
199     [_normal_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
200     FREENULL(key);
202     key = config_GetPsz("key-zoom-double");
203     keyString = [NSString stringWithFormat:@"%s", key];
204     [_double_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
205     [_double_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
206     FREENULL(key);
208     [self setSubmenusEnabled: FALSE];
210     /* configure playback / controls menu */
211     self.controlsMenu.delegate = self;
212     [_rendererNoneItem setState:NSOnState];
213     _rendererMenuController = [[VLCRendererMenuController alloc] init];
214     _rendererMenuController.rendererNoneItem = _rendererNoneItem;
215     _rendererMenuController.rendererMenu = _rendererMenu;
217     [[NSNotificationCenter defaultCenter] addObserver: self
218                                              selector: @selector(refreshVoutDeviceMenu:)
219                                                  name: NSApplicationDidChangeScreenParametersNotification
220                                                object: nil];
222     [self setupVarMenuItem:_add_intf target: (vlc_object_t *)p_intf
223                              var:"intf-add" selector: @selector(toggleVar:)];
225     /* setup extensions menu */
226     /* Let the ExtensionsManager itself build the menu */
227     VLCExtensionsManager *extMgr = [[VLCMain sharedInstance] extensionsManager];
228     [extMgr buildMenu:_extensionsMenu];
229     [_extensions setEnabled: ([_extensionsMenu numberOfItems] > 0)];
231     // FIXME: Implement preference for autoloading extensions on mac
232     if (![extMgr isLoaded] && ![extMgr cannotLoad])
233         [extMgr loadExtensions];
235     /* setup post-proc menu */
236     NSUInteger count = (NSUInteger) [_postprocessingMenu numberOfItems];
237     if (count > 0)
238         [_postprocessingMenu removeAllItems];
240     NSMenuItem *mitem;
241     [_postprocessingMenu setAutoenablesItems: YES];
242     [_postprocessingMenu addItemWithTitle: _NS("Disable") action:@selector(togglePostProcessing:) keyEquivalent:@""];
243     mitem = [_postprocessingMenu itemAtIndex: 0];
244     [mitem setTag: -1];
245     [mitem setEnabled: YES];
246     [mitem setTarget: self];
247     for (NSUInteger x = 1; x < 7; x++) {
248         [_postprocessingMenu addItemWithTitle:[NSString stringWithFormat:_NS("Level %i"), x]
249                                                action:@selector(togglePostProcessing:)
250                                         keyEquivalent:@""];
251         mitem = [_postprocessingMenu itemAtIndex:x];
252         [mitem setEnabled:YES];
253         [mitem setTag:x];
254         [mitem setTarget:self];
255     }
256     char *psz_config = config_GetPsz("video-filter");
257     if (psz_config) {
258         if (!strstr(psz_config, "postproc"))
259             [[_postprocessingMenu itemAtIndex:0] setState:NSOnState];
260         else
261             [[_postprocessingMenu itemWithTag:config_GetInt("postproc-q")] setState:NSOnState];
262         free(psz_config);
263     } else
264         [[_postprocessingMenu itemAtIndex:0] setState:NSOnState];
265     [_postprocessing setEnabled: NO];
267     [self refreshAudioDeviceList];
269     /* setup subtitles menu */
270     // Persist those variables on the playlist
271     playlist_t *p_playlist = pl_Get(getIntf());
272     var_Create(p_playlist, "freetype-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
273     var_Create(p_playlist, "freetype-background-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
274     var_Create(p_playlist, "freetype-background-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
275     var_Create(p_playlist, "freetype-outline-thickness", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
277     [self setupMenu: _subtitle_textcolorMenu withIntList:"freetype-color" andSelector:@selector(switchSubtitleOption:)];
278     [_subtitle_bgopacity_sld setIntegerValue: config_GetInt("freetype-background-opacity")];
279     [self setupMenu: _subtitle_bgcolorMenu withIntList:"freetype-background-color" andSelector:@selector(switchSubtitleOption:)];
280     [self setupMenu: _subtitle_outlinethicknessMenu withIntList:"freetype-outline-thickness" andSelector:@selector(switchSubtitleOption:)];
282     /* Build size menu based on different scale factors */
283     struct {
284         const char *const name;
285         int scaleValue;
286     } scaleValues[] = {
287         { N_("Smaller"), 50},
288         { N_("Small"),   75},
289         { N_("Normal"), 100},
290         { N_("Large"),  125},
291         { N_("Larger"), 150},
292         { NULL, 0 }
293     };
295     for(int i = 0; scaleValues[i].name; i++) {
296         NSMenuItem *menuItem = [_subtitle_sizeMenu addItemWithTitle: _NS(scaleValues[i].name) action:@selector(switchSubtitleSize:) keyEquivalent:@""];
297         [menuItem setTag:scaleValues[i].scaleValue];
298         [menuItem setTarget: self];
299     }
302 - (void)setupMenu: (NSMenu*)menu withIntList: (char *)psz_name andSelector:(SEL)selector
304     module_config_t *p_item;
306     [menu removeAllItems];
307     p_item = config_FindConfig(psz_name);
309     if (!p_item) {
310         msg_Err(getIntf(), "couldn't create menu int list for item '%s' as it does not exist", psz_name);
311         return;
312     }
314     for (int i = 0; i < p_item->list_count; i++) {
315         NSMenuItem *mi;
316         if (p_item->list_text != NULL)
317             mi = [[NSMenuItem alloc] initWithTitle: _NS(p_item->list_text[i]) action:NULL keyEquivalent: @""];
318         else if (p_item->list.i[i])
319             mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%d", p_item->list.i[i]] action:NULL keyEquivalent: @""];
320         else {
321             msg_Err(getIntf(), "item %d of pref %s failed to be created", i, psz_name);
322             continue;
323         }
325         [mi setTarget:self];
326         [mi setAction:selector];
327         [mi setTag:p_item->list.i[i]];
328         [mi setRepresentedObject:toNSStr(psz_name)];
329         [menu addItem:mi];
330         if (p_item->value.i == p_item->list.i[i])
331             [mi setState:NSOnState];
332     }
335 - (void)initStrings
337     /* main menu */
338     [_about setTitle: [_NS("About VLC media player") \
339                            stringByAppendingString: @"..."]];
340     [_checkForUpdate setTitle: _NS("Check for Update...")];
341     [_prefs setTitle: _NS("Preferences...")];
342     [_extensions setTitle: _NS("Extensions")];
343     [_extensionsMenu setTitle: _NS("Extensions")];
344     [_addonManager setTitle: _NS("Addons Manager")];
345     [_add_intf setTitle: _NS("Add Interface")];
346     [_add_intfMenu setTitle: _NS("Add Interface")];
347     [_services setTitle: _NS("Services")];
348     [_hide setTitle: _NS("Hide VLC")];
349     [_hide_others setTitle: _NS("Hide Others")];
350     [_show_all setTitle: _NS("Show All")];
351     [_quit setTitle: _NS("Quit VLC")];
353     [_fileMenu setTitle: _ANS("1:File")];
354     [_open_generic setTitle: _NS("Advanced Open File...")];
355     [_open_file setTitle: _NS("Open File...")];
356     [_open_disc setTitle: _NS("Open Disc...")];
357     [_open_net setTitle: _NS("Open Network...")];
358     [_open_capture setTitle: _NS("Open Capture Device...")];
359     [_open_recent setTitle: _NS("Open Recent")];
360     [_close_window setTitle: _NS("Close Window")];
361     [_convertandsave setTitle: _NS("Convert / Stream...")];
362     [_save_playlist setTitle: _NS("Save Playlist...")];
363     [_revealInFinder setTitle: _NS("Reveal in Finder")];
365     [_editMenu setTitle: _NS("Edit")];
366     [_cutItem setTitle: _NS("Cut")];
367     [_mcopyItem setTitle: _NS("Copy")];
368     [_pasteItem setTitle: _NS("Paste")];
369     [_clearItem setTitle: _NS("Delete")];
370     [_select_all setTitle: _NS("Select All")];
371     [_findItem setTitle: _NS("Find")];
373     [_viewMenu setTitle: _NS("View")];
374     [_toggleJumpButtons setTitle: _NS("Show Previous & Next Buttons")];
375     [_toggleJumpButtons setState: var_InheritBool(getIntf(), "macosx-show-playback-buttons")];
376     [_togglePlaymodeButtons setTitle: _NS("Show Shuffle & Repeat Buttons")];
377     [_togglePlaymodeButtons setState: var_InheritBool(getIntf(), "macosx-show-playmode-buttons")];
378     [_toggleEffectsButton setTitle: _NS("Show Audio Effects Button")];
379     [_toggleEffectsButton setState: var_InheritBool(getIntf(), "macosx-show-effects-button")];
380     [_toggleSidebar setTitle: _NS("Show Sidebar")];
381     [_playlistTableColumns setTitle: _NS("Playlist Table Columns")];
383     [_controlsMenu setTitle: _NS("Playback")];
384     [_play setTitle: _NS("Play")];
385     [_stop setTitle: _NS("Stop")];
386     [_record setTitle: _NS("Record")];
387     [_rate_view setAutoresizingMask:NSViewWidthSizable];
388     [_rate setView: _rate_view];
389     [_rateLabel setStringValue: _NS("Playback Speed")];
390     [_rate_slowerLabel setStringValue: _NS("Slower")];
391     [_rate_normalLabel setStringValue: _NS("Normal")];
392     [_rate_fasterLabel setStringValue: _NS("Faster")];
393     [_trackSynchronization setTitle: _NS("Track Synchronization")];
394     [_previous setTitle: _NS("Previous")];
395     [_next setTitle: _NS("Next")];
396     [_random setTitle: _NS("Random")];
397     [_repeat setTitle: _NS("Repeat One")];
398     [_loop setTitle: _NS("Repeat All")];
399     [_AtoBloop setTitle: _NS("A→B Loop")];
400     [_quitAfterPB setTitle: _NS("Quit after Playback")];
401     [_fwd setTitle: _NS("Step Forward")];
402     [_bwd setTitle: _NS("Step Backward")];
403     [_jumpToTime setTitle: _NS("Jump to Time")];
404     [_rendererMenuItem setTitle:_NS("Renderer")];
405     [_rendererNoneItem setTitle:_NS("No renderer")];
406     [_program setTitle: _NS("Program")];
407     [_programMenu setTitle: _NS("Program")];
408     [_title setTitle: _NS("Title")];
409     [_titleMenu setTitle: _NS("Title")];
410     [_chapter setTitle: _NS("Chapter")];
411     [_chapterMenu setTitle: _NS("Chapter")];
413     [_audioMenu setTitle: _NS("Audio")];
414     [_vol_up setTitle: _NS("Increase Volume")];
415     [_vol_down setTitle: _NS("Decrease Volume")];
416     [_mute setTitle: _NS("Mute")];
417     [_audiotrack setTitle: _NS("Audio Track")];
418     [_audiotrackMenu setTitle: _NS("Audio Track")];
419     [_channels setTitle: _NS("Stereo audio mode")];
420     [_channelsMenu setTitle: _NS("Stereo audio mode")];
421     [_audioDevice setTitle: _NS("Audio Device")];
422     [_audioDeviceMenu setTitle: _NS("Audio Device")];
423     [_visual setTitle: _NS("Visualizations")];
424     [_visualMenu setTitle: _NS("Visualizations")];
426     [_videoMenu setTitle: _NS("Video")];
427     [_half_window setTitle: _NS("Half Size")];
428     [_normal_window setTitle: _NS("Normal Size")];
429     [_double_window setTitle: _NS("Double Size")];
430     [_fittoscreen setTitle: _NS("Fit to Screen")];
431     [_fullscreenItem setTitle: _NS("Fullscreen")];
432     [_floatontop setTitle: _NS("Float on Top")];
433     [_snapshot setTitle: _NS("Snapshot")];
434     [_videotrack setTitle: _NS("Video Track")];
435     [_videotrackMenu setTitle: _NS("Video Track")];
436     [_aspect_ratio setTitle: _NS("Aspect ratio")];
437     [_aspect_ratioMenu setTitle: _NS("Aspect ratio")];
438     [_crop setTitle: _NS("Crop")];
439     [_cropMenu setTitle: _NS("Crop")];
440     [_screen setTitle: _NS("Fullscreen Video Device")];
441     [_screenMenu setTitle: _NS("Fullscreen Video Device")];
442     [_deinterlace setTitle: _NS("Deinterlace")];
443     [_deinterlaceMenu setTitle: _NS("Deinterlace")];
444     [_deinterlace_mode setTitle: _NS("Deinterlace mode")];
445     [_deinterlace_modeMenu setTitle: _NS("Deinterlace mode")];
446     [_postprocessing setTitle: _NS("Post processing")];
447     [_postprocessingMenu setTitle: _NS("Post processing")];
449     [_subtitlesMenu setTitle:_NS("Subtitles")];
450     [_openSubtitleFile setTitle: _NS("Add Subtitle File...")];
451     [_subtitle_track setTitle: _NS("Subtitles Track")];
452     [_subtitle_tracksMenu setTitle: _NS("Subtitles Track")];
453     [_subtitle_size setTitle: _NS("Text Size")];
454     [_subtitle_textcolor setTitle: _NS("Text Color")];
455     [_subtitle_outlinethickness setTitle: _NS("Outline Thickness")];
457     // Autoresizing with constraints does not work on 10.7,
458     // translate autoresizing mask to constriaints for now
459     [_subtitle_bgopacity_view setAutoresizingMask:NSViewWidthSizable];
460     [_subtitle_bgopacity setView: _subtitle_bgopacity_view];
461     [_subtitle_bgopacityLabel setStringValue: _NS("Background Opacity")];
462     [_subtitle_bgopacityLabel_gray setStringValue: _NS("Background Opacity")];
463     [_subtitle_bgcolor setTitle: _NS("Background Color")];
464     [_teletext setTitle: _NS("Teletext")];
465     [_teletext_transparent setTitle: _NS("Transparent")];
466     [_teletext_index setTitle: _NS("Index")];
467     [_teletext_red setTitle: _NS("Red")];
468     [_teletext_green setTitle: _NS("Green")];
469     [_teletext_yellow setTitle: _NS("Yellow")];
470     [_teletext_blue setTitle: _NS("Blue")];
472     [_windowMenu setTitle: _NS("Window")];
473     [_minimize setTitle: _NS("Minimize")];
474     [_zoom_window setTitle: _NS("Zoom")];
475     [_player setTitle: _NS("Player...")];
476     [_controller setTitle: _NS("Main Window...")];
477     [_audioeffects setTitle: _NS("Audio Effects...")];
478     [_videoeffects setTitle: _NS("Video Effects...")];
479     [_bookmarks setTitle: _NS("Bookmarks...")];
480     [_playlist setTitle: _NS("Playlist...")];
481     [_info setTitle: _NS("Media Information...")];
482     [_messages setTitle: _NS("Messages...")];
483     [_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
485     [_bring_atf setTitle: _NS("Bring All to Front")];
487     [_helpMenu setTitle: _NS("Help")];
488     [_help setTitle: _NS("VLC media player Help...")];
489     [_license setTitle: _NS("License")];
490     [_documentation setTitle: _NS("Online Documentation...")];
491     [_website setTitle: _NS("VideoLAN Website...")];
492     [_donation setTitle: _NS("Make a donation...")];
493     [_forum setTitle: _NS("Online Forum...")];
495     /* dock menu */
496     [_dockMenuplay setTitle: _NS("Play")];
497     [_dockMenustop setTitle: _NS("Stop")];
498     [_dockMenunext setTitle: _NS("Next")];
499     [_dockMenuprevious setTitle: _NS("Previous")];
500     [_dockMenumute setTitle: _NS("Mute")];
502     /* vout menu */
503     [_voutMenuplay setTitle: _NS("Play")];
504     [_voutMenustop setTitle: _NS("Stop")];
505     [_voutMenuprev setTitle: _NS("Previous")];
506     [_voutMenunext setTitle: _NS("Next")];
507     [_voutMenuvolup setTitle: _NS("Volume Up")];
508     [_voutMenuvoldown setTitle: _NS("Volume Down")];
509     [_voutMenumute setTitle: _NS("Mute")];
510     [_voutMenufullscreen setTitle: _NS("Fullscreen")];
511     [_voutMenusnapshot setTitle: _NS("Snapshot")];
514 #pragma mark - Termination
516 - (void)releaseRepresentedObjects:(NSMenu *)the_menu
518     NSArray *menuitems_array = [the_menu itemArray];
519     NSUInteger menuItemCount = [menuitems_array count];
520     for (NSUInteger i=0; i < menuItemCount; i++) {
521         NSMenuItem *one_item = [menuitems_array objectAtIndex:i];
522         if ([one_item hasSubmenu])
523             [self releaseRepresentedObjects: [one_item submenu]];
525         [one_item setRepresentedObject:NULL];
526     }
529 #pragma mark - Interface update
531 - (void)setupMenus
533     playlist_t *p_playlist = pl_Get(getIntf());
534     input_thread_t *p_input = playlist_CurrentInput(p_playlist);
535     if (p_input != NULL) {
536         [self setupVarMenuItem:_program target: (vlc_object_t *)p_input
537                                  var:"program" selector: @selector(toggleVar:)];
539         [self setupVarMenuItem:_title target: (vlc_object_t *)p_input
540                                  var:"title" selector: @selector(toggleVar:)];
542         [self setupVarMenuItem:_chapter target: (vlc_object_t *)p_input
543                                  var:"chapter" selector: @selector(toggleVar:)];
545         [self setupVarMenuItem:_audiotrack target: (vlc_object_t *)p_input
546                                  var:"audio-es" selector: @selector(toggleVar:)];
548         [self setupVarMenuItem:_videotrack target: (vlc_object_t *)p_input
549                                  var:"video-es" selector: @selector(toggleVar:)];
551         [self setupVarMenuItem:_subtitle_track target: (vlc_object_t *)p_input
552                                  var:"spu-es" selector: @selector(toggleVar:)];
554         audio_output_t *p_aout = playlist_GetAout(p_playlist);
555         if (p_aout != NULL) {
556             [self setupVarMenuItem:_channels target: (vlc_object_t *)p_aout
557                                      var:"stereo-mode" selector: @selector(toggleVar:)];
559             [self setupVarMenuItem:_visual target: (vlc_object_t *)p_aout
560                                      var:"visual" selector: @selector(toggleVar:)];
561             vlc_object_release(p_aout);
562         }
564         vout_thread_t *p_vout = getVoutForActiveWindow();
566         if (p_vout != NULL) {
567             [self setupVarMenuItem:_aspect_ratio target: (vlc_object_t *)p_vout
568                                      var:"aspect-ratio" selector: @selector(toggleVar:)];
570             [self setupVarMenuItem:_crop target: (vlc_object_t *) p_vout
571                                      var:"crop" selector: @selector(toggleVar:)];
573             [self setupVarMenuItem:_deinterlace target: (vlc_object_t *)p_vout
574                                      var:"deinterlace" selector: @selector(toggleVar:)];
576             [self setupVarMenuItem:_deinterlace_mode target: (vlc_object_t *)p_vout
577                                      var:"deinterlace-mode" selector: @selector(toggleVar:)];
579             vlc_object_release(p_vout);
581             [self refreshVoutDeviceMenu:nil];
582         }
583         [_postprocessing setEnabled:YES];
584         vlc_object_release(p_input);
585     } else {
586         [_postprocessing setEnabled:NO];
587     }
590 - (void)refreshVoutDeviceMenu:(NSNotification *)notification
592     NSUInteger count = (NSUInteger) [_screenMenu numberOfItems];
593     NSMenu *submenu = _screenMenu;
594     if (count > 0)
595         [submenu removeAllItems];
597     NSArray *screens = [NSScreen screens];
598     NSMenuItem *mitem;
599     count = [screens count];
600     [_screen setEnabled: YES];
601     [submenu addItemWithTitle: _NS("Default") action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
602     mitem = [submenu itemAtIndex: 0];
603     [mitem setTag: 0];
604     [mitem setEnabled: YES];
605     [mitem setTarget: self];
606     NSRect s_rect;
607     for (NSUInteger i = 0; i < count; i++) {
608         s_rect = [[screens objectAtIndex:i] frame];
609         [submenu addItemWithTitle: [NSString stringWithFormat: @"%@ %li (%ix%i)", _NS("Screen"), i+1,
610                                       (int)s_rect.size.width, (int)s_rect.size.height] action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
611         mitem = [submenu itemAtIndex:i+1];
612         [mitem setTag: (int)[[screens objectAtIndex:i] displayID]];
613         [mitem setEnabled: YES];
614         [mitem setTarget: self];
615     }
616     [[submenu itemWithTag: var_InheritInteger(getIntf(), "macosx-vdev")] setState: NSOnState];
619 - (void)setSubmenusEnabled:(BOOL)b_enabled
621     [_program setEnabled: b_enabled];
622     [_title setEnabled: b_enabled];
623     [_chapter setEnabled: b_enabled];
624     [_audiotrack setEnabled: b_enabled];
625     [_visual setEnabled: b_enabled];
626     [_videotrack setEnabled: b_enabled];
627     [_subtitle_track setEnabled: b_enabled];
628     [_channels setEnabled: b_enabled];
629     [_deinterlace setEnabled: b_enabled];
630     [_deinterlace_mode setEnabled: b_enabled];
631     [_screen setEnabled: b_enabled];
632     [_aspect_ratio setEnabled: b_enabled];
633     [_crop setEnabled: b_enabled];
636 - (void)setSubtitleMenuEnabled:(BOOL)b_enabled
638     [_openSubtitleFile setEnabled: b_enabled];
639     if (b_enabled) {
640         [_subtitle_bgopacityLabel_gray setHidden: YES];
641         [_subtitle_bgopacityLabel setHidden: NO];
642     } else {
643         [_subtitle_bgopacityLabel_gray setHidden: NO];
644         [_subtitle_bgopacityLabel setHidden: YES];
645     }
646     [_subtitle_bgopacity_sld setEnabled: b_enabled];
647     [_teletext setEnabled: b_enabled];
650 - (void)setRateControlsEnabled:(BOOL)b_enabled
652     [_rate_sld setEnabled: b_enabled];
653     [_rate_sld setIntValue: [[VLCCoreInteraction sharedInstance] playbackRate]];
654     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
655     double speed =  pow(2, (double)i / 17);
656     [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
658     NSColor *color = b_enabled ? [NSColor controlTextColor] : [NSColor disabledControlTextColor];
660     [_rateLabel setTextColor:color];
661     [_rate_slowerLabel setTextColor:color];
662     [_rate_normalLabel setTextColor:color];
663     [_rate_fasterLabel setTextColor:color];
664     [_rateTextField setTextColor:color];
666     [self setSubtitleMenuEnabled: b_enabled];
669 #pragma mark - View
671 - (IBAction)toggleEffectsButton:(id)sender
673     BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-effects-button");
674     config_PutInt("macosx-show-effects-button", b_value);
675     [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] toggleEffectsButton];
676     [_toggleEffectsButton setState: b_value];
679 - (IBAction)toggleJumpButtons:(id)sender
681     BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-playback-buttons");
682     config_PutInt("macosx-show-playback-buttons", b_value);
684     [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] toggleJumpButtons];
685     [[[VLCMain sharedInstance] voutProvider] updateWindowsUsingBlock:^(VLCVideoWindowCommon *window) {
686         [[window controlsBar] toggleForwardBackwardMode: b_value];
687     }];
689     [_toggleJumpButtons setState: b_value];
692 - (IBAction)togglePlaymodeButtons:(id)sender
694     BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-playmode-buttons");
695     config_PutInt("macosx-show-playmode-buttons", b_value);
696     [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] togglePlaymodeButtons];
697     [_togglePlaymodeButtons setState: b_value];
700 - (IBAction)toggleSidebar:(id)sender
702     [[[VLCMain sharedInstance] mainWindow] toggleLeftSubSplitView];
705 - (void)updateSidebarMenuItem:(BOOL)show;
707     [_toggleSidebar setState:show];
710 #pragma mark - Playback
712 - (IBAction)play:(id)sender
714     [[VLCCoreInteraction sharedInstance] playOrPause];
717 - (IBAction)stop:(id)sender
719     [[VLCCoreInteraction sharedInstance] stop];
722 - (IBAction)prev:(id)sender
724     [[VLCCoreInteraction sharedInstance] previous];
727 - (IBAction)next:(id)sender
729     [[VLCCoreInteraction sharedInstance] next];
732 - (IBAction)random:(id)sender
734     [[VLCCoreInteraction sharedInstance] shuffle];
737 - (IBAction)repeat:(id)sender
739     vlc_value_t val;
740     intf_thread_t *p_intf = getIntf();
741     playlist_t *p_playlist = pl_Get(p_intf);
743     var_Get(p_playlist, "repeat", &val);
744     if (! val.b_bool)
745         [[VLCCoreInteraction sharedInstance] repeatOne];
746     else
747         [[VLCCoreInteraction sharedInstance] repeatOff];
750 - (IBAction)loop:(id)sender
752     vlc_value_t val;
753     intf_thread_t *p_intf = getIntf();
754     playlist_t *p_playlist = pl_Get(p_intf);
756     var_Get(p_playlist, "loop", &val);
757     if (! val.b_bool)
758         [[VLCCoreInteraction sharedInstance] repeatAll];
759     else
760         [[VLCCoreInteraction sharedInstance] repeatOff];
763 - (IBAction)forward:(id)sender
765     [[VLCCoreInteraction sharedInstance] forward];
768 - (IBAction)backward:(id)sender
770     [[VLCCoreInteraction sharedInstance] backward];
773 - (IBAction)volumeUp:(id)sender
775     [[VLCCoreInteraction sharedInstance] volumeUp];
778 - (IBAction)volumeDown:(id)sender
780     [[VLCCoreInteraction sharedInstance] volumeDown];
783 - (IBAction)mute:(id)sender
785     [[VLCCoreInteraction sharedInstance] toggleMute];
788 - (void)lockVideosAspectRatio:(id)sender
790     [[VLCCoreInteraction sharedInstance] setAspectRatioIsLocked: ![sender state]];
791     [sender setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
794 - (IBAction)quitAfterPlayback:(id)sender
796     playlist_t *p_playlist = pl_Get(getIntf());
797     bool b_value = !var_CreateGetBool(p_playlist, "play-and-exit");
798     var_SetBool(p_playlist, "play-and-exit", b_value);
799     config_PutInt("play-and-exit", b_value);
802 - (IBAction)toggleRecord:(id)sender
804     [[VLCCoreInteraction sharedInstance] toggleRecord];
807 - (void)updateRecordState:(BOOL)b_value
809     [_record setState:b_value];
812 - (IBAction)setPlaybackRate:(id)sender
814     [[VLCCoreInteraction sharedInstance] setPlaybackRate: [_rate_sld intValue]];
815     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
816     double speed =  pow(2, (double)i / 17);
817     [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
820 - (void)updatePlaybackRate
822     int i = [[VLCCoreInteraction sharedInstance] playbackRate];
823     double speed =  pow(2, (double)i / 17);
824     [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
825     [_rate_sld setIntValue: i];
828 - (IBAction)toggleAtoBloop:(id)sender
830     [[VLCCoreInteraction sharedInstance] setAtoB];
833 - (IBAction)goToSpecificTime:(id)sender
835     input_thread_t *p_input = pl_CurrentInput(getIntf());
836     if (p_input) {
837         /* we can obviously only do that if an input is available */
838         vlc_tick_t length = var_GetInteger(p_input, "length");
839         [_timeSelectionPanel setMaxValue:(int)SEC_FROM_VLC_TICK(length)];
840         vlc_tick_t pos = var_GetInteger(p_input, "time");
841         [_timeSelectionPanel setJumpTimeValue: (int)SEC_FROM_VLC_TICK(pos)];
842         [_timeSelectionPanel runModalForWindow:[NSApp mainWindow]
843                              completionHandler:^(NSInteger returnCode, int64_t returnTime) {
845             if (returnCode != NSModalResponseOK)
846                 return;
848             input_thread_t *p_input = pl_CurrentInput(getIntf());
849             if (p_input) {
850                 input_SetTime(p_input, vlc_tick_from_sec(returnTime),
851                               var_GetBool(p_input, "input-fast-seek"));
852                 vlc_object_release(p_input);
853             }
854         }];
856         vlc_object_release(p_input);
857     }
860 - (IBAction)selectRenderer:(id)sender
862     [_rendererMenuController selectRenderer:sender];
865 #pragma mark - audio menu
867 - (void)refreshAudioDeviceList
869     char **ids, **names;
870     char *currentDevice;
872     [_audioDeviceMenu removeAllItems];
874     audio_output_t *p_aout = getAout();
875     if (!p_aout)
876         return;
878     int n = aout_DevicesList(p_aout, &ids, &names);
879     if (n == -1) {
880         vlc_object_release(p_aout);
881         return;
882     }
884     currentDevice = aout_DeviceGet(p_aout);
885     NSMenuItem *_tmp;
887     for (NSUInteger x = 0; x < n; x++) {
888         _tmp = [_audioDeviceMenu addItemWithTitle:toNSStr(names[x]) action:@selector(toggleAudioDevice:) keyEquivalent:@""];
889         [_tmp setTarget:self];
890         [_tmp setTag:[[NSString stringWithFormat:@"%s", ids[x]] intValue]];
891     }
892     vlc_object_release(p_aout);
894     [[_audioDeviceMenu itemWithTag:[[NSString stringWithFormat:@"%s", currentDevice] intValue]] setState:NSOnState];
896     free(currentDevice);
898     for (NSUInteger x = 0; x < n; x++) {
899         free(ids[x]);
900         free(names[x]);
901     }
902     free(ids);
903     free(names);
905     [_audioDeviceMenu setAutoenablesItems:YES];
906     [_audioDevice setEnabled:YES];
909 - (void)toggleAudioDevice:(id)sender
911     audio_output_t *p_aout = getAout();
912     if (!p_aout)
913         return;
915     int returnValue = 0;
917     if ([sender tag] > 0)
918         returnValue = aout_DeviceSet(p_aout, [[NSString stringWithFormat:@"%li", [sender tag]] UTF8String]);
919     else
920         returnValue = aout_DeviceSet(p_aout, NULL);
922     if (returnValue != 0)
923         msg_Warn(getIntf(), "failed to set audio device %li", [sender tag]);
925     vlc_object_release(p_aout);
926     [self refreshAudioDeviceList];
929 #pragma mark - video menu
931 - (IBAction)toggleFullscreen:(id)sender
933     [[VLCCoreInteraction sharedInstance] toggleFullscreen];
936 - (IBAction)resizeVideoWindow:(id)sender
938     input_thread_t *p_input = pl_CurrentInput(getIntf());
939     if (p_input) {
940         vout_thread_t *p_vout = getVoutForActiveWindow();
941         if (p_vout) {
942             if (sender == _half_window)
943                 var_SetFloat(p_vout, "zoom", 0.5);
944             else if (sender == _normal_window)
945                 var_SetFloat(p_vout, "zoom", 1.0);
946             else if (sender == _double_window)
947                 var_SetFloat(p_vout, "zoom", 2.0);
948             else
949             {
950                 [[NSApp keyWindow] performZoom:sender];
951             }
952             vlc_object_release(p_vout);
953         }
954         vlc_object_release(p_input);
955     }
958 - (IBAction)floatOnTop:(id)sender
960     input_thread_t *p_input = pl_CurrentInput(getIntf());
961     if (p_input) {
962         vout_thread_t *p_vout = getVoutForActiveWindow();
963         if (p_vout) {
964             BOOL b_fs = var_ToggleBool(p_vout, "video-on-top");
965             var_SetBool(pl_Get(getIntf()), "video-on-top", b_fs);
967             vlc_object_release(p_vout);
968         }
969         vlc_object_release(p_input);
970     }
973 - (IBAction)createVideoSnapshot:(id)sender
975     input_thread_t *p_input = pl_CurrentInput(getIntf());
976     if (p_input) {
977         vout_thread_t *p_vout = getVoutForActiveWindow();
978         if (p_vout) {
979             var_TriggerCallback(p_vout, "video-snapshot");
980             vlc_object_release(p_vout);
981         }
982         vlc_object_release(p_input);
983     }
986 - (void)_disablePostProcessing
988     [[VLCCoreInteraction sharedInstance] setVideoFilter:"postproc" on:false];
991 - (void)_enablePostProcessing
993     [[VLCCoreInteraction sharedInstance] setVideoFilter:"postproc" on:true];
996 - (void)togglePostProcessing:(id)sender
998     NSInteger count = [_postprocessingMenu numberOfItems];
999     for (NSUInteger x = 0; x < count; x++)
1000         [[_postprocessingMenu itemAtIndex:x] setState:NSOffState];
1002     if ([sender tag] == -1) {
1003         [self _disablePostProcessing];
1004         [sender setState:NSOnState];
1005     } else {
1006         [self _enablePostProcessing];
1007         [sender setState:NSOnState];
1009         [[VLCCoreInteraction sharedInstance] setVideoFilterProperty:"postproc-q" forFilter:"postproc" withValue:(vlc_value_t){ .i_int = [sender tag] }];
1010     }
1013 - (void)toggleFullscreenDevice:(id)sender
1015     config_PutInt("macosx-vdev", [sender tag]);
1016     [self refreshVoutDeviceMenu: nil];
1019 #pragma mark - Subtitles Menu
1021 - (IBAction)addSubtitleFile:(id)sender
1023     NSInteger i_returnValue = 0;
1024     input_thread_t *p_input = pl_CurrentInput(getIntf());
1025     if (!p_input)
1026         return;
1028     input_item_t *p_item = input_GetItem(p_input);
1029     if (!p_item) {
1030         vlc_object_release(p_input);
1031         return;
1032     }
1034     char *path = input_item_GetURI(p_item);
1036     if (!path)
1037         path = strdup("");
1039     NSOpenPanel *openPanel = [NSOpenPanel openPanel];
1040     [openPanel setCanChooseFiles: YES];
1041     [openPanel setCanChooseDirectories: NO];
1042     [openPanel setAllowsMultipleSelection: YES];
1044     [openPanel setAllowedFileTypes: [NSArray arrayWithObjects:@"cdg",@"idx",@"srt",@"sub",@"utf",@"ass",@"ssa",@"aqt",@"jss",@"psb",@"rt",@"smi",@"txt",@"smil",@"stl",@"usf",@"dks",@"pjs",@"mpl2",@"mks",@"vtt",@"ttml",@"dfxp",nil]];
1046     NSURL *url = [NSURL URLWithString:[toNSStr(path) stringByExpandingTildeInPath]];
1047     url = [url URLByDeletingLastPathComponent];
1048     [openPanel setDirectoryURL: url];
1049     free(path);
1050     vlc_object_release(p_input);
1052     i_returnValue = [openPanel runModal];
1054     if (i_returnValue == NSModalResponseOK)
1055         [[VLCCoreInteraction sharedInstance] addSubtitlesToCurrentInput:[openPanel URLs]];
1058 - (void)switchSubtitleSize:(id)sender
1060     NSInteger intValue = [sender tag];
1061     var_SetInteger(pl_Get(getIntf()), "sub-text-scale", intValue);
1065 - (void)switchSubtitleOption:(id)sender
1067     NSInteger intValue = [sender tag];
1068     NSString *representedObject = [sender representedObject];
1070     var_SetInteger(pl_Get(getIntf()), [representedObject UTF8String], intValue);
1072     NSMenu *menu = [sender menu];
1073     NSUInteger count = (NSUInteger) [menu numberOfItems];
1074     for (NSUInteger x = 0; x < count; x++)
1075         [[menu itemAtIndex:x] setState:NSOffState];
1076     [[menu itemWithTag:intValue] setState:NSOnState];
1079 - (IBAction)switchSubtitleBackgroundOpacity:(id)sender
1081     var_SetInteger(pl_Get(getIntf()), "freetype-background-opacity", [sender intValue]);
1084 - (IBAction)telxTransparent:(id)sender
1086     vlc_object_t *p_vbi;
1087     p_vbi = (vlc_object_t *) vlc_object_find_name(pl_Get(getIntf()), "zvbi");
1088     if (p_vbi) {
1089         var_SetBool(p_vbi, "vbi-opaque", [sender state]);
1090         [sender setState: ![sender state]];
1091         vlc_object_release(p_vbi);
1092     }
1095 - (IBAction)telxNavLink:(id)sender
1097     vlc_object_t *p_vbi;
1098     int i_page = 0;
1100     if ([[sender title] isEqualToString: _NS("Index")])
1101         i_page = 'i' << 16;
1102     else if ([[sender title] isEqualToString: _NS("Red")])
1103         i_page = 'r' << 16;
1104     else if ([[sender title] isEqualToString: _NS("Green")])
1105         i_page = 'g' << 16;
1106     else if ([[sender title] isEqualToString: _NS("Yellow")])
1107         i_page = 'y' << 16;
1108     else if ([[sender title] isEqualToString: _NS("Blue")])
1109         i_page = 'b' << 16;
1110     if (i_page == 0) return;
1112     p_vbi = (vlc_object_t *) vlc_object_find_name(pl_Get(getIntf()), "zvbi");
1113     if (p_vbi) {
1114         var_SetInteger(p_vbi, "vbi-page", i_page);
1115         vlc_object_release(p_vbi);
1116     }
1119 #pragma mark - Panels
1121 - (IBAction)intfOpenFile:(id)sender
1123     [[[VLCMain sharedInstance] open] openFileWithAction:^(NSArray *files) {
1124         [[[VLCMain sharedInstance] playlist] addPlaylistItems:files];
1125     }];
1128 - (IBAction)intfOpenFileGeneric:(id)sender
1130     [[[VLCMain sharedInstance] open] openFileGeneric];
1133 - (IBAction)intfOpenDisc:(id)sender
1135     [[[VLCMain sharedInstance] open] openDisc];
1138 - (IBAction)intfOpenNet:(id)sender
1140     [[[VLCMain sharedInstance] open] openNet];
1143 - (IBAction)intfOpenCapture:(id)sender
1145     [[[VLCMain sharedInstance] open] openCapture];
1148 - (IBAction)savePlaylist:(id)sender
1150     playlist_t *p_playlist = pl_Get(getIntf());
1152     NSSavePanel *savePanel = [NSSavePanel savePanel];
1153     NSString * name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
1155     static dispatch_once_t once;
1156     dispatch_once(&once, ^{
1157         [[NSBundle mainBundle] loadNibNamed:@"PlaylistAccessoryView" owner:self topLevelObjects:nil];
1158     });
1160     [_playlistSaveAccessoryText setStringValue: _NS("File Format:")];
1161     [[_playlistSaveAccessoryPopup itemAtIndex:0] setTitle: _NS("Extended M3U")];
1162     [[_playlistSaveAccessoryPopup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
1163     [[_playlistSaveAccessoryPopup itemAtIndex:2] setTitle: _NS("HTML playlist")];
1165     [savePanel setTitle: _NS("Save Playlist")];
1166     [savePanel setPrompt: _NS("Save")];
1167     [savePanel setAccessoryView: _playlistSaveAccessoryView];
1168     [savePanel setNameFieldStringValue: name];
1170     if ([savePanel runModal] == NSFileHandlingPanelOKButton) {
1171         NSString *filename = [[savePanel URL] path];
1173         if ([_playlistSaveAccessoryPopup indexOfSelectedItem] == 0) {
1174             NSString *actualFilename;
1175             NSRange range;
1176             range.location = [filename length] - [@".m3u" length];
1177             range.length = [@".m3u" length];
1179             if ([filename compare:@".m3u" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1180                 actualFilename = [NSString stringWithFormat: @"%@.m3u", filename];
1181             else
1182                 actualFilename = filename;
1184             playlist_Export(p_playlist,
1185                             [actualFilename fileSystemRepresentation],
1186                             true, "export-m3u");
1187         } else if ([_playlistSaveAccessoryPopup indexOfSelectedItem] == 1) {
1188             NSString *actualFilename;
1189             NSRange range;
1190             range.location = [filename length] - [@".xspf" length];
1191             range.length = [@".xspf" length];
1193             if ([filename compare:@".xspf" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1194                 actualFilename = [NSString stringWithFormat: @"%@.xspf", filename];
1195             else
1196                 actualFilename = filename;
1198             playlist_Export(p_playlist,
1199                             [actualFilename fileSystemRepresentation],
1200                             true, "export-xspf");
1201         } else {
1202             NSString *actualFilename;
1203             NSRange range;
1204             range.location = [filename length] - [@".html" length];
1205             range.length = [@".html" length];
1207             if ([filename compare:@".html" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1208                 actualFilename = [NSString stringWithFormat: @"%@.html", filename];
1209             else
1210                 actualFilename = filename;
1212             playlist_Export(p_playlist,
1213                             [actualFilename fileSystemRepresentation],
1214                             true, "export-html");
1215         }
1216     }
1219 - (IBAction)showConvertAndSave:(id)sender
1221     [[[VLCMain sharedInstance] convertAndSaveWindow] showWindow:self];
1224 - (IBAction)showVideoEffects:(id)sender
1226     [[[VLCMain sharedInstance] videoEffectsPanel] toggleWindow:sender];
1229 - (IBAction)showTrackSynchronization:(id)sender
1231     [[[VLCMain sharedInstance] trackSyncPanel] toggleWindow:sender];
1234 - (IBAction)showAudioEffects:(id)sender
1236     [[[VLCMain sharedInstance] audioEffectsPanel] toggleWindow:sender];
1239 - (IBAction)showBookmarks:(id)sender
1241     [[[VLCMain sharedInstance] bookmarks] toggleWindow:sender];
1244 - (IBAction)showPreferences:(id)sender
1246     NSInteger i_level = [[[VLCMain sharedInstance] voutProvider] currentStatusWindowLevel];
1247     [[[VLCMain sharedInstance] simplePreferences] showSimplePrefsWithLevel:i_level];
1250 - (IBAction)openAddonManager:(id)sender
1252     if (!_addonsController)
1253         _addonsController = [[VLCAddonsWindowController alloc] init];
1255     [_addonsController showWindow:self];
1258 - (IBAction)showErrorsAndWarnings:(id)sender
1260     [[[[VLCMain sharedInstance] coreDialogProvider] errorPanel] showWindow:self];
1263 - (IBAction)showMessagesPanel:(id)showMessagesPanel
1265     [[[VLCMain sharedInstance] debugMsgPanel] showWindow:self];
1268 - (IBAction)showMainWindow:(id)sender
1270     [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:sender];
1273 - (IBAction)showPlaylist:(id)sender
1275     [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserMenuEvent];
1278 #pragma mark - Help and Docs
1280 - (IBAction)showAbout:(id)sender
1282     if (!_aboutWindowController)
1283         _aboutWindowController = [[VLCAboutWindowController alloc] init];
1285     [_aboutWindowController showAbout];
1288 - (IBAction)showLicense:(id)sender
1290     if (!_aboutWindowController)
1291         _aboutWindowController = [[VLCAboutWindowController alloc] init];
1293     [_aboutWindowController showGPL];
1296 - (IBAction)showHelp:(id)sender
1298     if (!_helpWindowController)
1299         _helpWindowController = [[VLCHelpWindowController alloc] init];
1301     [_helpWindowController showHelp];
1304 - (IBAction)openDocumentation:(id)sender
1306     NSURL *url = [NSURL URLWithString: @"http://www.videolan.org/doc/"];
1308     [[NSWorkspace sharedWorkspace] openURL: url];
1311 - (IBAction)openWebsite:(id)sender
1313     NSURL *url = [NSURL URLWithString: @"http://www.videolan.org/"];
1315     [[NSWorkspace sharedWorkspace] openURL: url];
1318 - (IBAction)openForum:(id)sender
1320     NSURL *url = [NSURL URLWithString: @"http://forum.videolan.org/"];
1322     [[NSWorkspace sharedWorkspace] openURL: url];
1325 - (IBAction)openDonate:(id)sender
1327     NSURL *url = [NSURL URLWithString: @"http://www.videolan.org/contribute.html#paypal"];
1329     [[NSWorkspace sharedWorkspace] openURL: url];
1332 - (IBAction)showInformationPanel:(id)sender
1334     [[[VLCMain sharedInstance] currentMediaInfoPanel] toggleWindow:sender];
1337 #pragma mark - convinience stuff for other objects
1339 - (void)setPlay
1341     [_play setTitle: _NS("Play")];
1342     [_dockMenuplay setTitle: _NS("Play")];
1343     [_voutMenuplay setTitle: _NS("Play")];
1346 - (void)setPause
1348     [_play setTitle: _NS("Pause")];
1349     [_dockMenuplay setTitle: _NS("Pause")];
1350     [_voutMenuplay setTitle: _NS("Pause")];
1353 - (void)setRepeatOne
1355     [_repeat setState: NSOnState];
1356     [_loop setState: NSOffState];
1359 - (void)setRepeatAll
1361     [_repeat setState: NSOffState];
1362     [_loop setState: NSOnState];
1365 - (void)setRepeatOff
1367     [_repeat setState: NSOffState];
1368     [_loop setState: NSOffState];
1371 - (void)setShuffle
1373     bool b_value;
1374     playlist_t *p_playlist = pl_Get(getIntf());
1375     b_value = var_GetBool(p_playlist, "random");
1377     [_random setState: b_value];
1380 #pragma mark - Dynamic menu creation and validation
1382 - (void)setupVarMenuItem:(NSMenuItem *)mi
1383                   target:(vlc_object_t *)p_object
1384                      var:(const char *)psz_variable
1385                 selector:(SEL)pf_callback
1387     vlc_value_t val;
1388     char *text;
1389     int i_type = var_Type(p_object, psz_variable);
1391     switch(i_type & VLC_VAR_TYPE) {
1392         case VLC_VAR_VOID:
1393         case VLC_VAR_BOOL:
1394         case VLC_VAR_STRING:
1395         case VLC_VAR_INTEGER:
1396             break;
1397         default:
1398             /* Variable doesn't exist or isn't handled */
1399             msg_Warn(p_object, "variable %s doesn't exist or isn't handled", psz_variable);
1400             return;
1401     }
1403     /* Get the descriptive name of the variable */
1404     var_Change(p_object, psz_variable, VLC_VAR_GETTEXT, &text);
1405     [mi setTitle: _NS(text ? text : psz_variable)];
1407     if (i_type & VLC_VAR_HASCHOICE) {
1408         NSMenu *menu = [mi submenu];
1410         [self setupVarMenu:menu forMenuItem:mi target:p_object
1411                        var:psz_variable selector:pf_callback];
1413         free(text);
1414         return;
1415     }
1417     if (var_Get(p_object, psz_variable, &val) < 0)
1418         return;
1420     VLCAutoGeneratedMenuContent *data;
1421     switch(i_type & VLC_VAR_TYPE) {
1422         case VLC_VAR_VOID:
1423             data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1424                                                                       andValue: val ofType: i_type];
1425             [mi setRepresentedObject:data];
1426             break;
1428         case VLC_VAR_BOOL:
1429             data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1430                                                                       andValue: val ofType: i_type];
1431             [mi setRepresentedObject:data];
1432             if (!(i_type & VLC_VAR_ISCOMMAND))
1433                 [mi setState: val.b_bool ? TRUE : FALSE ];
1434             break;
1436         default:
1437             break;
1438     }
1440     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1441     free(text);
1445 - (void)setupVarMenu:(NSMenu *)menu
1446          forMenuItem: (NSMenuItem *)parent
1447               target:(vlc_object_t *)p_object
1448                  var:(const char *)psz_variable
1449             selector:(SEL)pf_callback
1451     vlc_value_t val;
1452     vlc_value_t *val_list;
1453     char **text_list;
1454     size_t count, i;
1455     int i_type;
1457     /* remove previous items */
1458     [menu removeAllItems];
1460     /* we disable everything here, and enable it again when needed, below */
1461     [parent setEnabled:NO];
1463     /* Aspect Ratio */
1464     if ([[parent title] isEqualToString: _NS("Aspect ratio")] == YES) {
1465         NSMenuItem *lmi_tmp2;
1466         lmi_tmp2 = [menu addItemWithTitle: _NS("Lock Aspect Ratio") action: @selector(lockVideosAspectRatio:) keyEquivalent: @""];
1467         [lmi_tmp2 setTarget: self];
1468         [lmi_tmp2 setEnabled: YES];
1469         [lmi_tmp2 setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
1470         [parent setEnabled: YES];
1471         [menu addItem: [NSMenuItem separatorItem]];
1472     }
1474     /* Check the type of the object variable */
1475     i_type = var_Type(p_object, psz_variable);
1477     /* Make sure we want to display the variable */
1478     if (i_type & VLC_VAR_HASCHOICE) {
1479         size_t count;
1481         var_Change(p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &count);
1482         if (count <= 1)
1483             return;
1484     }
1485     else
1486         return;
1488     switch(i_type & VLC_VAR_TYPE) {
1489         case VLC_VAR_VOID:
1490         case VLC_VAR_BOOL:
1491         case VLC_VAR_STRING:
1492         case VLC_VAR_INTEGER:
1493             break;
1494         default:
1495             /* Variable doesn't exist or isn't handled */
1496             return;
1497     }
1499     if (var_Get(p_object, psz_variable, &val) < 0) {
1500         return;
1501     }
1503     if (var_Change(p_object, psz_variable, VLC_VAR_GETCHOICES,
1504                    &count, &val_list, &text_list) < 0) {
1505         if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1506         return;
1507     }
1509     /* make (un)sensitive */
1510     [parent setEnabled: (count > 1)];
1512     for (i = 0; i < count; i++) {
1513         NSMenuItem *lmi;
1514         NSString *title = @"";
1515         VLCAutoGeneratedMenuContent *data;
1517         switch(i_type & VLC_VAR_TYPE) {
1518             case VLC_VAR_STRING:
1520                 title = _NS(text_list[i] ? text_list[i] : val_list[i].psz_string);
1522                 lmi = [menu addItemWithTitle: title action: pf_callback keyEquivalent: @""];
1523                 data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1524                                                                           andValue: val_list[i] ofType: i_type];
1525                 [lmi setRepresentedObject:data];
1526                 [lmi setTarget: self];
1528                 if (!strcmp(val.psz_string, val_list[i].psz_string) && !(i_type & VLC_VAR_ISCOMMAND))
1529                     [lmi setState: TRUE ];
1531                 free(text_list[i]);
1532                 free(val_list[i].psz_string);
1533                 break;
1535             case VLC_VAR_INTEGER:
1537                 title = text_list[i] ?
1538                 _NS(text_list[i]) : [NSString stringWithFormat: @"%"PRId64, val_list[i].i_int];
1540                 lmi = [menu addItemWithTitle: title action: pf_callback keyEquivalent: @""];
1541                 data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1542                                                                           andValue: val_list[i] ofType: i_type];
1543                 [lmi setRepresentedObject:data];
1544                 [lmi setTarget: self];
1546                 if (val_list[i].i_int == val.i_int && !(i_type & VLC_VAR_ISCOMMAND))
1547                     [lmi setState: TRUE ];
1549                 free(text_list[i]);
1550                 break;
1552             default:
1553                 break;
1554         }
1555     }
1557     /* clean up everything */
1558     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1559     free(text_list);
1560     free(val_list);
1563 - (void)toggleVar:(id)sender
1565     NSMenuItem *mi = (NSMenuItem *)sender;
1566     VLCAutoGeneratedMenuContent *data = [mi representedObject];
1567     [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
1568                              toTarget: self withObject: data];
1570     return;
1573 - (int)toggleVarThread: (id)data
1575     @autoreleasepool {
1576         vlc_object_t *p_object;
1578         assert([data isKindOfClass:[VLCAutoGeneratedMenuContent class]]);
1579         VLCAutoGeneratedMenuContent *menuContent = (VLCAutoGeneratedMenuContent *)data;
1581         p_object = [menuContent vlcObject];
1583         if (p_object != NULL) {
1584             var_Set(p_object, [menuContent name], [menuContent value]);
1585             vlc_object_release(p_object);
1586             return true;
1587         }
1588         return VLC_EGENERIC;
1589     }
1592 #pragma mark - menu delegation
1594 - (void)menuWillOpen:(NSMenu *)menu
1596     [_cancelRendererDiscoveryTimer invalidate];
1597     [_rendererMenuController startRendererDiscoveries];
1600 - (void)menuDidClose:(NSMenu *)menu
1602     _cancelRendererDiscoveryTimer = [NSTimer scheduledTimerWithTimeInterval:20.
1603                                                                      target:self
1604                                                                    selector:@selector(cancelRendererDiscovery)
1605                                                                    userInfo:nil
1606                                                                     repeats:NO];
1609 - (void)cancelRendererDiscovery
1611     [_rendererMenuController stopRendererDiscoveries];
1614 @end
1616 @implementation VLCMainMenu (NSMenuValidation)
1618 - (BOOL)validateMenuItem:(NSMenuItem *)mi
1620     BOOL enabled = YES;
1621     vlc_value_t val;
1622     playlist_t *p_playlist = pl_Get(getIntf());
1623     input_thread_t *p_input = playlist_CurrentInput(p_playlist);
1625     if (mi == _stop || mi == _voutMenustop || mi == _dockMenustop) {
1626         if (!p_input)
1627             enabled = NO;
1628         [self setupMenus]; /* Make sure input menu is up to date */
1629     } else if (mi == _previous          ||
1630                mi == _voutMenuprev      ||
1631                mi == _dockMenuprevious  ||
1632                mi == _next              ||
1633                mi == _voutMenunext      ||
1634                mi == _dockMenunext
1635                ) {
1636         PL_LOCK;
1637         enabled = playlist_CurrentSize(p_playlist) > 1;
1638         PL_UNLOCK;
1639     } else if (mi == _record) {
1640         enabled = NO;
1641         if (p_input)
1642             enabled = var_GetBool(p_input, "can-record");
1643     } else if (mi == _random) {
1644         int i_state;
1645         var_Get(p_playlist, "random", &val);
1646         i_state = val.b_bool ? NSOnState : NSOffState;
1647         [mi setState: i_state];
1648     } else if (mi == _repeat) {
1649         int i_state;
1650         var_Get(p_playlist, "repeat", &val);
1651         i_state = val.b_bool ? NSOnState : NSOffState;
1652         [mi setState: i_state];
1653     } else if (mi == _loop) {
1654         int i_state;
1655         var_Get(p_playlist, "loop", &val);
1656         i_state = val.b_bool ? NSOnState : NSOffState;
1657         [mi setState: i_state];
1658     } else if (mi == _quitAfterPB) {
1659         int i_state;
1660         bool b_value = var_InheritBool(p_playlist, "play-and-exit");
1661         i_state = b_value ? NSOnState : NSOffState;
1662         [mi setState: i_state];
1663     } else if (mi == _fwd || mi == _bwd || mi == _jumpToTime) {
1664         if (p_input != NULL) {
1665             var_Get(p_input, "can-seek", &val);
1666             enabled = val.b_bool;
1667         } else {
1668             enabled = NO;
1669         }
1670     } else if (mi == _mute || mi == _dockMenumute || mi == _voutMenumute) {
1671         [mi setState: [[VLCCoreInteraction sharedInstance] mute] ? NSOnState : NSOffState];
1672         [self setupMenus]; /* Make sure audio menu is up to date */
1673         [self refreshAudioDeviceList];
1674     } else if (mi == _half_window           ||
1675                mi == _normal_window         ||
1676                mi == _double_window         ||
1677                mi == _fittoscreen           ||
1678                mi == _snapshot              ||
1679                mi == _voutMenusnapshot      ||
1680                mi == _fullscreenItem        ||
1681                mi == _voutMenufullscreen    ||
1682                mi == _floatontop
1683                ) {
1684         enabled = NO;
1686         if (p_input != NULL) {
1687             vout_thread_t *p_vout = getVoutForActiveWindow();
1688             if (p_vout != NULL) {
1689                 if (mi == _floatontop)
1690                     [mi setState: var_GetBool(p_vout, "video-on-top")];
1692                 if (mi == _fullscreenItem || mi == _voutMenufullscreen)
1693                     [mi setState: var_GetBool(p_vout, "fullscreen")];
1695                 enabled = YES;
1696                 vlc_object_release(p_vout);
1697             }
1698         }
1700         [self setupMenus]; /* Make sure video menu is up to date */
1702     } else if (mi == _openSubtitleFile) {
1703         enabled = [mi isEnabled];
1704         [self setupMenus]; /* Make sure subtitles menu is up to date */
1705     } else {
1706         NSMenuItem *_parent = [mi parentItem];
1707         if (_parent == _subtitle_size || mi == _subtitle_size           ||
1708             _parent == _subtitle_textcolor || mi == _subtitle_textcolor ||
1709             _parent == _subtitle_bgcolor || mi == _subtitle_bgcolor     ||
1710             _parent == _subtitle_bgopacity || mi == _subtitle_bgopacity ||
1711             _parent == _subtitle_outlinethickness || mi == _subtitle_outlinethickness ||
1712             _parent == _teletext || mi == _teletext
1713             ) {
1714             enabled = _openSubtitleFile.isEnabled;
1715         }
1716     }
1718     if (p_input)
1719         vlc_object_release(p_input);
1721     return enabled;
1724 @end
1727 /*****************************************************************************
1728  *VLCAutoGeneratedMenuContent implementation
1729  *****************************************************************************
1730  *Object connected to a playlistitem which remembers the data belonging to
1731  *the variable of the autogenerated menu
1732  *****************************************************************************/
1734 @interface VLCAutoGeneratedMenuContent ()
1736     char *psz_name;
1737     vlc_object_t *vlc_object;
1738     vlc_value_t value;
1739     int i_type;
1741 @end
1742 @implementation VLCAutoGeneratedMenuContent
1744 -(id) initWithVariableName:(const char *)name ofObject:(vlc_object_t *)object
1745                   andValue:(vlc_value_t)val ofType:(int)type
1747     self = [super init];
1749     if (self != nil) {
1750         vlc_object = vlc_object_hold(object);
1751         psz_name = strdup(name);
1752         i_type = type;
1753         value = val;
1754         if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1755             value.psz_string = strdup(val.psz_string);
1756     }
1758     return(self);
1761 - (void)dealloc
1763     if (vlc_object)
1764         vlc_object_release(vlc_object);
1765     if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1766         free(value.psz_string);
1767     free(psz_name);
1770 - (const char *)name
1772     return psz_name;
1775 - (vlc_value_t)value
1777     return value;
1780 - (vlc_object_t *)vlcObject
1782     return vlc_object_hold(vlc_object);
1785 - (int)type
1787     return i_type;
1790 @end