VLCSimplePrefsController.m 60.2 KB
Newer Older
1
/*****************************************************************************
2
* VLCSimplePrefsController.m: Simple Preferences for Mac OS X
3
*****************************************************************************
4
* Copyright (C) 2008-2014 VLC authors and VideoLAN
5
* $Id$
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
*
* Authors: Felix Paul Kühne <fkuehne at videolan dot org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
23
24
25
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
26

27
#import "CompatibilityFixes.h"
28
#import "VLCSimplePrefsController.h"
29
#import "prefs.h"
30
#import <vlc_actions.h>
31
#import <vlc_interface.h>
32
#import <vlc_dialog.h>
33
#import <vlc_modules.h>
34
#import <vlc_plugin.h>
35
#import <vlc_config_cat.h>
36
#import "misc.h"
37
#import "VLCMain.h"
38
#import "VLCMain+OldPrefs.h"
39
#import "AppleRemote.h"
David's avatar
David committed
40
#import "VLCCoreInteraction.h"
41
#import "NSScreen+VLCAdditions.h"
42

43
#ifdef HAVE_SPARKLE
44
#import <Sparkle/Sparkle.h>                        //for o_intf_last_updateLabel
45
#endif
46

47

48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
static struct {
    const char iso[6];
    const char name[34];
    BOOL isRightToLeft;

} const language_map[] = {
    { "auto",  N_("Auto"),              NO },
    { "en",    "American English",      NO },
    { "ar",    "عربي",                  YES },
    { "bn",    "বাংলা",                  NO },
    { "pt_BR", "Português Brasileiro",  NO },
    { "en_GB", "British English",       NO },
    { "el",    "Νέα Ελληνικά",          NO },
    { "bg",    "български език",        NO },
    { "ca",    "Català",                NO },
    { "zh_TW", "正體中文",               NO },
    { "cs",    "Čeština",               NO },
    { "cy",    "Cymraeg",               NO },
    { "da",    "Dansk",                 NO },
    { "nl",    "Nederlands",            NO },
    { "fi",    "Suomi",                 NO },
    { "et",    "eesti keel",            NO },
    { "eu",    "Euskara",               NO },
    { "fr",    "Français",              NO },
    { "ga",    "Gaeilge",               NO },
    { "gd",    "Gàidhlig",              NO },
    { "gl",    "Galego",                NO },
    { "ka",    "ქართული",               NO },
    { "de",    "Deutsch",               NO },
    { "he",    "עברית",                  YES },
    { "hr",    "hrvatski",              NO },
    { "hu",    "Magyar",                NO },
    { "hy",    "հայերեն",                NO },
    { "is",    "íslenska",              NO },
    { "id",    "Bahasa Indonesia",      NO },
    { "it",    "Italiano",              NO },
    { "ja",    "日本語",                 NO },
    { "ko",    "한국어",                  NO },
    { "lt",    "lietuvių",              NO },
    { "mn",    "Монгол хэл",            NO },
    { "ms",    "Melayu",                NO },
    { "nb",    "Bokmål",                NO },
    { "nn",    "Nynorsk",               NO },
    { "kk",    "Қазақ тілі",            NO },
    { "km",    "ភាសាខ្មែរ",                NO },
    { "ne",    "नेपाली",                  NO },
    { "oc",    "Occitan",               NO },
    { "fa",    "فارسی",                 YES },
    { "pl",    "Polski",                NO },
    { "pt_PT", "Português",             NO },
    { "pa",    "ਪੰਜਾਬੀ",                  NO },
    { "ro",    "Română",                NO },
    { "ru",    "Русский",               NO },
    { "zh_CN", "简体中文",               NO },
    { "si",    "සිංහල",                NO },
    { "sr",    "српски",                NO },
    { "sk",    "Slovensky",             NO },
    { "sl",    "slovenščina",           NO },
    { "ckb",   "کوردیی سۆرانی",         YES },
    { "es",    "Español",               NO },
    { "sv",    "Svenska",               NO },
    { "te",    "తెలుగు",                 NO },
    { "tr",    "Türkçe",                NO },
    { "uk",    "украї́нська мо́ва",       NO },
    { "vi",    "tiếng Việt",            NO },
    { "wa",    "Walon",                 NO }
114
115
};

116
117
118
static NSString* VLCSPrefsToolbarIdentifier = @"Our Simple Preferences Toolbar Identifier";
static NSString* VLCIntfSettingToolbarIdentifier = @"Intf Settings Item Identifier";
static NSString* VLCAudioSettingToolbarIdentifier = @"Audio Settings Item Identifier";
119
static NSString* VLCVideoSettingToolbarIdentifier = @"Video Settings Item Identifier";
120
121
122
static NSString* VLCOSDSettingToolbarIdentifier = @"Subtitles Settings Item Identifier";
static NSString* VLCInputSettingToolbarIdentifier = @"Input Settings Item Identifier";
static NSString* VLCHotkeysSettingToolbarIdentifier = @"Hotkeys Settings Item Identifier";
123

124
@interface VLCSimplePrefsController() <NSToolbarDelegate, NSWindowDelegate>
125
{
126
127
128
129
130
131
132
133
134
135
136
137
138
    BOOL _audioSettingChanged;
    BOOL _intfSettingChanged;
    BOOL _videoSettingChanged;
    BOOL _osdSettingChanged;
    BOOL _inputSettingChanged;
    BOOL _hotkeyChanged;

    NSOpenPanel *_selectFolderPanel;
    NSArray *_hotkeyDescriptions;
    NSArray *_hotkeyNames;
    NSArray *_hotkeysNonUseableKeys;
    NSMutableArray *_hotkeySettings;
    NSString *_keyInTransition;
139
140
141
142
143

    intf_thread_t *p_intf;
}
@end

144
@implementation VLCSimplePrefsController
145

146
147
#pragma mark Initialisation

148
149
- (id)init
{
150
151
    self = [super initWithWindowNibName:@"SimplePreferences"];
    if (self) {
David's avatar
David committed
152
        p_intf = getIntf();
153
154
    }

155
    return self;
156
157
}

158
- (void)windowDidLoad
159
160
{
    [self initStrings];
161

162
#ifdef HAVE_SPARKLE
163
    [_intf_updateCheckbox bind:@"value"
164
165
166
167
                   toObject:[SUUpdater sharedUpdater]
                withKeyPath:@"automaticallyChecksForUpdates"
                    options:nil];
#else
168
169
    [_intf_updateCheckbox setState:NSOffState];
    [_intf_updateCheckbox setEnabled:NO];
170
171
#endif

172
    /* setup the toolbar */
