1/*****************************************************************************
2 *MainMenu.m: MacOS X interface module
3 *****************************************************************************
4 *Copyright (C) 2011-2018 Felix Paul Kühne
5 *$Id: 736dd27b492cc7dc5f11bb393a2f8c5445a7d4c7 $
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 "VLCMainMenu.h"
25#import "VLCMain.h"
26
27#import <vlc_common.h>
28#import <vlc_playlist.h>
29#import <vlc_input.h>
30
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"
54
55#ifdef HAVE_SPARKLE
56#import <Sparkle/Sparkle.h>
57#endif
58
59@interface VLCMainMenu() <NSMenuDelegate>
60{
61    VLCAboutWindowController *_aboutWindowController;
62    VLCHelpWindowController  *_helpWindowController;
63    VLCAddonsWindowController *_addonsController;
64    VLCRendererMenuController *_rendererMenuController;
65    NSTimer *_cancelRendererDiscoveryTimer;
66
67    NSMenu *_playlistTableColumnsContextMenu;
68
69    __strong VLCTimeSelectionPanelController *_timeSelectionPanel;
70}
71@end
72
73@implementation VLCMainMenu
74
75#pragma mark - Initialization
76
77- (void)dealloc
78{
79    msg_Dbg(getIntf(), "Deinitializing main menu");
80    [[NSNotificationCenter defaultCenter] removeObserver: self];
81
82    [self releaseRepresentedObjects:[NSApp mainMenu]];
83}
84
85- (void)awakeFromNib
86{
87    _timeSelectionPanel = [[VLCTimeSelectionPanelController alloc] init];
88
89    /* check whether the user runs OSX with a RTL language */
90    NSArray* languages = [NSLocale preferredLanguages];
91    NSString* preferredLanguage = [languages firstObject];
92
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    }
97
98    [self setRateControlsEnabled:NO];
99
100#ifdef HAVE_SPARKLE
101    [_checkForUpdate setAction:@selector(checkForUpdates:)];
102    [_checkForUpdate setTarget:[SUUpdater sharedUpdater]];
103#else
104    [_checkForUpdate setEnabled:NO];
105#endif
106
107    NSString* keyString;
108    vlc_value_t val;
109    VLCStringUtility *stringUtility = [VLCStringUtility sharedInstance];
110    char *key;
111
112    /* Get ExtensionsManager */
113    intf_thread_t *p_intf = getIntf();
114
115    [self initStrings];
116
117    key = config_GetPsz(p_intf, "key-quit");
118    keyString = [NSString stringWithFormat:@"%s", key];
119    [_quit setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
120    [_quit setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
121    FREENULL(key);
122
123    // do not assign play/pause key
124
125    key = config_GetPsz(p_intf, "key-stop");
126    keyString = [NSString stringWithFormat:@"%s", key];
127    [_stop setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
128    [_stop setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
129    FREENULL(key);
130
131    key = config_GetPsz(p_intf, "key-prev");
132    keyString = [NSString stringWithFormat:@"%s", key];
133    [_previous setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
134    [_previous setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
135    FREENULL(key);
136
137    key = config_GetPsz(p_intf, "key-next");
138    keyString = [NSString stringWithFormat:@"%s", key];
139    [_next setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
140    [_next setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
141    FREENULL(key);
142
143    key = config_GetPsz(p_intf, "key-jump+short");
144    keyString = [NSString stringWithFormat:@"%s", key];
145    [_fwd setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
146    [_fwd setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
147    FREENULL(key);
148
149    key = config_GetPsz(p_intf, "key-jump-short");
150    keyString = [NSString stringWithFormat:@"%s", key];
151    [_bwd setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
152    [_bwd setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
153    FREENULL(key);
154
155    key = config_GetPsz(p_intf, "key-vol-up");
156    keyString = [NSString stringWithFormat:@"%s", key];
157    [_vol_up setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
158    [_vol_up setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
159    FREENULL(key);
160
161    key = config_GetPsz(p_intf, "key-vol-down");
162    keyString = [NSString stringWithFormat:@"%s", key];
163    [_vol_down setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
164    [_vol_down setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
165    FREENULL(key);
166
167    key = config_GetPsz(p_intf, "key-vol-mute");
168    keyString = [NSString stringWithFormat:@"%s", key];
169    [_mute setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
170    [_mute setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
171    FREENULL(key);
172
173    key = config_GetPsz(p_intf, "key-toggle-fullscreen");
174    keyString = [NSString stringWithFormat:@"%s", key];
175    [_fullscreenItem setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
176    [_fullscreenItem setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
177    FREENULL(key);
178
179    key = config_GetPsz(p_intf, "key-snapshot");
180    keyString = [NSString stringWithFormat:@"%s", key];
181    [_snapshot setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
182    [_snapshot setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
183    FREENULL(key);
184
185    key = config_GetPsz(p_intf, "key-random");
186    keyString = [NSString stringWithFormat:@"%s", key];
187    [_random setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
188    [_random setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
189    FREENULL(key);
190
191    key = config_GetPsz(p_intf, "key-zoom-half");
192    keyString = [NSString stringWithFormat:@"%s", key];
193    [_half_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
194    [_half_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
195    FREENULL(key);
196
197    key = config_GetPsz(p_intf, "key-zoom-original");
198    keyString = [NSString stringWithFormat:@"%s", key];
199    [_normal_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
200    [_normal_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
201    FREENULL(key);
202
203    key = config_GetPsz(p_intf, "key-zoom-double");
204    keyString = [NSString stringWithFormat:@"%s", key];
205    [_double_window setKeyEquivalent: [stringUtility VLCKeyToString: keyString]];
206    [_double_window setKeyEquivalentModifierMask: [stringUtility VLCModifiersToCocoa:keyString]];
207    FREENULL(key);
208
209    [self setSubmenusEnabled: FALSE];
210
211    /* configure playback / controls menu */
212    self.controlsMenu.delegate = self;
213    [_rendererNoneItem setState:NSOnState];
214    _rendererMenuController = [[VLCRendererMenuController alloc] init];
215    _rendererMenuController.rendererNoneItem = _rendererNoneItem;
216    _rendererMenuController.rendererMenu = _rendererMenu;
217
218    [[NSNotificationCenter defaultCenter] addObserver: self
219                                             selector: @selector(refreshVoutDeviceMenu:)
220                                                 name: NSApplicationDidChangeScreenParametersNotification
221                                               object: nil];
222
223    [self setupVarMenuItem:_add_intf target: (vlc_object_t *)p_intf
224                             var:"intf-add" selector: @selector(toggleVar:)];
225
226    /* setup extensions menu */
227    /* Let the ExtensionsManager itself build the menu */
228    VLCExtensionsManager *extMgr = [[VLCMain sharedInstance] extensionsManager];
229    [extMgr buildMenu:_extensionsMenu];
230    [_extensions setEnabled: ([_extensionsMenu numberOfItems] > 0)];
231
232    // FIXME: Implement preference for autoloading extensions on mac
233    if (![extMgr isLoaded] && ![extMgr cannotLoad])
234        [extMgr loadExtensions];
235
236    /* setup post-proc menu */
237    NSUInteger count = (NSUInteger) [_postprocessingMenu numberOfItems];
238    if (count > 0)
239        [_postprocessingMenu removeAllItems];
240
241    NSMenuItem *mitem;
242    [_postprocessingMenu setAutoenablesItems: YES];
243    [_postprocessingMenu addItemWithTitle: _NS("Disable") action:@selector(togglePostProcessing:) keyEquivalent:@""];
244    mitem = [_postprocessingMenu itemAtIndex: 0];
245    [mitem setTag: -1];
246    [mitem setEnabled: YES];
247    [mitem setTarget: self];
248    for (NSUInteger x = 1; x < 7; x++) {
249        [_postprocessingMenu addItemWithTitle:[NSString stringWithFormat:_NS("Level %i"), x]
250                                               action:@selector(togglePostProcessing:)
251                                        keyEquivalent:@""];
252        mitem = [_postprocessingMenu itemAtIndex:x];
253        [mitem setEnabled:YES];
254        [mitem setTag:x];
255        [mitem setTarget:self];
256    }
257    char *psz_config = config_GetPsz(p_intf, "video-filter");
258    if (psz_config) {
259        if (!strstr(psz_config, "postproc"))
260            [[_postprocessingMenu itemAtIndex:0] setState:NSOnState];
261        else
262            [[_postprocessingMenu itemWithTag:config_GetInt(p_intf, "postproc-q")] setState:NSOnState];
263        free(psz_config);
264    } else
265        [[_postprocessingMenu itemAtIndex:0] setState:NSOnState];
266    [_postprocessing setEnabled: NO];
267
268    [self refreshAudioDeviceList];
269
270    /* setup subtitles menu */
271    // Persist those variables on the playlist
272    playlist_t *p_playlist = pl_Get(getIntf());
273    var_Create(p_playlist, "freetype-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
274    var_Create(p_playlist, "freetype-background-opacity", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
275    var_Create(p_playlist, "freetype-background-color", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
276    var_Create(p_playlist, "freetype-outline-thickness", VLC_VAR_INTEGER | VLC_VAR_DOINHERIT);
277
278    [self setupMenu: _subtitle_textcolorMenu withIntList:"freetype-color" andSelector:@selector(switchSubtitleOption:)];
279    [_subtitle_bgopacity_sld setIntValue: config_GetInt(VLC_OBJECT(p_intf), "freetype-background-opacity")];
280    [self setupMenu: _subtitle_bgcolorMenu withIntList:"freetype-background-color" andSelector:@selector(switchSubtitleOption:)];
281    [self setupMenu: _subtitle_outlinethicknessMenu withIntList:"freetype-outline-thickness" andSelector:@selector(switchSubtitleOption:)];
282
283    /* Build size menu based on different scale factors */
284    struct {
285        const char *const name;
286        int scaleValue;
287    } scaleValues[] = {
288        { N_("Smaller"), 50},
289        { N_("Small"),   75},
290        { N_("Normal"), 100},
291        { N_("Large"),  125},
292        { N_("Larger"), 150},
293        { NULL, 0 }
294    };
295
296    for(int i = 0; scaleValues[i].name; i++) {
297        NSMenuItem *menuItem = [_subtitle_sizeMenu addItemWithTitle: _NS(scaleValues[i].name) action:@selector(switchSubtitleSize:) keyEquivalent:@""];
298        [menuItem setTag:scaleValues[i].scaleValue];
299        [menuItem setTarget: self];
300    }
301}
302
303- (void)setupMenu: (NSMenu*)menu withIntList: (char *)psz_name andSelector:(SEL)selector
304{
305    module_config_t *p_item;
306
307    [menu removeAllItems];
308    p_item = config_FindConfig(psz_name);
309
310    if (!p_item) {
311        msg_Err(getIntf(), "couldn't create menu int list for item '%s' as it does not exist", psz_name);
312        return;
313    }
314
315    for (int i = 0; i < p_item->list_count; i++) {
316        NSMenuItem *mi;
317        if (p_item->list_text != NULL)
318            mi = [[NSMenuItem alloc] initWithTitle: _NS(p_item->list_text[i]) action:NULL keyEquivalent: @""];
319        else if (p_item->list.i[i])
320            mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%d", p_item->list.i[i]] action:NULL keyEquivalent: @""];
321        else {
322            msg_Err(getIntf(), "item %d of pref %s failed to be created", i, psz_name);
323            continue;
324        }
325
326        [mi setTarget:self];
327        [mi setAction:selector];
328        [mi setTag:p_item->list.i[i]];
329        [mi setRepresentedObject:toNSStr(psz_name)];
330        [menu addItem:mi];
331        if (p_item->value.i == p_item->list.i[i])
332            [mi setState:NSOnState];
333    }
334}
335
336- (void)initStrings
337{
338    /* main menu */
339    [_about setTitle: [_NS("About VLC media player") \
340                           stringByAppendingString: @"..."]];
341    [_checkForUpdate setTitle: _NS("Check for Update...")];
342    [_prefs setTitle: _NS("Preferences...")];
343    [_extensions setTitle: _NS("Extensions")];
344    [_extensionsMenu setTitle: _NS("Extensions")];
345    [_addonManager setTitle: _NS("Addons Manager")];
346    [_add_intf setTitle: _NS("Add Interface")];
347    [_add_intfMenu setTitle: _NS("Add Interface")];
348    [_services setTitle: _NS("Services")];
349    [_hide setTitle: _NS("Hide VLC")];
350    [_hide_others setTitle: _NS("Hide Others")];
351    [_show_all setTitle: _NS("Show All")];
352    [_quit setTitle: _NS("Quit VLC")];
353
354    [_fileMenu setTitle: _ANS("1:File")];
355    [_open_generic setTitle: _NS("Advanced Open File...")];
356    [_open_file setTitle: _NS("Open File...")];
357    [_open_disc setTitle: _NS("Open Disc...")];
358    [_open_net setTitle: _NS("Open Network...")];
359    [_open_capture setTitle: _NS("Open Capture Device...")];
360    [_open_recent setTitle: _NS("Open Recent")];
361    [_close_window setTitle: _NS("Close Window")];
362    [_convertandsave setTitle: _NS("Convert / Stream...")];
363    [_save_playlist setTitle: _NS("Save Playlist...")];
364    [_revealInFinder setTitle: _NS("Reveal in Finder")];
365
366    [_editMenu setTitle: _NS("Edit")];
367    [_cutItem setTitle: _NS("Cut")];
368    [_mcopyItem setTitle: _NS("Copy")];
369    [_pasteItem setTitle: _NS("Paste")];
370    [_clearItem setTitle: _NS("Delete")];
371    [_select_all setTitle: _NS("Select All")];
372    [_findItem setTitle: _NS("Find")];
373
374    [_viewMenu setTitle: _NS("View")];
375    [_toggleJumpButtons setTitle: _NS("Show Previous & Next Buttons")];
376    [_toggleJumpButtons setState: var_InheritBool(getIntf(), "macosx-show-playback-buttons")];
377    [_togglePlaymodeButtons setTitle: _NS("Show Shuffle & Repeat Buttons")];
378    [_togglePlaymodeButtons setState: var_InheritBool(getIntf(), "macosx-show-playmode-buttons")];
379    [_toggleEffectsButton setTitle: _NS("Show Audio Effects Button")];
380    [_toggleEffectsButton setState: var_InheritBool(getIntf(), "macosx-show-effects-button")];
381    [_toggleSidebar setTitle: _NS("Show Sidebar")];
382    [_playlistTableColumns setTitle: _NS("Playlist Table Columns")];
383
384    [_controlsMenu setTitle: _NS("Playback")];
385    [_play setTitle: _NS("Play")];
386    [_stop setTitle: _NS("Stop")];
387    [_record setTitle: _NS("Record")];
388    [_rate_view setAutoresizingMask:NSViewWidthSizable];
389    [_rate setView: _rate_view];
390    [_rateLabel setStringValue: _NS("Playback Speed")];
391    [_rate_slowerLabel setStringValue: _NS("Slower")];
392    [_rate_normalLabel setStringValue: _NS("Normal")];
393    [_rate_fasterLabel setStringValue: _NS("Faster")];
394    [_trackSynchronization setTitle: _NS("Track Synchronization")];
395    [_previous setTitle: _NS("Previous")];
396    [_next setTitle: _NS("Next")];
397    [_random setTitle: _NS("Random")];
398    [_repeat setTitle: _NS("Repeat One")];
399    [_loop setTitle: _NS("Repeat All")];
400    [_AtoBloop setTitle: _NS("A→B Loop")];
401    [_quitAfterPB setTitle: _NS("Quit after Playback")];
402    [_fwd setTitle: _NS("Step Forward")];
403    [_bwd setTitle: _NS("Step Backward")];
404    [_jumpToTime setTitle: _NS("Jump to Time")];
405    [_rendererMenuItem setTitle:_NS("Renderer")];
406    [_rendererNoneItem setTitle:_NS("No renderer")];
407    [_program setTitle: _NS("Program")];
408    [_programMenu setTitle: _NS("Program")];
409    [_title setTitle: _NS("Title")];
410    [_titleMenu setTitle: _NS("Title")];
411    [_chapter setTitle: _NS("Chapter")];
412    [_chapterMenu setTitle: _NS("Chapter")];
413
414    [_audioMenu setTitle: _NS("Audio")];
415    [_vol_up setTitle: _NS("Increase Volume")];
416    [_vol_down setTitle: _NS("Decrease Volume")];
417    [_mute setTitle: _NS("Mute")];
418    [_audiotrack setTitle: _NS("Audio Track")];
419    [_audiotrackMenu setTitle: _NS("Audio Track")];
420    [_channels setTitle: _NS("Stereo audio mode")];
421    [_channelsMenu setTitle: _NS("Stereo audio mode")];
422    [_audioDevice setTitle: _NS("Audio Device")];
423    [_audioDeviceMenu setTitle: _NS("Audio Device")];
424    [_visual setTitle: _NS("Visualizations")];
425    [_visualMenu setTitle: _NS("Visualizations")];
426
427    [_videoMenu setTitle: _NS("Video")];
428    [_half_window setTitle: _NS("Half Size")];
429    [_normal_window setTitle: _NS("Normal Size")];
430    [_double_window setTitle: _NS("Double Size")];
431    [_fittoscreen setTitle: _NS("Fit to Screen")];
432    [_fullscreenItem setTitle: _NS("Fullscreen")];
433    [_floatontop setTitle: _NS("Float on Top")];
434    [_snapshot setTitle: _NS("Snapshot")];
435    [_videotrack setTitle: _NS("Video Track")];
436    [_videotrackMenu setTitle: _NS("Video Track")];
437    [_aspect_ratio setTitle: _NS("Aspect ratio")];
438    [_aspect_ratioMenu setTitle: _NS("Aspect ratio")];
439    [_crop setTitle: _NS("Crop")];
440    [_cropMenu setTitle: _NS("Crop")];
441    [_screen setTitle: _NS("Fullscreen Video Device")];
442    [_screenMenu setTitle: _NS("Fullscreen Video Device")];
443    [_deinterlace setTitle: _NS("Deinterlace")];
444    [_deinterlaceMenu setTitle: _NS("Deinterlace")];
445    [_deinterlace_mode setTitle: _NS("Deinterlace mode")];
446    [_deinterlace_modeMenu setTitle: _NS("Deinterlace mode")];
447    [_postprocessing setTitle: _NS("Post processing")];
448    [_postprocessingMenu setTitle: _NS("Post processing")];
449
450    [_subtitlesMenu setTitle:_NS("Subtitles")];
451    [_openSubtitleFile setTitle: _NS("Add Subtitle File...")];
452    [_subtitle_track setTitle: _NS("Subtitles Track")];
453    [_subtitle_tracksMenu setTitle: _NS("Subtitles Track")];
454    [_subtitle_size setTitle: _NS("Text Size")];
455    [_subtitle_textcolor setTitle: _NS("Text Color")];
456    [_subtitle_outlinethickness setTitle: _NS("Outline Thickness")];
457
458    // Autoresizing with constraints does not work on 10.7,
459    // translate autoresizing mask to constriaints for now
460    [_subtitle_bgopacity_view setAutoresizingMask:NSViewWidthSizable];
461    [_subtitle_bgopacity setView: _subtitle_bgopacity_view];
462    [_subtitle_bgopacityLabel setStringValue: _NS("Background Opacity")];
463    [_subtitle_bgopacityLabel_gray setStringValue: _NS("Background Opacity")];
464    [_subtitle_bgcolor setTitle: _NS("Background Color")];
465    [_teletext setTitle: _NS("Teletext")];
466    [_teletext_transparent setTitle: _NS("Transparent")];
467    [_teletext_index setTitle: _NS("Index")];
468    [_teletext_red setTitle: _NS("Red")];
469    [_teletext_green setTitle: _NS("Green")];
470    [_teletext_yellow setTitle: _NS("Yellow")];
471    [_teletext_blue setTitle: _NS("Blue")];
472
473    [_windowMenu setTitle: _NS("Window")];
474    [_minimize setTitle: _NS("Minimize")];
475    [_zoom_window setTitle: _NS("Zoom")];
476    [_player setTitle: _NS("Player...")];
477    [_controller setTitle: _NS("Main Window...")];
478    [_audioeffects setTitle: _NS("Audio Effects...")];
479    [_videoeffects setTitle: _NS("Video Effects...")];
480    [_bookmarks setTitle: _NS("Bookmarks...")];
481    [_playlist setTitle: _NS("Playlist...")];
482    [_info setTitle: _NS("Media Information...")];
483    [_messages setTitle: _NS("Messages...")];
484    [_errorsAndWarnings setTitle: _NS("Errors and Warnings...")];
485
486    [_bring_atf setTitle: _NS("Bring All to Front")];
487
488    [_helpMenu setTitle: _NS("Help")];
489    [_help setTitle: _NS("VLC media player Help...")];
490    [_license setTitle: _NS("License")];
491    [_documentation setTitle: _NS("Online Documentation...")];
492    [_website setTitle: _NS("VideoLAN Website...")];
493    [_donation setTitle: _NS("Make a donation...")];
494    [_forum setTitle: _NS("Online Forum...")];
495
496    /* dock menu */
497    [_dockMenuplay setTitle: _NS("Play")];
498    [_dockMenustop setTitle: _NS("Stop")];
499    [_dockMenunext setTitle: _NS("Next")];
500    [_dockMenuprevious setTitle: _NS("Previous")];
501    [_dockMenumute setTitle: _NS("Mute")];
502
503    /* vout menu */
504    [_voutMenuplay setTitle: _NS("Play")];
505    [_voutMenustop setTitle: _NS("Stop")];
506    [_voutMenuprev setTitle: _NS("Previous")];
507    [_voutMenunext setTitle: _NS("Next")];
508    [_voutMenuvolup setTitle: _NS("Volume Up")];
509    [_voutMenuvoldown setTitle: _NS("Volume Down")];
510    [_voutMenumute setTitle: _NS("Mute")];
511    [_voutMenufullscreen setTitle: _NS("Fullscreen")];
512    [_voutMenusnapshot setTitle: _NS("Snapshot")];
513}
514
515#pragma mark - Termination
516
517- (void)releaseRepresentedObjects:(NSMenu *)the_menu
518{
519    NSArray *menuitems_array = [the_menu itemArray];
520    NSUInteger menuItemCount = [menuitems_array count];
521    for (NSUInteger i=0; i < menuItemCount; i++) {
522        NSMenuItem *one_item = [menuitems_array objectAtIndex:i];
523        if ([one_item hasSubmenu])
524            [self releaseRepresentedObjects: [one_item submenu]];
525
526        [one_item setRepresentedObject:NULL];
527    }
528}
529
530#pragma mark - Interface update
531
532- (void)setupMenus
533{
534    playlist_t *p_playlist = pl_Get(getIntf());
535    input_thread_t *p_input = playlist_CurrentInput(p_playlist);
536    if (p_input != NULL) {
537        [self setupVarMenuItem:_program target: (vlc_object_t *)p_input
538                                 var:"program" selector: @selector(toggleVar:)];
539
540        [self setupVarMenuItem:_title target: (vlc_object_t *)p_input
541                                 var:"title" selector: @selector(toggleVar:)];
542
543        [self setupVarMenuItem:_chapter target: (vlc_object_t *)p_input
544                                 var:"chapter" selector: @selector(toggleVar:)];
545
546        [self setupVarMenuItem:_audiotrack target: (vlc_object_t *)p_input
547                                 var:"audio-es" selector: @selector(toggleVar:)];
548
549        [self setupVarMenuItem:_videotrack target: (vlc_object_t *)p_input
550                                 var:"video-es" selector: @selector(toggleVar:)];
551
552        [self setupVarMenuItem:_subtitle_track target: (vlc_object_t *)p_input
553                                 var:"spu-es" selector: @selector(toggleVar:)];
554
555        audio_output_t *p_aout = playlist_GetAout(p_playlist);
556        if (p_aout != NULL) {
557            [self setupVarMenuItem:_channels target: (vlc_object_t *)p_aout
558                                     var:"stereo-mode" selector: @selector(toggleVar:)];
559
560            [self setupVarMenuItem:_visual target: (vlc_object_t *)p_aout
561                                     var:"visual" selector: @selector(toggleVar:)];
562            vlc_object_release(p_aout);
563        }
564
565        vout_thread_t *p_vout = getVoutForActiveWindow();
566
567        if (p_vout != NULL) {
568            [self setupVarMenuItem:_aspect_ratio target: (vlc_object_t *)p_vout
569                                     var:"aspect-ratio" selector: @selector(toggleVar:)];
570
571            [self setupVarMenuItem:_crop target: (vlc_object_t *) p_vout
572                                     var:"crop" selector: @selector(toggleVar:)];
573
574            [self setupVarMenuItem:_deinterlace target: (vlc_object_t *)p_vout
575                                     var:"deinterlace" selector: @selector(toggleVar:)];
576
577            [self setupVarMenuItem:_deinterlace_mode target: (vlc_object_t *)p_vout
578                                     var:"deinterlace-mode" selector: @selector(toggleVar:)];
579
580            vlc_object_release(p_vout);
581
582            [self refreshVoutDeviceMenu:nil];
583        }
584        [_postprocessing setEnabled:YES];
585        vlc_object_release(p_input);
586    } else {
587        [_postprocessing setEnabled:NO];
588    }
589}
590
591- (void)refreshVoutDeviceMenu:(NSNotification *)notification
592{
593    NSUInteger count = (NSUInteger) [_screenMenu numberOfItems];
594    NSMenu *submenu = _screenMenu;
595    if (count > 0)
596        [submenu removeAllItems];
597
598    NSArray *screens = [NSScreen screens];
599    NSMenuItem *mitem;
600    count = [screens count];
601    [_screen setEnabled: YES];
602    [submenu addItemWithTitle: _NS("Default") action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
603    mitem = [submenu itemAtIndex: 0];
604    [mitem setTag: 0];
605    [mitem setEnabled: YES];
606    [mitem setTarget: self];
607    NSRect s_rect;
608    for (NSUInteger i = 0; i < count; i++) {
609        s_rect = [[screens objectAtIndex:i] frame];
610        [submenu addItemWithTitle: [NSString stringWithFormat: @"%@ %li (%ix%i)", _NS("Screen"), i+1,
611                                      (int)s_rect.size.width, (int)s_rect.size.height] action:@selector(toggleFullscreenDevice:) keyEquivalent:@""];
612        mitem = [submenu itemAtIndex:i+1];
613        [mitem setTag: (int)[[screens objectAtIndex:i] displayID]];
614        [mitem setEnabled: YES];
615        [mitem setTarget: self];
616    }
617    [[submenu itemWithTag: var_InheritInteger(getIntf(), "macosx-vdev")] setState: NSOnState];
618}
619
620- (void)setSubmenusEnabled:(BOOL)b_enabled
621{
622    [_program setEnabled: b_enabled];
623    [_title setEnabled: b_enabled];
624    [_chapter setEnabled: b_enabled];
625    [_audiotrack setEnabled: b_enabled];
626    [_visual setEnabled: b_enabled];
627    [_videotrack setEnabled: b_enabled];
628    [_subtitle_track setEnabled: b_enabled];
629    [_channels setEnabled: b_enabled];
630    [_deinterlace setEnabled: b_enabled];
631    [_deinterlace_mode setEnabled: b_enabled];
632    [_screen setEnabled: b_enabled];
633    [_aspect_ratio setEnabled: b_enabled];
634    [_crop setEnabled: b_enabled];
635}
636
637- (void)setSubtitleMenuEnabled:(BOOL)b_enabled
638{
639    [_openSubtitleFile setEnabled: b_enabled];
640    if (b_enabled) {
641        [_subtitle_bgopacityLabel_gray setHidden: YES];
642        [_subtitle_bgopacityLabel setHidden: NO];
643    } else {
644        [_subtitle_bgopacityLabel_gray setHidden: NO];
645        [_subtitle_bgopacityLabel setHidden: YES];
646    }
647    [_subtitle_bgopacity_sld setEnabled: b_enabled];
648    [_teletext setEnabled: b_enabled];
649}
650
651- (void)setRateControlsEnabled:(BOOL)b_enabled
652{
653    [_rate_sld setEnabled: b_enabled];
654    [_rate_sld setIntValue: [[VLCCoreInteraction sharedInstance] playbackRate]];
655    int i = [[VLCCoreInteraction sharedInstance] playbackRate];
656    double speed =  pow(2, (double)i / 17);
657    [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
658
659    NSColor *color = b_enabled ? [NSColor controlTextColor] : [NSColor disabledControlTextColor];
660
661    [_rateLabel setTextColor:color];
662    [_rate_slowerLabel setTextColor:color];
663    [_rate_normalLabel setTextColor:color];
664    [_rate_fasterLabel setTextColor:color];
665    [_rateTextField setTextColor:color];
666
667    [self setSubtitleMenuEnabled: b_enabled];
668}
669
670#pragma mark - View
671
672- (IBAction)toggleEffectsButton:(id)sender
673{
674    BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-effects-button");
675    config_PutInt(getIntf(), "macosx-show-effects-button", b_value);
676    [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] setupEffectsButton:YES];
677    [_toggleEffectsButton setState: b_value];
678}
679
680- (IBAction)toggleJumpButtons:(id)sender
681{
682    BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-playback-buttons");
683    config_PutInt(getIntf(), "macosx-show-playback-buttons", b_value);
684
685    [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] setupJumpButtons:YES];
686    [[[VLCMain sharedInstance] voutController] updateWindowsUsingBlock:^(VLCVideoWindowCommon *window) {
687        [[window controlsBar] toggleForwardBackwardMode: b_value];
688    }];
689
690    [_toggleJumpButtons setState: b_value];
691}
692
693- (IBAction)togglePlaymodeButtons:(id)sender
694{
695    BOOL b_value = !var_InheritBool(getIntf(), "macosx-show-playmode-buttons");
696    config_PutInt(getIntf(), "macosx-show-playmode-buttons", b_value);
697    [(VLCMainWindowControlsBar *)[[[VLCMain sharedInstance] mainWindow] controlsBar] setupPlaymodeButtons:YES];
698    [_togglePlaymodeButtons setState: b_value];
699}
700
701- (IBAction)toggleSidebar:(id)sender
702{
703    [[[VLCMain sharedInstance] mainWindow] toggleLeftSubSplitView];
704}
705
706- (void)updateSidebarMenuItem:(BOOL)show;
707{
708    [_toggleSidebar setState:show];
709}
710
711#pragma mark - Playback
712
713- (IBAction)play:(id)sender
714{
715    [[VLCCoreInteraction sharedInstance] playOrPause];
716}
717
718- (IBAction)stop:(id)sender
719{
720    [[VLCCoreInteraction sharedInstance] stop];
721}
722
723- (IBAction)prev:(id)sender
724{
725    [[VLCCoreInteraction sharedInstance] previous];
726}
727
728- (IBAction)next:(id)sender
729{
730    [[VLCCoreInteraction sharedInstance] next];
731}
732
733- (IBAction)random:(id)sender
734{
735    [[VLCCoreInteraction sharedInstance] shuffle];
736}
737
738- (IBAction)repeat:(id)sender
739{
740    vlc_value_t val;
741    intf_thread_t *p_intf = getIntf();
742    playlist_t *p_playlist = pl_Get(p_intf);
743
744    var_Get(p_playlist, "repeat", &val);
745    if (! val.b_bool)
746        [[VLCCoreInteraction sharedInstance] repeatOne];
747    else
748        [[VLCCoreInteraction sharedInstance] repeatOff];
749}
750
751- (IBAction)loop:(id)sender
752{
753    vlc_value_t val;
754    intf_thread_t *p_intf = getIntf();
755    playlist_t *p_playlist = pl_Get(p_intf);
756
757    var_Get(p_playlist, "loop", &val);
758    if (! val.b_bool)
759        [[VLCCoreInteraction sharedInstance] repeatAll];
760    else
761        [[VLCCoreInteraction sharedInstance] repeatOff];
762}
763
764- (IBAction)forward:(id)sender
765{
766    [[VLCCoreInteraction sharedInstance] forward];
767}
768
769- (IBAction)backward:(id)sender
770{
771    [[VLCCoreInteraction sharedInstance] backward];
772}
773
774- (IBAction)volumeUp:(id)sender
775{
776    [[VLCCoreInteraction sharedInstance] volumeUp];
777}
778
779- (IBAction)volumeDown:(id)sender
780{
781    [[VLCCoreInteraction sharedInstance] volumeDown];
782}
783
784- (IBAction)mute:(id)sender
785{
786    [[VLCCoreInteraction sharedInstance] toggleMute];
787}
788
789- (void)lockVideosAspectRatio:(id)sender
790{
791    [[VLCCoreInteraction sharedInstance] setAspectRatioIsLocked: ![sender state]];
792    [sender setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
793}
794
795- (IBAction)quitAfterPlayback:(id)sender
796{
797    playlist_t *p_playlist = pl_Get(getIntf());
798    bool b_value = !var_CreateGetBool(p_playlist, "play-and-exit");
799    var_SetBool(p_playlist, "play-and-exit", b_value);
800    config_PutInt(getIntf(), "play-and-exit", b_value);
801}
802
803- (IBAction)toggleRecord:(id)sender
804{
805    [[VLCCoreInteraction sharedInstance] toggleRecord];
806}
807
808- (void)updateRecordState:(BOOL)b_value
809{
810    [_record setState:b_value];
811}
812
813- (IBAction)setPlaybackRate:(id)sender
814{
815    [[VLCCoreInteraction sharedInstance] setPlaybackRate: [_rate_sld intValue]];
816    int i = [[VLCCoreInteraction sharedInstance] playbackRate];
817    double speed =  pow(2, (double)i / 17);
818    [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
819}
820
821- (void)updatePlaybackRate
822{
823    int i = [[VLCCoreInteraction sharedInstance] playbackRate];
824    double speed =  pow(2, (double)i / 17);
825    [_rateTextField setStringValue: [NSString stringWithFormat:@"%.2fx", speed]];
826    [_rate_sld setIntValue: i];
827}
828
829- (IBAction)toggleAtoBloop:(id)sender
830{
831    [[VLCCoreInteraction sharedInstance] setAtoB];
832}
833
834- (IBAction)goToSpecificTime:(id)sender
835{
836    input_thread_t *p_input = pl_CurrentInput(getIntf());
837    if (p_input) {
838        /* we can obviously only do that if an input is available */
839        int64_t length = var_GetInteger(p_input, "length");
840        [_timeSelectionPanel setMaxValue:(length / CLOCK_FREQ)];
841        int64_t pos = var_GetInteger(p_input, "time");
842        [_timeSelectionPanel setJumpTimeValue: (pos / CLOCK_FREQ)];
843        [_timeSelectionPanel runModalForWindow:[NSApp mainWindow]
844                             completionHandler:^(NSInteger returnCode, int64_t returnTime) {
845
846            if (returnCode != NSOKButton)
847                return;
848
849            input_thread_t *p_input = pl_CurrentInput(getIntf());
850            if (p_input) {
851                input_Control(p_input, INPUT_SET_TIME, (int64_t)(returnTime *1000000));
852                vlc_object_release(p_input);
853            }
854        }];
855
856        vlc_object_release(p_input);
857    }
858}
859
860- (IBAction)selectRenderer:(id)sender
861{
862    [_rendererMenuController selectRenderer:sender];
863}
864
865#pragma mark - audio menu
866
867- (void)refreshAudioDeviceList
868{
869    char **ids, **names;
870    char *currentDevice;
871
872    [_audioDeviceMenu removeAllItems];
873
874    audio_output_t *p_aout = getAout();
875    if (!p_aout)
876        return;
877
878    int n = aout_DevicesList(p_aout, &ids, &names);
879    if (n == -1) {
880        vlc_object_release(p_aout);
881        return;
882    }
883
884    currentDevice = aout_DeviceGet(p_aout);
885    NSMenuItem *_tmp;
886
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);
893
894    [[_audioDeviceMenu itemWithTag:[[NSString stringWithFormat:@"%s", currentDevice] intValue]] setState:NSOnState];
895
896    free(currentDevice);
897
898    for (NSUInteger x = 0; x < n; x++) {
899        free(ids[x]);
900        free(names[x]);
901    }
902    free(ids);
903    free(names);
904
905    [_audioDeviceMenu setAutoenablesItems:YES];
906    [_audioDevice setEnabled:YES];
907}
908
909- (void)toggleAudioDevice:(id)sender
910{
911    audio_output_t *p_aout = getAout();
912    if (!p_aout)
913        return;
914
915    int returnValue = 0;
916
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);
921
922    if (returnValue != 0)
923        msg_Warn(getIntf(), "failed to set audio device %li", [sender tag]);
924
925    vlc_object_release(p_aout);
926    [self refreshAudioDeviceList];
927}
928
929#pragma mark - video menu
930
931- (IBAction)toggleFullscreen:(id)sender
932{
933    [[VLCCoreInteraction sharedInstance] toggleFullscreen];
934}
935
936- (IBAction)resizeVideoWindow:(id)sender
937{
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    }
956}
957
958- (IBAction)floatOnTop:(id)sender
959{
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);
966
967            vlc_object_release(p_vout);
968        }
969        vlc_object_release(p_input);
970    }
971}
972
973- (IBAction)createVideoSnapshot:(id)sender
974{
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    }
984}
985
986- (void)_disablePostProcessing
987{
988    [[VLCCoreInteraction sharedInstance] setVideoFilter:"postproc" on:false];
989}
990
991- (void)_enablePostProcessing
992{
993    [[VLCCoreInteraction sharedInstance] setVideoFilter:"postproc" on:true];
994}
995
996- (void)togglePostProcessing:(id)sender
997{
998    char *psz_name = "postproc";
999    NSInteger count = [_postprocessingMenu numberOfItems];
1000    for (NSUInteger x = 0; x < count; x++)
1001        [[_postprocessingMenu itemAtIndex:x] setState:NSOffState];
1002
1003    if ([sender tag] == -1) {
1004        [self _disablePostProcessing];
1005        [sender setState:NSOnState];
1006    } else {
1007        [self _enablePostProcessing];
1008        [sender setState:NSOnState];
1009
1010        [[VLCCoreInteraction sharedInstance] setVideoFilterProperty:"postproc-q" forFilter:"postproc" withValue:(vlc_value_t){ .i_int = [sender tag] }];
1011    }
1012}
1013
1014- (void)toggleFullscreenDevice:(id)sender
1015{
1016    config_PutInt(getIntf(), "macosx-vdev", [sender tag]);
1017    [self refreshVoutDeviceMenu: nil];
1018}
1019
1020#pragma mark - Subtitles Menu
1021
1022- (IBAction)addSubtitleFile:(id)sender
1023{
1024    NSInteger i_returnValue = 0;
1025    input_thread_t *p_input = pl_CurrentInput(getIntf());
1026    if (!p_input)
1027        return;
1028
1029    input_item_t *p_item = input_GetItem(p_input);
1030    if (!p_item) {
1031        vlc_object_release(p_input);
1032        return;
1033    }
1034
1035    char *path = input_item_GetURI(p_item);
1036
1037    if (!path)
1038        path = strdup("");
1039
1040    NSOpenPanel *openPanel = [NSOpenPanel openPanel];
1041    [openPanel setCanChooseFiles: YES];
1042    [openPanel setCanChooseDirectories: NO];
1043    [openPanel setAllowsMultipleSelection: YES];
1044
1045    [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
1047    NSURL *url = [NSURL URLWithString:[toNSStr(path) stringByExpandingTildeInPath]];
1048    url = [url URLByDeletingLastPathComponent];
1049    [openPanel setDirectoryURL: url];
1050    free(path);
1051    vlc_object_release(p_input);
1052
1053    i_returnValue = [openPanel runModal];
1054
1055    if (i_returnValue == NSOKButton)
1056        [[VLCCoreInteraction sharedInstance] addSubtitlesToCurrentInput:[openPanel URLs]];
1057}
1058
1059- (void)switchSubtitleSize:(id)sender
1060{
1061    int intValue = [sender tag];
1062    var_SetInteger(pl_Get(getIntf()), "sub-text-scale", intValue);
1063}
1064
1065
1066- (void)switchSubtitleOption:(id)sender
1067{
1068    int intValue = [sender tag];
1069    NSString *representedObject = [sender representedObject];
1070
1071    var_SetInteger(pl_Get(getIntf()), [representedObject UTF8String], intValue);
1072
1073    NSMenu *menu = [sender menu];
1074    NSUInteger count = (NSUInteger) [menu numberOfItems];
1075    for (NSUInteger x = 0; x < count; x++)
1076        [[menu itemAtIndex:x] setState:NSOffState];
1077    [[menu itemWithTag:intValue] setState:NSOnState];
1078}
1079
1080- (IBAction)switchSubtitleBackgroundOpacity:(id)sender
1081{
1082    var_SetInteger(pl_Get(getIntf()), "freetype-background-opacity", [sender intValue]);
1083}
1084
1085- (IBAction)telxTransparent:(id)sender
1086{
1087    vlc_object_t *p_vbi;
1088    p_vbi = (vlc_object_t *) vlc_object_find_name(pl_Get(getIntf()), "zvbi");
1089    if (p_vbi) {
1090        var_SetBool(p_vbi, "vbi-opaque", [sender state]);
1091        [sender setState: ![sender state]];
1092        vlc_object_release(p_vbi);
1093    }
1094}
1095
1096- (IBAction)telxNavLink:(id)sender
1097{
1098    vlc_object_t *p_vbi;
1099    int i_page = 0;
1100
1101    if ([[sender title] isEqualToString: _NS("Index")])
1102        i_page = 'i' << 16;
1103    else if ([[sender title] isEqualToString: _NS("Red")])
1104        i_page = 'r' << 16;
1105    else if ([[sender title] isEqualToString: _NS("Green")])
1106        i_page = 'g' << 16;
1107    else if ([[sender title] isEqualToString: _NS("Yellow")])
1108        i_page = 'y' << 16;
1109    else if ([[sender title] isEqualToString: _NS("Blue")])
1110        i_page = 'b' << 16;
1111    if (i_page == 0) return;
1112
1113    p_vbi = (vlc_object_t *) vlc_object_find_name(pl_Get(getIntf()), "zvbi");
1114    if (p_vbi) {
1115        var_SetInteger(p_vbi, "vbi-page", i_page);
1116        vlc_object_release(p_vbi);
1117    }
1118}
1119
1120#pragma mark - Panels
1121
1122- (IBAction)intfOpenFile:(id)sender
1123{
1124    [[[VLCMain sharedInstance] open] openFileWithAction:^(NSArray *files) {
1125        [[[VLCMain sharedInstance] playlist] addPlaylistItems:files];
1126    }];
1127}
1128
1129- (IBAction)intfOpenFileGeneric:(id)sender
1130{
1131    [[[VLCMain sharedInstance] open] openFileGeneric];
1132}
1133
1134- (IBAction)intfOpenDisc:(id)sender
1135{
1136    [[[VLCMain sharedInstance] open] openDisc];
1137}
1138
1139- (IBAction)intfOpenNet:(id)sender
1140{
1141    [[[VLCMain sharedInstance] open] openNet];
1142}
1143
1144- (IBAction)intfOpenCapture:(id)sender
1145{
1146    [[[VLCMain sharedInstance] open] openCapture];
1147}
1148
1149- (IBAction)savePlaylist:(id)sender
1150{
1151    playlist_t *p_playlist = pl_Get(getIntf());
1152
1153    NSSavePanel *savePanel = [NSSavePanel savePanel];
1154    NSString * name = [NSString stringWithFormat: @"%@", _NS("Untitled")];
1155
1156    [NSBundle loadNibNamed:@"PlaylistAccessoryView" owner:self];
1157
1158    [_playlistSaveAccessoryText setStringValue: _NS("File Format:")];
1159    [[_playlistSaveAccessoryPopup itemAtIndex:0] setTitle: _NS("Extended M3U")];
1160    [[_playlistSaveAccessoryPopup itemAtIndex:1] setTitle: _NS("XML Shareable Playlist Format (XSPF)")];
1161    [[_playlistSaveAccessoryPopup itemAtIndex:2] setTitle: _NS("HTML playlist")];
1162
1163    [savePanel setTitle: _NS("Save Playlist")];
1164    [savePanel setPrompt: _NS("Save")];
1165    [savePanel setAccessoryView: _playlistSaveAccessoryView];
1166    [savePanel setNameFieldStringValue: name];
1167
1168    if ([savePanel runModal] == NSFileHandlingPanelOKButton) {
1169        NSString *filename = [[savePanel URL] path];
1170
1171        if ([_playlistSaveAccessoryPopup indexOfSelectedItem] == 0) {
1172            NSString *actualFilename;
1173            NSRange range;
1174            range.location = [filename length] - [@".m3u" length];
1175            range.length = [@".m3u" length];
1176
1177            if ([filename compare:@".m3u" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1178                actualFilename = [NSString stringWithFormat: @"%@.m3u", filename];
1179            else
1180                actualFilename = filename;
1181
1182            playlist_Export(p_playlist,
1183                            [actualFilename fileSystemRepresentation],
1184                            true, "export-m3u");
1185        } else if ([_playlistSaveAccessoryPopup indexOfSelectedItem] == 1) {
1186            NSString *actualFilename;
1187            NSRange range;
1188            range.location = [filename length] - [@".xspf" length];
1189            range.length = [@".xspf" length];
1190
1191            if ([filename compare:@".xspf" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1192                actualFilename = [NSString stringWithFormat: @"%@.xspf", filename];
1193            else
1194                actualFilename = filename;
1195
1196            playlist_Export(p_playlist,
1197                            [actualFilename fileSystemRepresentation],
1198                            true, "export-xspf");
1199        } else {
1200            NSString *actualFilename;
1201            NSRange range;
1202            range.location = [filename length] - [@".html" length];
1203            range.length = [@".html" length];
1204
1205            if ([filename compare:@".html" options: NSCaseInsensitiveSearch range: range] != NSOrderedSame)
1206                actualFilename = [NSString stringWithFormat: @"%@.html", filename];
1207            else
1208                actualFilename = filename;
1209
1210            playlist_Export(p_playlist,
1211                            [actualFilename fileSystemRepresentation],
1212                            true, "export-html");
1213        }
1214    }
1215}
1216
1217- (IBAction)showConvertAndSave:(id)sender
1218{
1219    [[[VLCMain sharedInstance] convertAndSaveWindow] showWindow:self];
1220}
1221
1222- (IBAction)showVideoEffects:(id)sender
1223{
1224    [[[VLCMain sharedInstance] videoEffectsPanel] toggleWindow:sender];
1225}
1226
1227- (IBAction)showTrackSynchronization:(id)sender
1228{
1229    [[[VLCMain sharedInstance] trackSyncPanel] toggleWindow:sender];
1230}
1231
1232- (IBAction)showAudioEffects:(id)sender
1233{
1234    [[[VLCMain sharedInstance] audioEffectsPanel] toggleWindow:sender];
1235}
1236
1237- (IBAction)showBookmarks:(id)sender
1238{
1239    [[[VLCMain sharedInstance] bookmarks] toggleWindow:sender];
1240}
1241
1242- (IBAction)showPreferences:(id)sender
1243{
1244    NSInteger i_level = [[[VLCMain sharedInstance] voutController] currentStatusWindowLevel];
1245    [[[VLCMain sharedInstance] simplePreferences] showSimplePrefsWithLevel:i_level];
1246}
1247
1248- (IBAction)openAddonManager:(id)sender
1249{
1250    if (!_addonsController)
1251        _addonsController = [[VLCAddonsWindowController alloc] init];
1252
1253    [_addonsController showWindow:self];
1254}
1255
1256- (IBAction)showErrorsAndWarnings:(id)sender
1257{
1258    [[[[VLCMain sharedInstance] coreDialogProvider] errorPanel] showWindow:self];
1259}
1260
1261- (IBAction)showMessagesPanel:(id)showMessagesPanel
1262{
1263    [[[VLCMain sharedInstance] debugMsgPanel] showWindow:self];
1264}
1265
1266- (IBAction)showMainWindow:(id)sender
1267{
1268    [[[VLCMain sharedInstance] mainWindow] makeKeyAndOrderFront:sender];
1269}
1270
1271- (IBAction)showPlaylist:(id)sender
1272{
1273    [[[VLCMain sharedInstance] mainWindow] changePlaylistState: psUserMenuEvent];
1274}
1275
1276#pragma mark - Help and Docs
1277
1278- (IBAction)showAbout:(id)sender
1279{
1280    if (!_aboutWindowController)
1281        _aboutWindowController = [[VLCAboutWindowController alloc] init];
1282
1283    [_aboutWindowController showAbout];
1284}
1285
1286- (IBAction)showLicense:(id)sender
1287{
1288    if (!_aboutWindowController)
1289        _aboutWindowController = [[VLCAboutWindowController alloc] init];
1290
1291    [_aboutWindowController showGPL];
1292}
1293
1294- (IBAction)showHelp:(id)sender
1295{
1296    if (!_helpWindowController)
1297        _helpWindowController = [[VLCHelpWindowController alloc] init];
1298
1299    [_helpWindowController showHelp];
1300}
1301
1302- (IBAction)openDocumentation:(id)sender
1303{
1304    NSURL *url = [NSURL URLWithString: @"https://www.videolan.org/doc/"];
1305
1306    [[NSWorkspace sharedWorkspace] openURL: url];
1307}
1308
1309- (IBAction)openWebsite:(id)sender
1310{
1311    NSURL *url = [NSURL URLWithString: @"https://www.videolan.org/"];
1312
1313    [[NSWorkspace sharedWorkspace] openURL: url];
1314}
1315
1316- (IBAction)openForum:(id)sender
1317{
1318    NSURL *url = [NSURL URLWithString: @"https://forum.videolan.org/"];
1319
1320    [[NSWorkspace sharedWorkspace] openURL: url];
1321}
1322
1323- (IBAction)openDonate:(id)sender
1324{
1325    NSURL *url = [NSURL URLWithString: @"https://www.videolan.org/contribute.html#paypal"];
1326
1327    [[NSWorkspace sharedWorkspace] openURL: url];
1328}
1329
1330- (IBAction)showInformationPanel:(id)sender
1331{
1332    [[[VLCMain sharedInstance] currentMediaInfoPanel] toggleWindow:sender];
1333}
1334
1335#pragma mark - convinience stuff for other objects
1336
1337- (void)setPlay
1338{
1339    [_play setTitle: _NS("Play")];
1340    [_dockMenuplay setTitle: _NS("Play")];
1341    [_voutMenuplay setTitle: _NS("Play")];
1342}
1343
1344- (void)setPause
1345{
1346    [_play setTitle: _NS("Pause")];
1347    [_dockMenuplay setTitle: _NS("Pause")];
1348    [_voutMenuplay setTitle: _NS("Pause")];
1349}
1350
1351- (void)setRepeatOne
1352{
1353    [_repeat setState: NSOnState];
1354    [_loop setState: NSOffState];
1355}
1356
1357- (void)setRepeatAll
1358{
1359    [_repeat setState: NSOffState];
1360    [_loop setState: NSOnState];
1361}
1362
1363- (void)setRepeatOff
1364{
1365    [_repeat setState: NSOffState];
1366    [_loop setState: NSOffState];
1367}
1368
1369- (void)setShuffle
1370{
1371    bool b_value;
1372    playlist_t *p_playlist = pl_Get(getIntf());
1373    b_value = var_GetBool(p_playlist, "random");
1374
1375    [_random setState: b_value];
1376}
1377
1378#pragma mark - Dynamic menu creation and validation
1379
1380- (void)setupVarMenuItem:(NSMenuItem *)mi
1381                  target:(vlc_object_t *)p_object
1382                     var:(const char *)psz_variable
1383                selector:(SEL)pf_callback
1384{
1385    vlc_value_t val, text;
1386    int i_type = var_Type(p_object, psz_variable);
1387
1388    switch(i_type & VLC_VAR_TYPE) {
1389        case VLC_VAR_VOID:
1390        case VLC_VAR_BOOL:
1391        case VLC_VAR_STRING:
1392        case VLC_VAR_INTEGER:
1393            break;
1394        default:
1395            /* Variable doesn't exist or isn't handled */
1396            msg_Warn(p_object, "variable %s doesn't exist or isn't handled", psz_variable);
1397            return;
1398    }
1399
1400    /* Get the descriptive name of the variable */
1401    var_Change(p_object, psz_variable, VLC_VAR_GETTEXT, &text, NULL);
1402    [mi setTitle: _NS(text.psz_string ? text.psz_string : psz_variable)];
1403
1404    if (i_type & VLC_VAR_HASCHOICE) {
1405        NSMenu *menu = [mi submenu];
1406
1407        [self setupVarMenu:menu forMenuItem:mi target:p_object
1408                       var:psz_variable selector:pf_callback];
1409
1410        free(text.psz_string);
1411        return;
1412    }
1413
1414    if (var_Get(p_object, psz_variable, &val) < 0)
1415        return;
1416
1417    VLCAutoGeneratedMenuContent *data;
1418    switch(i_type & VLC_VAR_TYPE) {
1419        case VLC_VAR_VOID:
1420            data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1421                                                                      andValue: val ofType: i_type];
1422            [mi setRepresentedObject:data];
1423            break;
1424
1425        case VLC_VAR_BOOL:
1426            data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1427                                                                      andValue: val ofType: i_type];
1428            [mi setRepresentedObject:data];
1429            if (!(i_type & VLC_VAR_ISCOMMAND))
1430                [mi setState: val.b_bool ? TRUE : FALSE ];
1431            break;
1432
1433        default:
1434            break;
1435    }
1436
1437    if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1438    free(text.psz_string);
1439}
1440
1441
1442- (void)setupVarMenu:(NSMenu *)menu
1443         forMenuItem: (NSMenuItem *)parent
1444              target:(vlc_object_t *)p_object
1445                 var:(const char *)psz_variable
1446            selector:(SEL)pf_callback
1447{
1448    vlc_value_t val, val_list, text_list;
1449    int i_type, i;
1450
1451    /* remove previous items */
1452    [menu removeAllItems];
1453
1454    /* we disable everything here, and enable it again when needed, below */
1455    [parent setEnabled:NO];
1456
1457    /* Aspect Ratio */
1458    if ([[parent title] isEqualToString: _NS("Aspect ratio")] == YES) {
1459        NSMenuItem *lmi_tmp2;
1460        lmi_tmp2 = [menu addItemWithTitle: _NS("Lock Aspect Ratio") action: @selector(lockVideosAspectRatio:) keyEquivalent: @""];
1461        [lmi_tmp2 setTarget: self];
1462        [lmi_tmp2 setEnabled: YES];
1463        [lmi_tmp2 setState: [[VLCCoreInteraction sharedInstance] aspectRatioIsLocked]];
1464        [parent setEnabled: YES];
1465        [menu addItem: [NSMenuItem separatorItem]];
1466    }
1467
1468    /* Check the type of the object variable */
1469    i_type = var_Type(p_object, psz_variable);
1470
1471    /* Make sure we want to display the variable */
1472    if (i_type & VLC_VAR_HASCHOICE) {
1473        var_Change(p_object, psz_variable, VLC_VAR_CHOICESCOUNT, &val, NULL);
1474        if (val.i_int == 0 || val.i_int == 1)
1475            return;
1476    }
1477    else
1478        return;
1479
1480    switch(i_type & VLC_VAR_TYPE) {
1481        case VLC_VAR_VOID:
1482        case VLC_VAR_BOOL:
1483        case VLC_VAR_STRING:
1484        case VLC_VAR_INTEGER:
1485            break;
1486        default:
1487            /* Variable doesn't exist or isn't handled */
1488            return;
1489    }
1490
1491    if (var_Get(p_object, psz_variable, &val) < 0) {
1492        return;
1493    }
1494
1495    if (var_Change(p_object, psz_variable, VLC_VAR_GETCHOICES,
1496                   &val_list, &text_list) < 0) {
1497        if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1498        return;
1499    }
1500
1501    /* make (un)sensitive */
1502    [parent setEnabled: (val_list.p_list->i_count > 1)];
1503
1504    for (i = 0; i < val_list.p_list->i_count; i++) {
1505        NSMenuItem *lmi;
1506        NSString *title = @"";
1507        VLCAutoGeneratedMenuContent *data;
1508
1509        switch(i_type & VLC_VAR_TYPE) {
1510            case VLC_VAR_STRING:
1511
1512                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);
1513
1514                lmi = [menu addItemWithTitle: title action: pf_callback keyEquivalent: @""];
1515                data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1516                                                                          andValue: val_list.p_list->p_values[i] ofType: i_type];
1517                [lmi setRepresentedObject:data];
1518                [lmi setTarget: self];
1519
1520                if (!strcmp(val.psz_string, val_list.p_list->p_values[i].psz_string) && !(i_type & VLC_VAR_ISCOMMAND))
1521                    [lmi setState: TRUE ];
1522
1523                break;
1524
1525            case VLC_VAR_INTEGER:
1526
1527                title = text_list.p_list->p_values[i].psz_string ?
1528                _NS(text_list.p_list->p_values[i].psz_string) : [NSString stringWithFormat: @"%"PRId64, val_list.p_list->p_values[i].i_int];
1529
1530                lmi = [menu addItemWithTitle: title action: pf_callback keyEquivalent: @""];
1531                data = [[VLCAutoGeneratedMenuContent alloc] initWithVariableName: psz_variable ofObject: p_object
1532                                                                          andValue: val_list.p_list->p_values[i] ofType: i_type];
1533                [lmi setRepresentedObject:data];
1534                [lmi setTarget: self];
1535
1536                if (val_list.p_list->p_values[i].i_int == val.i_int && !(i_type & VLC_VAR_ISCOMMAND))
1537                    [lmi setState: TRUE ];
1538                break;
1539
1540            default:
1541                break;
1542        }
1543    }
1544
1545    /* clean up everything */
1546    if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING) free(val.psz_string);
1547    var_FreeList(&val_list, &text_list);
1548}
1549
1550- (void)toggleVar:(id)sender
1551{
1552    NSMenuItem *mi = (NSMenuItem *)sender;
1553    VLCAutoGeneratedMenuContent *data = [mi representedObject];
1554    [NSThread detachNewThreadSelector: @selector(toggleVarThread:)
1555                             toTarget: self withObject: data];
1556
1557    return;
1558}
1559
1560- (int)toggleVarThread: (id)data
1561{
1562    @autoreleasepool {
1563        vlc_object_t *p_object;
1564
1565        assert([data isKindOfClass:[VLCAutoGeneratedMenuContent class]]);
1566        VLCAutoGeneratedMenuContent *menuContent = (VLCAutoGeneratedMenuContent *)data;
1567
1568        p_object = [menuContent vlcObject];
1569
1570        if (p_object != NULL) {
1571            var_Set(p_object, [menuContent name], [menuContent value]);
1572            vlc_object_release(p_object);
1573            return true;
1574        }
1575        return VLC_EGENERIC;
1576    }
1577}
1578
1579#pragma mark - menu delegation
1580
1581- (void)menuWillOpen:(NSMenu *)menu
1582{
1583    [_cancelRendererDiscoveryTimer invalidate];
1584    [_rendererMenuController startRendererDiscoveries];
1585}
1586
1587- (void)menuDidClose:(NSMenu *)menu
1588{
1589    _cancelRendererDiscoveryTimer = [NSTimer scheduledTimerWithTimeInterval:20.
1590                                                                     target:self
1591                                                                   selector:@selector(cancelRendererDiscovery)
1592                                                                   userInfo:nil
1593                                                                    repeats:NO];
1594}
1595
1596- (void)cancelRendererDiscovery
1597{
1598    [_rendererMenuController stopRendererDiscoveries];
1599}
1600
1601@end
1602
1603@implementation VLCMainMenu (NSMenuValidation)
1604
1605- (BOOL)validateMenuItem:(NSMenuItem *)mi
1606{
1607    NSString *title = [mi title];
1608    BOOL enabled = YES;
1609    vlc_value_t val;
1610    playlist_t *p_playlist = pl_Get(getIntf());
1611    input_thread_t *p_input = playlist_CurrentInput(p_playlist);
1612
1613    if (mi == _stop || mi == _voutMenustop || mi == _dockMenustop) {
1614        if (!p_input)
1615            enabled = NO;
1616        [self setupMenus]; /* Make sure input menu is up to date */
1617    } else if (mi == _previous          ||
1618               mi == _voutMenuprev      ||
1619               mi == _dockMenuprevious  ||
1620               mi == _next              ||
1621               mi == _voutMenunext      ||
1622               mi == _dockMenunext
1623               ) {
1624        PL_LOCK;
1625        enabled = playlist_CurrentSize(p_playlist) > 1;
1626        PL_UNLOCK;
1627    } else if (mi == _record) {
1628        enabled = NO;
1629        if (p_input)
1630            enabled = var_GetBool(p_input, "can-record");
1631    } else if (mi == _random) {
1632        int i_state;
1633        var_Get(p_playlist, "random", &val);
1634        i_state = val.b_bool ? NSOnState : NSOffState;
1635        [mi setState: i_state];
1636    } else if (mi == _repeat) {
1637        int i_state;
1638        var_Get(p_playlist, "repeat", &val);
1639        i_state = val.b_bool ? NSOnState : NSOffState;
1640        [mi setState: i_state];
1641    } else if (mi == _loop) {
1642        int i_state;
1643        var_Get(p_playlist, "loop", &val);
1644        i_state = val.b_bool ? NSOnState : NSOffState;
1645        [mi setState: i_state];
1646    } else if (mi == _quitAfterPB) {
1647        int i_state;
1648        bool b_value = var_InheritBool(p_playlist, "play-and-exit");
1649        i_state = b_value ? NSOnState : NSOffState;
1650        [mi setState: i_state];
1651    } else if (mi == _fwd || mi == _bwd || mi == _jumpToTime) {
1652        if (p_input != NULL) {
1653            var_Get(p_input, "can-seek", &val);
1654            enabled = val.b_bool;
1655        } else {
1656            enabled = NO;
1657        }
1658    } else if (mi == _mute || mi == _dockMenumute || mi == _voutMenumute) {
1659        [mi setState: [[VLCCoreInteraction sharedInstance] mute] ? NSOnState : NSOffState];
1660        [self setupMenus]; /* Make sure audio menu is up to date */
1661        [self refreshAudioDeviceList];
1662    } else if (mi == _half_window           ||
1663               mi == _normal_window         ||
1664               mi == _double_window         ||
1665               mi == _fittoscreen           ||
1666               mi == _snapshot              ||
1667               mi == _voutMenusnapshot      ||
1668               mi == _fullscreenItem        ||
1669               mi == _voutMenufullscreen    ||
1670               mi == _floatontop
1671               ) {
1672        enabled = NO;
1673
1674        if (p_input != NULL) {
1675            vout_thread_t *p_vout = getVoutForActiveWindow();
1676            if (p_vout != NULL) {
1677                if (mi == _floatontop)
1678                    [mi setState: var_GetBool(p_vout, "video-on-top")];
1679
1680                if (mi == _fullscreenItem || mi == _voutMenufullscreen)
1681                    [mi setState: var_GetBool(p_vout, "fullscreen")];
1682
1683                enabled = YES;
1684                vlc_object_release(p_vout);
1685            }
1686        }
1687
1688        [self setupMenus]; /* Make sure video menu is up to date */
1689
1690    } else if (mi == _openSubtitleFile) {
1691        enabled = [mi isEnabled];
1692        [self setupMenus]; /* Make sure subtitles menu is up to date */
1693    } else {
1694        NSMenuItem *_parent = [mi parentItem];
1695        if (_parent == _subtitle_size || mi == _subtitle_size           ||
1696            _parent == _subtitle_textcolor || mi == _subtitle_textcolor ||
1697            _parent == _subtitle_bgcolor || mi == _subtitle_bgcolor     ||
1698            _parent == _subtitle_bgopacity || mi == _subtitle_bgopacity ||
1699            _parent == _subtitle_outlinethickness || mi == _subtitle_outlinethickness ||
1700            _parent == _teletext || mi == _teletext
1701            ) {
1702            enabled = _openSubtitleFile.isEnabled;
1703        }
1704    }
1705
1706    if (p_input)
1707        vlc_object_release(p_input);
1708
1709    return enabled;
1710}
1711
1712@end
1713
1714
1715/*****************************************************************************
1716 *VLCAutoGeneratedMenuContent implementation
1717 *****************************************************************************
1718 *Object connected to a playlistitem which remembers the data belonging to
1719 *the variable of the autogenerated menu
1720 *****************************************************************************/
1721
1722@interface VLCAutoGeneratedMenuContent ()
1723{
1724    char *psz_name;
1725    vlc_object_t *vlc_object;
1726    vlc_value_t value;
1727    int i_type;
1728}
1729@end
1730@implementation VLCAutoGeneratedMenuContent
1731
1732-(id) initWithVariableName:(const char *)name ofObject:(vlc_object_t *)object
1733                  andValue:(vlc_value_t)val ofType:(int)type
1734{
1735    self = [super init];
1736
1737    if (self != nil) {
1738        vlc_object = vlc_object_hold(object);
1739        psz_name = strdup(name);
1740        i_type = type;
1741        value = val;
1742        if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1743            value.psz_string = strdup(val.psz_string);
1744    }
1745
1746    return(self);
1747}
1748
1749- (void)dealloc
1750{
1751    if (vlc_object)
1752        vlc_object_release(vlc_object);
1753    if ((i_type & VLC_VAR_TYPE) == VLC_VAR_STRING)
1754        free(value.psz_string);
1755    free(psz_name);
1756}
1757
1758- (const char *)name
1759{
1760    return psz_name;
1761}
1762
1763- (vlc_value_t)value
1764{
1765    return value;
1766}
1767
1768- (vlc_object_t *)vlcObject
1769{
1770    return vlc_object_hold(vlc_object);
1771}
1772
1773- (int)type
1774{
1775    return i_type;
1776}
1777
1778@end
1779