173
174
175
176
177
178
    NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: VLCSPrefsToolbarIdentifier];
    [toolbar setAllowsUserCustomization: NO];
    [toolbar setAutosavesConfiguration: NO];
    [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
    [toolbar setSizeMode: NSToolbarSizeModeRegular];
    [toolbar setDelegate: self];
179
    [self.window setToolbar:toolbar];
180

181
182
    [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
    [self.window setHidesOnDeactivate:YES];
183

184
185
    [_hotkeys_listbox setTarget:self];
    [_hotkeys_listbox setDoubleAction:@selector(hotkeyTableDoubleClick:)];
186

187
    /* setup useful stuff */
188
    _hotkeysNonUseableKeys = [NSArray arrayWithObjects:@"Command-c", @"Command-x", @"Command-v", @"Command-a", @"Command-," , @"Command-h", @"Command-Alt-h", @"Command-Shift-o", @"Command-o", @"Command-d", @"Command-n", @"Command-s", @"Command-l", @"Command-r", @"Command-3", @"Command-m", @"Command-w", @"Command-Shift-w", @"Command-Shift-c", @"Command-Shift-p", @"Command-i", @"Command-e", @"Command-Shift-e", @"Command-b", @"Command-Shift-m", @"Command-Ctrl-m", @"Command-?", @"Command-Alt-?", @"Command-Shift-f", nil];
189
190
191
192
193
194
195

    // Workaround for Mac OS X Lion, which does not apply the same constraints when set in IB
    NSView *clipView = _contentView.superview;

    NSDictionary *views = @{ @"view": _contentView };
    NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:views];
    [clipView addConstraints:constraints];
196
197
}

198
199
#define CreateToolbarItem(name, desc, img, sel) \
    toolbarItem = create_toolbar_item(itemIdent, name, desc, img, self, @selector(sel));
200
static inline NSToolbarItem *
201
create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSString *img, id target, SEL selector)
202
{
203
    NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdent];
204

205
206
    [toolbarItem setLabel:name];
    [toolbarItem setPaletteLabel:desc];
207

208
209
    [toolbarItem setToolTip:desc];
    [toolbarItem setImage:[NSImage imageNamed:img]];
210

211
212
    [toolbarItem setTarget:target];
    [toolbarItem setAction:selector];
213

214
215
    [toolbarItem setEnabled:YES];
    [toolbarItem setAutovalidates:YES];
216

217
    return toolbarItem;
218
219
}

220
221
222
- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar
      itemForItemIdentifier:(NSString *)itemIdent
  willBeInsertedIntoToolbar:(BOOL)willBeInserted
223
{
224
    NSToolbarItem *toolbarItem = nil;
225

226
    if ([itemIdent isEqual: VLCIntfSettingToolbarIdentifier]) {
227
        CreateToolbarItem(_NS("Interface"), _NS("Interface Settings"), @"VLCInterfaceCone", showInterfaceSettings);
228
    } else if ([itemIdent isEqual: VLCAudioSettingToolbarIdentifier]) {
229
        CreateToolbarItem(_NS("Audio"), _NS("Audio Settings"), @"VLCAudioCone", showAudioSettings);
230
    } else if ([itemIdent isEqual: VLCVideoSettingToolbarIdentifier]) {
231
        CreateToolbarItem(_NS("Video"), _NS("Video Settings"), @"VLCVideoCone", showVideoSettings);
232
    } else if ([itemIdent isEqual: VLCOSDSettingToolbarIdentifier]) {
233
        CreateToolbarItem(_NS(SUBPIC_TITLE), _NS("Subtitle & On Screen Display Settings"), @"VLCSubtitleCone", showOSDSettings);
234
    } else if ([itemIdent isEqual: VLCInputSettingToolbarIdentifier]) {
235
        CreateToolbarItem(_NS(INPUT_TITLE), _NS("Input & Codec Settings"), @"VLCInputCone", showInputSettings);
236
    } else if ([itemIdent isEqual: VLCHotkeysSettingToolbarIdentifier]) {
237
        CreateToolbarItem(_NS("Hotkeys"), _NS("Hotkeys settings"), @"VLCHotkeysCone", showHotkeySettings);
238
239
    }

240
    return toolbarItem;
241
242
243
244
}

- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
{
245
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
246
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier,
247
             NSToolbarFlexibleSpaceItemIdentifier, nil];
248
249
250
251
}

- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
{
252
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
253
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier,
254
             NSToolbarFlexibleSpaceItemIdentifier, nil];
255
256
257
258
}

- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
{
259
260
    return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
             VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, nil];
261
262
263
264
}

- (void)initStrings
{
265
    /* audio */
266
267
268
269
270
271
272
273
274
275
    [_audio_effectsBox setTitle: _NS("Audio Effects")];
    [_audio_enableCheckbox setTitle: _NS("Enable audio")];
    [_audio_generalBox setTitle: _NS("General Audio")];
    [_audio_langLabel setStringValue: _NS("Preferred Audio language")];
    [_audio_lastCheckbox setTitle: _NS("Enable Last.fm submissions")];
    [_audio_lastpwdLabel setStringValue: _NS("Password")];
    [_audio_lastuserLabel setStringValue: _NS("Username")];
    [_audio_visualLabel setStringValue: _NS("Visualization")];
    [_audio_autosavevol_yesButtonCell setTitle: _NS("Keep audio level between sessions")];
    [_audio_autosavevol_noButtonCell setTitle: _NS("Always reset audio start level to:")];
276
277

    /* hotkeys */
278
279
280
281
282
283
284
285
    [_hotkeys_changeButton setTitle: _NS("Change")];
    [_hotkeys_change_win setTitle: _NS("Change Hotkey")];
    [_hotkeys_change_cancelButton setTitle: _NS("Cancel")];
    [_hotkeys_change_okButton setTitle: _NS("OK")];
    [_hotkeys_clearButton setTitle: _NS("Clear")];
    [_hotkeysLabel setStringValue: _NS("Select an action to change the associated hotkey:")];
    [[[_hotkeys_listbox tableColumnWithIdentifier: @"action"] headerCell] setStringValue: _NS("Action")];
    [[[_hotkeys_listbox tableColumnWithIdentifier: @"shortcut"] headerCell] setStringValue: _NS("Shortcut")];
286
287

    /* input */
288
289
290
291
292
293
294
295
296
    [_input_recordBox setTitle: _NS("Record directory or filename")];
    [_input_recordButton setTitle: _NS("Browse...")];
    [_input_recordButton setToolTip: _NS("Directory or filename where the records will be stored")];
    [_input_aviLabel setStringValue: _NS("Repair AVI Files")];
    [_input_cachelevelLabel setStringValue: _NS("Default Caching Level")];
    [_input_cachingBox setTitle: _NS("Caching")];
    [_input_cachelevel_customLabel setStringValue: _NS("Use the complete preferences to configure custom caching values for each access module.")];
    [_input_muxBox setTitle: _NS("Codecs / Muxers")];
    [_input_netBox setTitle: _NS("Network")];
297
    [_input_hardwareAccelerationCheckbox setTitle: _NS("Enable hardware acceleration")];
298
299
300
    [_input_postprocLabel setStringValue: _NS("Post-Processing Quality")];
    [_input_skipLoopLabel setStringValue: _NS("Skip the loop filter for H.264 decoding")];
    [_input_urlhandlerButton setTitle: _NS("Edit default application settings for network protocols")];
301
    [_input_skipFramesCheckbox setTitle: _NS("Skip frames")];
302
303

    /* url handler */
304
305
306
307
    [_urlhandler_titleLabel setStringValue: _NS("Open network streams using the following protocols")];
    [_urlhandler_subtitleLabel setStringValue: _NS("Note that these are system-wide settings.")];
    [_urlhandler_saveButton setTitle: _NS("Save")];
    [_urlhandler_cancelButton setTitle: _NS("Cancel")];
308
309

    /* interface */
310
    [_intf_generalSettingsBox setTitle:_NS("General settings")];
311
    [_intf_languageLabel setStringValue: _NS("Language")];
312
313
314

    [_intf_playbackControlBox setTitle:_NS("Playback control")];
    [_intf_continueplaybackLabel setStringValue:_NS("Continue playback")];
315
316
    [_intf_appleremoteCheckbox setTitle: _NS("Control playback with the Apple Remote")];
    [_intf_mediakeysCheckbox setTitle: _NS("Control playback with media keys")];
317
    [_intf_appleremote_sysvolCheckbox setTitle: _NS("Control system volume with the Apple Remote")];
Goran Dokic's avatar
Goran Dokic committed
318
    [_intf_statusIconCheckbox setTitle: _NS("Display VLC status menu icon")];
319
320
321
322
323
324

    [_intf_playbackBehaviourBox setTitle:_NS("Playback behaviour")];
    [_intf_enableNotificationsCheckbox setTitle: _NS("Enable notifications on playlist item change")];
    [_intf_pauseitunesLabel setStringValue:_NS("Control external music players")];

    [_intf_networkBox setTitle: _NS("Privacy / Network Interaction")];
325
326
327
    [_intf_artCheckbox setTitle: _NS("Allow metadata network access")];
    [_intf_updateCheckbox setTitle: _NS("Automatically check for updates")];
    [_intf_last_updateLabel setStringValue: @""];
328
329

    [_intf_luahttpBox setTitle:_NS("HTTP web interface")];
330
    [_intf_enableluahttpCheckbox setTitle: _NS("Enable HTTP web interface")];
331
    [_intf_luahttppwdLabel setStringValue:_NS("Password")];
332

333
    /* Subtitles and OSD */
334
335
336
337
338
339
340
341
342
343
344
345
346
347
    [_osd_encodingLabel setStringValue: _NS("Default Encoding")];
    [_osd_fontBox setTitle: _NS("Display Settings")];
    [_osd_fontButton setTitle: _NS("Choose...")];
    [_osd_font_colorLabel setStringValue: _NS("Font color")];
    [_osd_font_sizeLabel setStringValue: _NS("Font size")];
    [_osd_fontLabel setStringValue: _NS("Font")];
    [_osd_langBox setTitle: _NS("Subtitle languages")];
    [_osd_langLabel setStringValue: _NS("Preferred subtitle language")];
    [_osd_osdBox setTitle: _NS("On Screen Display")];
    [_osd_osdCheckbox setTitle: _NS("Enable OSD")];
    [_osd_opacityLabel setStringValue: _NS("Opacity")];
    [_osd_forceboldCheckbox setTitle: _NS("Force bold")];
    [_osd_outline_colorLabel setStringValue: _NS("Outline color")];
    [_osd_outline_thicknessLabel setStringValue: _NS("Outline thickness")];
348
349

    /* video */
350
    [_video_enableCheckbox setTitle: _NS("Enable video")];
351
352
353
    [_video_displayBox setTitle: _NS("Display")];
    [_video_embeddedCheckbox setTitle: _NS("Show video within the main window")];
    [_video_pauseWhenMinimizedCheckbox setTitle:_NS("Pause the video playback when minimized")];
354
    [_video_onTopCheckbox setTitle: _NS("Float on Top")];
355
356
357
358
359
360
361
362
    [_video_videodecoCheckbox setTitle: _NS("Window decorations")];

    [_video_fullscreenBox setTitle:_NS("Fullscreen settings")];
    [_video_startInFullscreenCheckbox setTitle:_NS("Start in fullscreen")];
    [_video_blackScreenCheckbox setTitle: _NS("Black screens in Fullscreen mode")];
    [_video_nativeFullscreenCheckbox setTitle: _NS("Use the native fullscreen mode")];
    [_video_deviceLabel setStringValue: _NS("Fullscreen Video Device")];

363
364
365
366
367
368
369
370
371
    [_video_snapBox setTitle: _NS("Video snapshots")];
    [_video_snap_folderButton setTitle: _NS("Browse...")];
    [_video_snap_folderLabel setStringValue: _NS("Folder")];
    [_video_snap_formatLabel setStringValue: _NS("Format")];
    [_video_snap_prefixLabel setStringValue: _NS("Prefix")];
    [_video_snap_seqnumCheckbox setTitle: _NS("Sequential numbering")];
    [_video_deinterlaceLabel setStringValue: _NS("Deinterlace")];
    [_video_deinterlace_modeLabel setStringValue: _NS("Deinterlace mode")];
    [_video_videoBox setTitle: _NS("Video")];
372

373
    /* generic stuff */
374
375
376
377
    [_showAllButton setTitle: _NS("Show All")];
    [_cancelButton setTitle: _NS("Cancel")];
    [_resetButton setTitle: _NS("Reset All")];
    [_saveButton setTitle: _NS("Save")];
378
    [self.window setTitle: _NS("Preferences")];
379
380
}

381
382
/* TODO: move this part to core */
#define config_GetLabel(a,b) __config_GetLabel(VLC_OBJECT(a),b)
383
static inline const char * __config_GetLabel(vlc_object_t *p_this, const char *psz_name)
384
{
385
    module_config_t *p_config = config_FindConfig(psz_name);
386
387

    /* sanity checks */
388
389
    if (!p_config) {
        msg_Err(p_this, "option %s does not exist", psz_name);
390
391
392
        return NULL;
    }

393
    if (p_config->psz_longtext)
394
        return p_config->psz_longtext;
395
    else if (p_config->psz_text)
396
397
        return p_config->psz_text;
    else
398
        msg_Warn(p_this, "option %s does not include any help", psz_name);
399
400
401
402

    return NULL;
}

403
404
405
#pragma mark -
#pragma mark Setup controls

406
407
408
409
410
- (void)setupButton: (NSPopUpButton *)object forStringList: (const char *)name
{
    module_config_t *p_item;

    [object removeAllItems];
411
    p_item = config_FindConfig(name);
412
    /* serious problem, if no item found */
413
    assert(p_item);
414

415
    char **values, **texts;
416
    ssize_t count = config_GetPszChoices(name, &values, &texts);
417
418
419
420
421
422
    if (count < 0) {
        msg_Err(p_intf, "Cannot get choices for %s", name);
        return;
    }
    for (ssize_t i = 0; i < count && texts; i++) {
        if (texts[i] == NULL || values[i] == NULL)
423
            continue;
424
425
426
427

        if (strcmp(texts[i], "") != 0) {
            NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""];
            [mi setRepresentedObject: toNSStr(values[i])];
428
            [[object menu] addItem:mi];
429
430
431
432
433

            if (p_item->value.psz && !strcmp(p_item->value.psz, values[i]))
                [object selectItem: [object lastItem]];
        } else {
            [[object menu] addItem: [NSMenuItem separatorItem]];
434
        }
435
436
437

        free(texts[i]);
        free(values[i]);
438
    }
439
440
441
442
443
444
445
446
447
448
449
450

    free(texts);
    free(values);

    if (p_item->psz_longtext)
        [object setToolTip: _NS(p_item->psz_longtext)];
}

// just for clarification that this is a module list
- (void)setupButton: (NSPopUpButton *)object forModuleList: (const char *)name
{
    [self setupButton: object forStringList: name];
451
452
453
454
455
456
457
}

- (void)setupButton: (NSPopUpButton *)object forIntList: (const char *)name
{
    module_config_t *p_item;

    [object removeAllItems];
458
    p_item = config_FindConfig(name);
459
460

    /* serious problem, if no item found */
461
    assert(p_item);
462

463
464
    int64_t *values;
    char **texts;
465
    ssize_t count = config_GetIntChoices(name, &values, &texts);
466
467
    for (ssize_t i = 0; i < count; i++) {
        NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""];
468
        [mi setRepresentedObject:[NSNumber numberWithInteger:values[i]]];
469
        [[object menu] addItem:mi];
470
471

        if (p_item->value.i == values[i])
472
            [object selectItem:[object lastItem]];
473
474

        free(texts[i]);
475
    }
476
477
478
479
    free(texts);

    if (p_item->psz_longtext)
        [object setToolTip: _NS(p_item->psz_longtext)];
480
481
}

482
483
- (void)setupButton: (NSButton *)object forBoolValue: (const char *)name
{
484
    [object setState: config_GetInt(name)];
Rémi Denis-Courmont's avatar
Rémi Denis-Courmont committed
485
    [object setToolTip: _NS(config_GetLabel(p_intf, name))];
486
487
}

488
- (void)setupField:(NSTextField *)object forOption:(const char *)psz_option
489
{
490
    char *psz_tmp = config_GetPsz(psz_option);
491
    [object setStringValue: toNSStr(psz_tmp)];
492
    [object setToolTip: _NS(config_GetLabel(p_intf, psz_option))];
493
    free(psz_tmp);
494
495
}

496
497
- (BOOL)hasModule:(NSString *)moduleName inConfig:(NSString *)config
{
498
    char *value = config_GetPsz([config UTF8String]);
499
500
501
502
503
504
505
506
    NSString *modules = toNSStr(value);
    free(value);

    return [[modules componentsSeparatedByString:@":"] containsObject:moduleName];
}

- (void)changeModule:(NSString *)moduleName inConfig:(NSString *)config enable:(BOOL)enable
{
507
    char *value = config_GetPsz([config UTF8String]);
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
    NSString *modules = toNSStr(value);
    free(value);

    NSMutableArray *components = [[modules componentsSeparatedByString:@":"] mutableCopy];
    if (enable) {
        if (![components containsObject:moduleName]) {
            [components addObject:moduleName];
        }
    } else {
        [components removeObject:moduleName];
    }

    // trim empty entries
    [components removeObject:@""];

523
    config_PutPsz([config UTF8String], [[components componentsJoinedByString:@":"] UTF8String]);
524
525
}

526
527
- (void)resetControls
{
528
529
    int i = 0;
    NSInteger y = 0;
530
531
532
533

    /**********************
     * interface settings *
     **********************/
534
535
536
    NSUInteger sel = 0;
    const char *pref = NULL;
    pref = [[[NSUserDefaults standardUserDefaults] objectForKey:@"language"] UTF8String];
537
538
    for (int x = 0; x < ARRAY_SIZE(language_map); x++) {
        [_intf_languagePopup addItemWithTitle:toNSStr(language_map[x].name)];
539
        if (pref) {
540
            if (!strcmp(language_map[x].iso, pref))
541
542
543
                sel = x;
        }
    }
544
    [_intf_languagePopup selectItemAtIndex:sel];
545

546
    [self setupButton:_intf_continueplaybackPopup forIntList: "macosx-continue-playback"];
547
548
549
550
551
552
553
    if (!var_InheritBool(p_intf, "macosx-recentitems")) {
        [_intf_continueplaybackPopup setEnabled: NO];
        [_intf_continueplaybackPopup setToolTip: _NS("Media files cannot be resumed because keeping recent media items is disabled.")];
    } else {
        [_intf_continueplaybackPopup setEnabled: YES];
    }

554
555
    [self setupButton:_intf_appleremoteCheckbox forBoolValue: "macosx-appleremote"];
    [self setupButton:_intf_appleremote_sysvolCheckbox forBoolValue: "macosx-appleremote-sysvol"];
Goran Dokic's avatar
Goran Dokic committed
556
    [self setupButton:_intf_statusIconCheckbox forBoolValue: "macosx-statusicon"];
557
    [self setupButton:_intf_mediakeysCheckbox forBoolValue: "macosx-mediakeys"];
558

559
560
    [self setupButton:_video_nativeFullscreenCheckbox forBoolValue: "macosx-nativefullscreenmode"];
    [self setupButton:_video_embeddedCheckbox forBoolValue: "embedded-video"];
561

562
    [self setupButton:_intf_pauseitunesPopup forIntList: "macosx-control-itunes"];
563

564
    [self setupButton:_intf_artCheckbox forBoolValue: "metadata-network-access"];
565

566

567
#ifdef HAVE_SPARKLE
568
    if ([[SUUpdater sharedUpdater] lastUpdateCheckDate] != NULL)
569
        [_intf_last_updateLabel setStringValue: [NSString stringWithFormat: _NS("Last check on: %@"), [[[SUUpdater sharedUpdater] lastUpdateCheckDate] descriptionWithLocale: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]]];
570
    else
571
        [_intf_last_updateLabel setStringValue: _NS("No check was performed yet.")];
572
573
#endif

574
575
576
577
578
579
580
    BOOL growlEnabled = [self hasModule:@"growl" inConfig:@"control"];
    [_intf_enableNotificationsCheckbox setState: growlEnabled ? NSOnState : NSOffState];

    BOOL httpEnabled = [self hasModule:@"http" inConfig:@"extraintf"];
    [_intf_enableluahttpCheckbox setState: httpEnabled ? NSOnState : NSOffState];
    _intf_luahttppwdTextField.enabled = httpEnabled;

581
    [self setupField:_intf_luahttppwdTextField forOption: "http-password"];
582
583
584
585

    /******************
     * audio settings *
     ******************/
586
    [self setupButton:_audio_enableCheckbox forBoolValue: "audio"];
587

588
    if (config_GetInt("volume-save")) {
589
590
591
592
        [_audio_autosavevol_yesButtonCell setState: NSOnState];
        [_audio_autosavevol_noButtonCell setState: NSOffState];
        [_audio_volTextField setEnabled: NO];
        [_audio_volSlider setEnabled: NO];
593

594
595
        [_audio_volSlider setIntValue: 100];
        [_audio_volTextField setIntValue: 100];
596
    } else {
597
598
599
600
        [_audio_autosavevol_yesButtonCell setState: NSOffState];
        [_audio_autosavevol_noButtonCell setState: NSOnState];
        [_audio_volTextField setEnabled: YES];
        [_audio_volSlider setEnabled: YES];
601

602
        i = (int)var_InheritInteger(p_intf, "auhal-volume");
603
        i = i * 200. / AOUT_VOLUME_MAX;
604
605
        [_audio_volSlider setIntValue: i];
        [_audio_volTextField setIntValue: i];
606
    }
607

608
    [self setupField:_audio_langTextField forOption: "audio-language"];
609

610
    [self setupButton:_audio_visualPopup forModuleList: "audio-visual"];
611

612
    /* Last.FM is optional */
613
    if (module_exists("audioscrobbler")) {
614
615
        [self setupField:_audio_lastuserTextField forOption:"lastfm-username"];
        [self setupField:_audio_lastpwdSecureTextField forOption:"lastfm-password"];
616

617
        if (config_ExistIntf("audioscrobbler")) {
618
619
620
            [_audio_lastCheckbox setState: NSOnState];
            [_audio_lastuserTextField setEnabled: YES];
            [_audio_lastpwdSecureTextField setEnabled: YES];
621
        } else {
622
623
624
            [_audio_lastCheckbox setState: NSOffState];
            [_audio_lastuserTextField setEnabled: NO];
            [_audio_lastpwdSecureTextField setEnabled: NO];
625
        }
626
    } else
627
        [_audio_lastCheckbox setEnabled: NO];
628
629
630
631

    /******************
     * video settings *
     ******************/
632
    [self setupButton:_video_enableCheckbox forBoolValue: "video"];
633
    [self setupButton:_video_startInFullscreenCheckbox forBoolValue: "fullscreen"];
634
    [self setupButton:_video_onTopCheckbox forBoolValue: "video-on-top"];
635
    [self setupButton:_video_blackScreenCheckbox forBoolValue: "macosx-black"];
636
    [self setupButton:_video_videodecoCheckbox forBoolValue: "video-deco"];
637
    [self setupButton:_video_pauseWhenMinimizedCheckbox forBoolValue: "macosx-pause-minimized"];
638
639

    [_video_devicePopup removeAllItems];
640
641
    i = 0;
    y = [[NSScreen screens] count];
642
643
    [_video_devicePopup addItemWithTitle: _NS("Default")];
    [[_video_devicePopup lastItem] setTag: 0];
644
    while (i < y) {
645
        NSRect s_rect = [[[NSScreen screens] objectAtIndex:i] frame];
646
        [_video_devicePopup addItemWithTitle:
647
648
         [NSString stringWithFormat: @"%@ %i (%ix%i)", _NS("Screen"), i+1,
                   (int)s_rect.size.width, (int)s_rect.size.height]];
649
        [[_video_devicePopup lastItem] setTag: (int)[[[NSScreen screens] objectAtIndex:i] displayID]];
650
651
        i++;
    }
652
    [_video_devicePopup selectItemAtIndex: 0];
653
    [_video_devicePopup selectItemWithTag: config_GetInt("macosx-vdev")];
654

655
656
657
658
659
660
    [self setupField:_video_snap_folderTextField forOption:"snapshot-path"];
    [self setupField:_video_snap_prefixTextField forOption:"snapshot-prefix"];
    [self setupButton:_video_snap_seqnumCheckbox forBoolValue: "snapshot-sequential"];
    [self setupButton:_video_snap_formatPopup forStringList: "snapshot-format"];
    [self setupButton:_video_deinterlacePopup forIntList: "deinterlace"];
    [self setupButton:_video_deinterlace_modePopup forStringList: "deinterlace-mode"];
661

662
    // set lion fullscreen mode restrictions
663
    [self enableLionFullscreenMode: [_video_nativeFullscreenCheckbox state]];
664

665
666
667
    /***************************
     * input & codecs settings *
     ***************************/
668
    [self setupField:_input_recordTextField forOption:"input-record-path"];
669
670

    [self setupButton:_input_hardwareAccelerationCheckbox forBoolValue: "videotoolbox"];
671
//    [_input_postprocTextField setIntValue: config_GetInt("postproc-q")];
672
    [_input_postprocTextField setToolTip: _NS(config_GetLabel(p_intf, "postproc-q"))];
673
    [self setupButton:_input_skipFramesCheckbox forBoolValue: "skip-frames"];
674
675
    [self setupButton:_input_aviPopup forIntList: "avi-index"];
    [self setupButton:_input_skipLoopPopup forIntList: "avcodec-skiploopfilter"];
676

677
    [_input_cachelevelPopup removeAllItems];
678
679
680
681
682
683
684
685
686
687
688
689
    NSMenuItem *item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Custom") action:nil keyEquivalent:@""];
    [item setTag: 0];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Lowest Latency") action:nil keyEquivalent:@""];
    [item setTag: 100];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Low Latency") action:nil keyEquivalent:@""];
    [item setTag: 200];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Normal") action:nil keyEquivalent:@""];
    [item setTag: 300];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Higher Latency") action:nil keyEquivalent:@""];
    [item setTag: 500];
    item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Highest Latency") action:nil keyEquivalent:@""];
    [item setTag: 1000];
690

691
    #define TestCaC(name, factor) \
692
    cache_equal = cache_equal && \
693
    (i_cache * factor == config_GetInt(name));
694
695

    /* Select the accurate value of the PopupButton */
696
    bool cache_equal = true;
697
    int i_cache = (int)config_GetInt("file-caching");
698

699
700
701
    TestCaC("network-caching", 10/3);
    TestCaC("disc-caching", 1);
    TestCaC("live-caching", 1);
702
703
704
    if (cache_equal) {
        [_input_cachelevelPopup selectItemWithTag: i_cache];
        [_input_cachelevel_customLabel setHidden: YES];
705
    } else {
706
707
        [_input_cachelevelPopup selectItemWithTitle: _NS("Custom")];
        [_input_cachelevel_customLabel setHidden: NO];
708
    }
709
    #undef TestCaC
710
711
712
713

    /*********************
     * subtitle settings *
     *********************/
714
    [self setupButton:_osd_osdCheckbox forBoolValue: "osd"];
715

716
717
    [self setupButton:_osd_encodingPopup forStringList: "subsdec-encoding"];
    [self setupField:_osd_langTextField forOption: "sub-language" ];
718

719
720
721
    [self setupField:_osd_fontTextField forOption: "freetype-font"];
    [self setupButton:_osd_font_colorPopup forIntList: "freetype-color"];
    [self setupButton:_osd_font_sizePopup forIntList: "freetype-rel-fontsize"];
722
    i = config_GetInt("freetype-opacity") * 100.0 / 255.0 + 0.5;
723
724
725
726
727
728
729
    [_osd_opacityTextField setIntValue: i];
    [_osd_opacitySlider setIntValue: i];
    [_osd_opacitySlider setToolTip: _NS(config_GetLabel(p_intf, "freetype-opacity"))];
    [_osd_opacityTextField setToolTip: [_osd_opacitySlider toolTip]];
    [self setupButton:_osd_forceboldCheckbox forBoolValue: "freetype-bold"];
    [self setupButton:_osd_outline_colorPopup forIntList: "freetype-outline-color"];
    [self setupButton:_osd_outline_thicknessPopup forIntList: "freetype-outline-thickness"];
730

731
732
733
    /********************
     * hotkeys settings *
     ********************/
734
735
736
    _hotkeySettings = [[NSMutableArray alloc] init];
    NSMutableArray *tempArray_desc = [[NSMutableArray alloc] init];
    NSMutableArray *tempArray_names = [[NSMutableArray alloc] init];
737

738
739
    /* Get the main Module */
    module_t *p_main = module_get_main();
740
    assert(p_main);
741
742
    unsigned confsize;
    module_config_t *p_config;
743

744
    p_config = module_config_get (p_main, &confsize);
745

746
    for (size_t i = 0; i < confsize; i++) {
747
        module_config_t *p_item = p_config + i;
748

749
750
751
        if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
           && !strncmp(p_item->psz_name , "key-", 4)
           && !EMPTY_STR(p_item->psz_text)) {
752
            [tempArray_desc addObject: _NS(p_item->psz_text)];
753
            [tempArray_names addObject: toNSStr(p_item->psz_name)];
754
            if (p_item->value.psz)
755
                [_hotkeySettings addObject: toNSStr(p_item->value.psz)];
756
            else
757
                [_hotkeySettings addObject: [NSString string]];
758
        }
759
    }
760
    module_config_free (p_config);
761

762
763
    _hotkeyDescriptions = [[NSArray alloc] initWithArray:tempArray_desc copyItems: YES];
    _hotkeyNames = [[NSArray alloc] initWithArray:tempArray_names copyItems: YES];
764

765
    [_hotkeys_listbox reloadData];
766
767
}

768
769
770
#pragma mark -
#pragma mark General actions

771
772
773
- (void)showSimplePrefs
{
    /* we want to show the interface settings, if no category was chosen */
774
775
    if ([[self.window toolbar] selectedItemIdentifier] == nil) {
        [[self.window toolbar] setSelectedItemIdentifier: VLCIntfSettingToolbarIdentifier];
776
777
        [self showInterfaceSettings];
    }
778

779
    [self resetControls];
780

781
    [self.window makeKeyAndOrderFront: self];
782
783
}

784
785
- (void)showSimplePrefsWithLevel:(NSInteger)i_window_level
{
786
    [self.window setLevel: i_window_level];
787
788
789
    [self showSimplePrefs];
}

790
791
- (IBAction)buttonAction:(id)sender
{
792
    if (sender == _cancelButton) {
793
        [[NSFontPanel sharedFontPanel] close];
794
        [self.window orderOut: sender];
795
    } else if (sender == _saveButton) {
796
        [self saveChangedSettings];
797
        [[NSFontPanel sharedFontPanel] close];
798
        [self.window orderOut: sender];
799
    } else if (sender == _showAllButton) {
800
801
        [self.window orderOut: self];
        [[[VLCMain sharedInstance] preferences] showPrefsWithLevel:[self.window level]];
802
803
    } else
        msg_Warn(p_intf, "unknown buttonAction sender");
804
805
}

806
807
808
809
810
811
812
813
814
815
816
817
818
- (IBAction)resetPreferences:(NSControl *)sender
{
    NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"),
                                   _NS("Continue"), nil, [sender window], self,
                                   @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil, @"%@",
                                   _NS("This will reset VLC media player's preferences.\n\n"
                                       "Note that VLC will restart during the process, so your current "
                                       "playlist will be emptied and eventual playback, streaming or "
                                       "transcoding activities will stop immediately.\n\n"
                                       "The Media Library will not be affected.\n\n"
                                       "Are you sure you want to continue?"));
}

819
- (void)sheetDidEnd:(NSWindow *)o_sheet
820
821
822
         returnCode:(int)i_return
        contextInfo:(void *)o_context
{
823
    if (i_return == NSAlertAlternateReturn) {
824
        /* reset VLC's config */
825
        config_ResetAll();
826
        [self resetControls];
827
828

        /* force config file creation, since libvlc won't exit normally */
829
        config_SaveConfigFile(p_intf);
830
831

        /* reset OS X defaults */
832
        [[VLCMain sharedInstance] resetAndReinitializeUserDefaults];
833
834
835
836
837
838
839
840
841
842

        /* Relaunch now */
        const char * path = [[[NSBundle mainBundle] executablePath] UTF8String];

        /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */
        if (fork() != 0) {
            exit(0);
            return;
        }
        execl(path, path, NULL);
843
844
845
    }
}

846
static inline void save_int_list(intf_thread_t * p_intf, id object, const char * name)
847
{
848
849
850
    NSNumber *p_valueobject = (NSNumber *)[[object selectedItem] representedObject];
    if (p_valueobject) {
        assert([p_valueobject isKindOfClass:[NSNumber class]]);
851
        config_PutInt(name, [p_valueobject intValue]);
852
    }
853
854
}

855
static inline void save_string_list(intf_thread_t * p_intf, id object, const char * name)
856
{
857
858
859
    NSString *p_stringobject = (NSString *)[[object selectedItem] representedObject];
    if (p_stringobject) {
        assert([p_stringobject isKindOfClass:[NSString class]]);
860
        config_PutPsz(name, [p_stringobject UTF8String]);
861
    }
862
863
}

864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
+ (BOOL)updateRightToLeftSettings
{
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    NSString *isoCode = [defaults stringForKey:@"language"];

    if (!isoCode || [isoCode isEqualToString:@"auto"]) {
        // Automatic handling of right to left
        [defaults removeObjectForKey:@"NSForceRightToLeftWritingDirection"];
        [defaults removeObjectForKey:@"AppleTextDirection"];
    } else {
        for(int i = 0; i < ARRAY_SIZE(language_map); i++) {
            if (!strcmp(language_map[i].iso, [isoCode UTF8String])) {
                [defaults setBool:language_map[i].isRightToLeft forKey:@"NSForceRightToLeftWritingDirection"];
                [defaults setBool:language_map[i].isRightToLeft forKey:@"AppleTextDirection"];
                return YES;
            }
        }
    }

    return NO;
}

886
887
- (void)saveChangedSettings
{
888
#define SaveIntList(object, name) save_int_list(p_intf, object, name)
889

890
#define SaveStringList(object, name) save_string_list(p_intf, object, name)
891
#define SaveModuleList(object, name) SaveStringList(object, name)
892

893
894
895
    /**********************
     * interface settings *
     **********************/
896
897
    if (_intfSettingChanged) {
        NSUInteger index = [_intf_languagePopup indexOfSelectedItem];
898
        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
899
        [defaults setObject:toNSStr(language_map[index].iso) forKey:@"language"];
900
        [VLCSimplePrefsController updateRightToLeftSettings];
901
902
        [defaults synchronize];

903
        config_PutInt("metadata-network-access", [_intf_artCheckbox state]);
904

905
906
907
908
        config_PutInt("macosx-appleremote", [_intf_appleremoteCheckbox state]);
        config_PutInt("macosx-appleremote-sysvol", [_intf_appleremote_sysvolCheckbox state]);
        config_PutInt("macosx-statusicon", [_intf_statusIconCheckbox state]);
        config_PutInt("macosx-mediakeys", [_intf_mediakeysCheckbox state]);
909

910
911
912
        [self changeModule:@"growl" inConfig:@"control" enable:[_intf_enableNotificationsCheckbox state] == NSOnState];

        [self changeModule:@"http" inConfig:@"extraintf" enable:[_intf_enableluahttpCheckbox state] == NSOnState];
913
        config_PutPsz("http-password", [[_intf_luahttppwdTextField stringValue] UTF8String]);