1/***************************************************************************** 2* VLCSimplePrefsController.m: Simple Preferences for Mac OS X 3***************************************************************************** 4* Copyright (C) 2008-2018 VLC authors and VideoLAN 5* $Id: 948ebef0a7e91efe7e386495b4321f59ecdcc2fc $ 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#ifdef HAVE_CONFIG_H 24# include "config.h" 25#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" 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 48static struct { 49 const char iso[6]; 50 const char name[34]; 51 BOOL isRightToLeft; 52 53} const language_map[] = { 54 { "auto", N_("Auto"), NO }, 55 { "en", "American English", NO }, 56 { "ar", "عربي", YES }, 57 { "an", "Aragonés", NO }, 58 { "as_IN", "অসমীয়া", NO }, 59 { "ast", "Asturianu", NO }, 60 { "be", "беларуская мова", NO }, 61 { "brx", "बर'/बड़", NO }, 62 { "bn", "বাংলা", NO }, 63 { "pt_BR", "Português Brasileiro", NO }, 64 { "en_GB", "British English", NO }, 65 { "el", "Νέα Ελληνικά", NO }, 66 { "bg", "български език", NO }, 67 { "ca", "Català", NO }, 68 { "zh_TW", "正體中文", NO }, 69 { "co", "Corsu", NO }, 70 { "cs", "Čeština", NO }, 71 { "cy", "Cymraeg", NO }, 72 { "da", "Dansk", NO }, 73 { "nl", "Nederlands", NO }, 74 { "fi", "Suomi", NO }, 75 { "et", "eesti keel", NO }, 76 { "eu", "Euskara", NO }, 77 { "fr", "Français", NO }, 78 { "ga", "Gaeilge", NO }, 79 { "gd", "Gàidhlig", NO }, 80 { "gl", "Galego", NO }, 81 { "gu", "ગુજરાતી", NO }, 82 { "de", "Deutsch", NO }, 83 { "he", "עברית", YES }, 84 { "hr", "hrvatski", NO }, 85 { "kn", "ಕನ್ನಡ", NO }, 86 { "lv", "Latviešu valoda", NO }, 87 { "hu", "Magyar", NO }, 88 { "mr", "मराठी", NO }, 89 { "is", "íslenska", NO }, 90 { "id", "Bahasa Indonesia", NO }, 91 { "it", "Italiano", NO }, 92 { "ja", "日本語", NO }, 93 { "ko", "한국어", NO }, 94 { "lt", "lietuvių", NO }, 95 { "ms", "Melayu", NO }, 96 { "nb", "Bokmål", NO }, 97 { "nn", "Nynorsk", NO }, 98 { "kk", "Қазақ тілі", NO }, 99 { "km", "ភាសាខ្មែរ", NO }, 100 { "ne", "नेपाली", NO }, 101 { "oc", "Occitan", NO }, 102 { "pl", "Polski", NO }, 103 { "pt_PT", "Português", NO }, 104 { "pa", "ਪੰਜਾਬੀ", NO }, 105 { "ro", "Română", NO }, 106 { "ru", "Русский", NO }, 107 { "zh_CN", "简体中文", NO }, 108 { "si", "සිංහල", NO }, 109 { "sr", "српски", NO }, 110 { "sk", "Slovensky", NO }, 111 { "sl", "slovenščina", NO }, 112 { "es", "Español", NO }, 113 { "es_MX", "Español Mexicano", NO }, 114 { "sv", "Svenska", NO }, 115 { "th", "ภาษาไทย", NO }, 116 { "tr", "Türkçe", NO }, 117 { "uk", "украї́нська мо́ва", NO }, 118 { "vi", "tiếng Việt", NO }, 119 { "wa", "Walon", NO } 120}; 121 122static NSString* VLCSPrefsToolbarIdentifier = @"Our Simple Preferences Toolbar Identifier"; 123static NSString* VLCIntfSettingToolbarIdentifier = @"Intf Settings Item Identifier"; 124static NSString* VLCAudioSettingToolbarIdentifier = @"Audio Settings Item Identifier"; 125static NSString* VLCVideoSettingToolbarIdentifier = @"Video Settings Item Identifier"; 126static NSString* VLCOSDSettingToolbarIdentifier = @"Subtitles Settings Item Identifier"; 127static NSString* VLCInputSettingToolbarIdentifier = @"Input Settings Item Identifier"; 128static NSString* VLCHotkeysSettingToolbarIdentifier = @"Hotkeys Settings Item Identifier"; 129 130@interface VLCSimplePrefsController() <NSToolbarDelegate, NSWindowDelegate> 131{ 132 BOOL _audioSettingChanged; 133 BOOL _intfSettingChanged; 134 BOOL _videoSettingChanged; 135 BOOL _osdSettingChanged; 136 BOOL _inputSettingChanged; 137 BOOL _hotkeyChanged; 138 139 NSOpenPanel *_selectFolderPanel; 140 NSArray *_hotkeyDescriptions; 141 NSArray *_hotkeyNames; 142 NSArray *_hotkeysNonUseableKeys; 143 NSMutableArray *_hotkeySettings; 144 NSString *_keyInTransition; 145 146 intf_thread_t *p_intf; 147} 148@end 149 150@implementation VLCSimplePrefsController 151 152#pragma mark Initialisation 153 154- (id)init 155{ 156 self = [super initWithWindowNibName:@"SimplePreferences"]; 157 if (self) { 158 p_intf = getIntf(); 159 } 160 161 return self; 162} 163 164- (void)windowDidLoad 165{ 166 [self initStrings]; 167 168#ifdef HAVE_SPARKLE 169 [_intf_updateCheckbox bind:@"value" 170 toObject:[SUUpdater sharedUpdater] 171 withKeyPath:@"automaticallyChecksForUpdates" 172 options:nil]; 173#else 174 [_intf_updateCheckbox setState:NSOffState]; 175 [_intf_updateCheckbox setEnabled:NO]; 176#endif 177 178 /* setup the toolbar */ 179 NSToolbar * toolbar = [[NSToolbar alloc] initWithIdentifier: VLCSPrefsToolbarIdentifier]; 180 [toolbar setAllowsUserCustomization: NO]; 181 [toolbar setAutosavesConfiguration: NO]; 182 [toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel]; 183 [toolbar setSizeMode: NSToolbarSizeModeRegular]; 184 [toolbar setDelegate: self]; 185 [self.window setToolbar:toolbar]; 186 187 [self.window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary]; 188 [self.window setHidesOnDeactivate:YES]; 189 190 [_hotkeys_listbox setTarget:self]; 191 [_hotkeys_listbox setDoubleAction:@selector(hotkeyTableDoubleClick:)]; 192 193 /* setup useful stuff */ 194 _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]; 195 196 // Workaround for Mac OS X Lion, which does not apply the same constraints when set in IB 197 NSView *clipView = _contentView.superview; 198 199 NSDictionary *views = @{ @"view": _contentView }; 200 NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:views]; 201 [clipView addConstraints:constraints]; 202} 203 204#define CreateToolbarItem(name, desc, img, sel) \ 205 toolbarItem = create_toolbar_item(itemIdent, name, desc, img, self, @selector(sel)); 206static inline NSToolbarItem * 207create_toolbar_item(NSString *itemIdent, NSString *name, NSString *desc, NSString *img, id target, SEL selector) 208{ 209 NSToolbarItem *toolbarItem = [[NSToolbarItem alloc] initWithItemIdentifier: itemIdent]; 210 211 [toolbarItem setLabel:name]; 212 [toolbarItem setPaletteLabel:desc]; 213 214 [toolbarItem setToolTip:desc]; 215 [toolbarItem setImage:[NSImage imageNamed:img]]; 216 217 [toolbarItem setTarget:target]; 218 [toolbarItem setAction:selector]; 219 220 [toolbarItem setEnabled:YES]; 221 [toolbarItem setAutovalidates:YES]; 222 223 return toolbarItem; 224} 225 226- (NSToolbarItem *) toolbar:(NSToolbar *)toolbar 227 itemForItemIdentifier:(NSString *)itemIdent 228 willBeInsertedIntoToolbar:(BOOL)willBeInserted 229{ 230 NSToolbarItem *toolbarItem = nil; 231 232 if ([itemIdent isEqual: VLCIntfSettingToolbarIdentifier]) { 233 CreateToolbarItem(_NS("Interface"), _NS("Interface Settings"), @"VLCInterfaceCone", showInterfaceSettings); 234 } else if ([itemIdent isEqual: VLCAudioSettingToolbarIdentifier]) { 235 CreateToolbarItem(_NS("Audio"), _NS("Audio Settings"), @"VLCAudioCone", showAudioSettings); 236 } else if ([itemIdent isEqual: VLCVideoSettingToolbarIdentifier]) { 237 CreateToolbarItem(_NS("Video"), _NS("Video Settings"), @"VLCVideoCone", showVideoSettings); 238 } else if ([itemIdent isEqual: VLCOSDSettingToolbarIdentifier]) { 239 CreateToolbarItem(_NS(SUBPIC_TITLE), _NS("Subtitle & On Screen Display Settings"), @"VLCSubtitleCone", showOSDSettings); 240 } else if ([itemIdent isEqual: VLCInputSettingToolbarIdentifier]) { 241 CreateToolbarItem(_NS(INPUT_TITLE), _NS("Input & Codec Settings"), @"VLCInputCone", showInputSettings); 242 } else if ([itemIdent isEqual: VLCHotkeysSettingToolbarIdentifier]) { 243 CreateToolbarItem(_NS("Hotkeys"), _NS("Hotkeys settings"), @"VLCHotkeysCone", showHotkeySettings); 244 } 245 246 return toolbarItem; 247} 248 249- (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar 250{ 251 return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, 252 VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, 253 NSToolbarFlexibleSpaceItemIdentifier, nil]; 254} 255 256- (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar 257{ 258 return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, 259 VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, 260 NSToolbarFlexibleSpaceItemIdentifier, nil]; 261} 262 263- (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar 264{ 265 return [NSArray arrayWithObjects:VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier, 266 VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, nil]; 267} 268 269- (void)initStrings 270{ 271 /* audio */ 272 [_audio_effectsBox setTitle: _NS("Audio Effects")]; 273 [_audio_enableCheckbox setTitle: _NS("Enable audio")]; 274 [_audio_generalBox setTitle: _NS("General Audio")]; 275 [_audio_langLabel setStringValue: _NS("Preferred Audio language")]; 276 [_audio_lastCheckbox setTitle: _NS("Enable Last.fm submissions")]; 277 [_audio_lastpwdLabel setStringValue: _NS("Password")]; 278 [_audio_lastuserLabel setStringValue: _NS("Username")]; 279 [_audio_visualLabel setStringValue: _NS("Visualization")]; 280 [_audio_autosavevol_yesButtonCell setTitle: _NS("Keep audio level between sessions")]; 281 [_audio_autosavevol_noButtonCell setTitle: _NS("Always reset audio start level to:")]; 282 283 /* hotkeys */ 284 [_hotkeys_changeButton setTitle: _NS("Change")]; 285 [_hotkeys_change_win setTitle: _NS("Change Hotkey")]; 286 [_hotkeys_change_cancelButton setTitle: _NS("Cancel")]; 287 [_hotkeys_change_okButton setTitle: _NS("OK")]; 288 [_hotkeys_clearButton setTitle: _NS("Clear")]; 289 [_hotkeysLabel setStringValue: _NS("Select an action to change the associated hotkey:")]; 290 [[[_hotkeys_listbox tableColumnWithIdentifier: @"action"] headerCell] setStringValue: _NS("Action")]; 291 [[[_hotkeys_listbox tableColumnWithIdentifier: @"shortcut"] headerCell] setStringValue: _NS("Shortcut")]; 292 293 /* input */ 294 [_input_recordBox setTitle: _NS("Record directory or filename")]; 295 [_input_recordButton setTitle: _NS("Browse...")]; 296 [_input_recordButton setToolTip: _NS("Directory or filename where the records will be stored")]; 297 [_input_aviLabel setStringValue: _NS("Repair AVI Files")]; 298 [_input_cachelevelLabel setStringValue: _NS("Default Caching Level")]; 299 [_input_cachingBox setTitle: _NS("Caching")]; 300 [_input_cachelevel_customLabel setStringValue: _NS("Use the complete preferences to configure custom caching values for each access module.")]; 301 [_input_muxBox setTitle: _NS("Codecs / Muxers")]; 302 [_input_netBox setTitle: _NS("Network")]; 303 [_input_hardwareAccelerationCheckbox setTitle: _NS("Hardware decoding")]; 304 [_input_postprocLabel setStringValue: _NS("Post-Processing Quality")]; 305 [_input_skipLoopLabel setStringValue: _NS("Skip the loop filter for H.264 decoding")]; 306 [_input_urlhandlerButton setTitle: _NS("Edit default application settings for network protocols")]; 307 [_input_skipFramesCheckbox setTitle: _NS("Skip frames")]; 308 309 /* url handler */ 310 [_urlhandler_titleLabel setStringValue: _NS("Open network streams using the following protocols")]; 311 [_urlhandler_subtitleLabel setStringValue: _NS("Note that these are system-wide settings.")]; 312 [_urlhandler_saveButton setTitle: _NS("Save")]; 313 [_urlhandler_cancelButton setTitle: _NS("Cancel")]; 314 315 /* interface */ 316 [_intf_generalSettingsBox setTitle:_NS("General settings")]; 317 [_intf_languageLabel setStringValue: _NS("Language")]; 318 [_intf_styleLabel setStringValue: _NS("Interface style")]; 319 [_intf_style_darkButtonCell setTitle: _NS("Dark")]; 320 [_intf_style_brightButtonCell setTitle: _NS("Bright")]; 321 322 [_intf_playbackControlBox setTitle:_NS("Playback control")]; 323 [_intf_continueplaybackLabel setStringValue:_NS("Continue playback")]; 324 [_intf_appleremoteCheckbox setTitle: _NS("Control playback with the Apple Remote")]; 325 [_intf_mediakeysCheckbox setTitle: _NS("Control playback with media keys")]; 326 [_intf_appleremote_sysvolCheckbox setTitle: _NS("Control system volume with the Apple Remote")]; 327 [_intf_statusIconCheckbox setTitle: _NS("Display VLC status menu icon")]; 328 329 [_intf_playbackBehaviourBox setTitle:_NS("Playback behaviour")]; 330 [_intf_enableNotificationsCheckbox setTitle: _NS("Enable notifications on playlist item change")]; 331 [_intf_pauseitunesLabel setStringValue:_NS("Control external music players")]; 332 333 [_intf_networkBox setTitle: _NS("Privacy / Network Interaction")]; 334 [_intf_artCheckbox setTitle: _NS("Allow metadata network access")]; 335 [_intf_updateCheckbox setTitle: _NS("Automatically check for updates")]; 336 [_intf_last_updateLabel setStringValue: @""]; 337 338 [_intf_luahttpBox setTitle:_NS("HTTP web interface")]; 339 [_intf_enableluahttpCheckbox setTitle: _NS("Enable HTTP web interface")]; 340 [_intf_luahttppwdLabel setStringValue:_NS("Password")]; 341 342 /* Subtitles and OSD */ 343 [_osd_encodingLabel setStringValue: _NS("Default Encoding")]; 344 [_osd_fontBox setTitle: _NS("Display Settings")]; 345 [_osd_fontButton setTitle: _NS("Choose...")]; 346 [_osd_font_colorLabel setStringValue: _NS("Font color")]; 347 [_osd_font_sizeLabel setStringValue: _NS("Font size")]; 348 [_osd_fontLabel setStringValue: _NS("Font")]; 349 [_osd_langBox setTitle: _NS("Subtitle languages")]; 350 [_osd_langLabel setStringValue: _NS("Preferred subtitle language")]; 351 [_osd_osdBox setTitle: _NS("On Screen Display")]; 352 [_osd_osdCheckbox setTitle: _NS("Enable OSD")]; 353 [_osd_opacityLabel setStringValue: _NS("Opacity")]; 354 [_osd_forceboldCheckbox setTitle: _NS("Force bold")]; 355 [_osd_outline_colorLabel setStringValue: _NS("Outline color")]; 356 [_osd_outline_thicknessLabel setStringValue: _NS("Outline thickness")]; 357 358 /* video */ 359 [_video_enableCheckbox setTitle: _NS("Enable video")]; 360 [_video_displayBox setTitle: _NS("Display")]; 361 [_video_embeddedCheckbox setTitle: _NS("Show video within the main window")]; 362 [_video_pauseWhenMinimizedCheckbox setTitle:_NS("Pause the video playback when minimized")]; 363 [_video_onTopCheckbox setTitle: _NS("Float on Top")]; 364 [_video_videodecoCheckbox setTitle: _NS("Window decorations")]; 365 366 [_video_fullscreenBox setTitle:_NS("Fullscreen settings")]; 367 [_video_startInFullscreenCheckbox setTitle:_NS("Start in fullscreen")]; 368 [_video_blackScreenCheckbox setTitle: _NS("Black screens in Fullscreen mode")]; 369 [_video_nativeFullscreenCheckbox setTitle: _NS("Use the native fullscreen mode")]; 370 [_video_deviceLabel setStringValue: _NS("Fullscreen Video Device")]; 371 372 [_video_snapBox setTitle: _NS("Video snapshots")]; 373 [_video_snap_folderButton setTitle: _NS("Browse...")]; 374 [_video_snap_folderLabel setStringValue: _NS("Folder")]; 375 [_video_snap_formatLabel setStringValue: _NS("Format")]; 376 [_video_snap_prefixLabel setStringValue: _NS("Prefix")]; 377 [_video_snap_seqnumCheckbox setTitle: _NS("Sequential numbering")]; 378 [_video_deinterlaceLabel setStringValue: _NS("Deinterlace")]; 379 [_video_deinterlace_modeLabel setStringValue: _NS("Deinterlace mode")]; 380 [_video_videoBox setTitle: _NS("Video")]; 381 382 /* generic stuff */ 383 [_showAllButton setTitle: _NS("Show All")]; 384 [_cancelButton setTitle: _NS("Cancel")]; 385 [_resetButton setTitle: _NS("Reset All")]; 386 [_saveButton setTitle: _NS("Save")]; 387 [self.window setTitle: _NS("Preferences")]; 388} 389 390/* TODO: move this part to core */ 391#define config_GetLabel(a,b) __config_GetLabel(VLC_OBJECT(a),b) 392static inline const char * __config_GetLabel(vlc_object_t *p_this, const char *psz_name) 393{ 394 module_config_t *p_config = config_FindConfig(psz_name); 395 396 /* sanity checks */ 397 if (!p_config) { 398 msg_Err(p_this, "option %s does not exist", psz_name); 399 return NULL; 400 } 401 402 if (p_config->psz_longtext) 403 return p_config->psz_longtext; 404 else if (p_config->psz_text) 405 return p_config->psz_text; 406 else 407 msg_Warn(p_this, "option %s does not include any help", psz_name); 408 409 return NULL; 410} 411 412#pragma mark - 413#pragma mark Setup controls 414 415- (void)setupButton: (NSPopUpButton *)object forStringList: (const char *)name 416{ 417 module_config_t *p_item; 418 419 [object removeAllItems]; 420 p_item = config_FindConfig(name); 421 /* serious problem, if no item found */ 422 assert(p_item); 423 424 char **values, **texts; 425 ssize_t count = config_GetPszChoices(VLC_OBJECT(getIntf()), name, 426 &values, &texts); 427 if (count < 0) { 428 msg_Err(p_intf, "Cannot get choices for %s", name); 429 return; 430 } 431 for (ssize_t i = 0; i < count && texts; i++) { 432 if (texts[i] == NULL || values[i] == NULL) 433 continue; 434 435 if (strcmp(texts[i], "") != 0) { 436 NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""]; 437 [mi setRepresentedObject: toNSStr(values[i])]; 438 [[object menu] addItem:mi]; 439 440 if (p_item->value.psz && !strcmp(p_item->value.psz, values[i])) 441 [object selectItem: [object lastItem]]; 442 } else { 443 [[object menu] addItem: [NSMenuItem separatorItem]]; 444 } 445 446 free(texts[i]); 447 free(values[i]); 448 } 449 450 free(texts); 451 free(values); 452 453 if (p_item->psz_longtext) 454 [object setToolTip: _NS(p_item->psz_longtext)]; 455} 456 457// just for clarification that this is a module list 458- (void)setupButton: (NSPopUpButton *)object forModuleList: (const char *)name 459{ 460 [self setupButton: object forStringList: name]; 461} 462 463- (void)setupButton: (NSPopUpButton *)object forIntList: (const char *)name 464{ 465 module_config_t *p_item; 466 467 [object removeAllItems]; 468 p_item = config_FindConfig(name); 469 470 /* serious problem, if no item found */ 471 assert(p_item); 472 473 int64_t *values; 474 char **texts; 475 ssize_t count = config_GetIntChoices(VLC_OBJECT(getIntf()), name, &values, &texts); 476 for (ssize_t i = 0; i < count; i++) { 477 NSMenuItem *mi = [[NSMenuItem alloc] initWithTitle: toNSStr(texts[i]) action: NULL keyEquivalent: @""]; 478 [mi setRepresentedObject:[NSNumber numberWithInt:values[i]]]; 479 [[object menu] addItem:mi]; 480 481 if (p_item->value.i == values[i]) 482 [object selectItem:[object lastItem]]; 483 484 free(texts[i]); 485 } 486 free(texts); 487 488 if (p_item->psz_longtext) 489 [object setToolTip: _NS(p_item->psz_longtext)]; 490} 491 492- (void)setupButton: (NSButton *)object forBoolValue: (const char *)name 493{ 494 [object setState: config_GetInt(p_intf, name)]; 495 [object setToolTip: _NS(config_GetLabel(p_intf, name))]; 496} 497 498- (void)setupField:(NSTextField *)object forOption:(const char *)psz_option 499{ 500 char *psz_tmp = config_GetPsz(p_intf, psz_option); 501 [object setStringValue: toNSStr(psz_tmp)]; 502 [object setToolTip: _NS(config_GetLabel(p_intf, psz_option))]; 503 free(psz_tmp); 504} 505 506- (BOOL)hasModule:(NSString *)moduleName inConfig:(NSString *)config 507{ 508 char *value = config_GetPsz(p_intf, [config UTF8String]); 509 NSString *modules = toNSStr(value); 510 free(value); 511 512 return [[modules componentsSeparatedByString:@":"] containsObject:moduleName]; 513} 514 515- (void)changeModule:(NSString *)moduleName inConfig:(NSString *)config enable:(BOOL)enable 516{ 517 char *value = config_GetPsz(p_intf, [config UTF8String]); 518 NSString *modules = toNSStr(value); 519 free(value); 520 521 NSMutableArray *components = [[modules componentsSeparatedByString:@":"] mutableCopy]; 522 if (enable) { 523 if (![components containsObject:moduleName]) { 524 [components addObject:moduleName]; 525 } 526 } else { 527 [components removeObject:moduleName]; 528 } 529 530 // trim empty entries 531 [components removeObject:@""]; 532 533 config_PutPsz(p_intf, [config UTF8String], [[components componentsJoinedByString:@":"] UTF8String]); 534} 535 536- (void)resetControls 537{ 538 module_config_t *p_item; 539 int i, y = 0; 540 char *psz_tmp; 541 542 /********************** 543 * interface settings * 544 **********************/ 545 NSUInteger sel = 0; 546 const char *pref = NULL; 547 pref = [[[NSUserDefaults standardUserDefaults] objectForKey:@"language"] UTF8String]; 548 for (int x = 0; x < ARRAY_SIZE(language_map); x++) { 549 [_intf_languagePopup addItemWithTitle:toNSStr(language_map[x].name)]; 550 if (pref) { 551 if (!strcmp(language_map[x].iso, pref)) 552 sel = x; 553 } 554 } 555 [_intf_languagePopup selectItemAtIndex:sel]; 556 557 [self setupButton:_intf_continueplaybackPopup forIntList: "macosx-continue-playback"]; 558 if (!var_InheritBool(p_intf, "macosx-recentitems")) { 559 [_intf_continueplaybackPopup setEnabled: NO]; 560 [_intf_continueplaybackPopup setToolTip: _NS("Media files cannot be resumed because keeping recent media items is disabled.")]; 561 } else { 562 [_intf_continueplaybackPopup setEnabled: YES]; 563 } 564 565 [self setupButton:_intf_appleremoteCheckbox forBoolValue: "macosx-appleremote"]; 566 [self setupButton:_intf_appleremote_sysvolCheckbox forBoolValue: "macosx-appleremote-sysvol"]; 567 [self setupButton:_intf_statusIconCheckbox forBoolValue: "macosx-statusicon"]; 568 if (OSX_SIERRA_AND_HIGHER) { 569 _intf_mediakeysCheckbox.enabled = NO; 570 _intf_mediakeysCheckbox.intValue = YES; 571 } else { 572 [self setupButton:_intf_mediakeysCheckbox forBoolValue: "macosx-mediakeys"]; 573 } 574 575 [self setupButton:_video_nativeFullscreenCheckbox forBoolValue: "macosx-nativefullscreenmode"]; 576 [self setupButton:_video_embeddedCheckbox forBoolValue: "embedded-video"]; 577 578 [self setupButton:_intf_pauseitunesPopup forIntList: "macosx-control-itunes"]; 579 580 [self setupButton:_intf_artCheckbox forBoolValue: "metadata-network-access"]; 581 582 583#ifdef HAVE_SPARKLE 584 if ([[SUUpdater sharedUpdater] lastUpdateCheckDate] != NULL) 585 [_intf_last_updateLabel setStringValue: [NSString stringWithFormat: _NS("Last check on: %@"), [[[SUUpdater sharedUpdater] lastUpdateCheckDate] descriptionWithLocale: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]]]; 586 else 587 [_intf_last_updateLabel setStringValue: _NS("No check was performed yet.")]; 588#endif 589 590 BOOL growlEnabled = [self hasModule:@"growl" inConfig:@"control"]; 591 [_intf_enableNotificationsCheckbox setState: growlEnabled ? NSOnState : NSOffState]; 592 593 if (config_GetInt(p_intf, "macosx-interfacestyle")) { 594 [_intf_style_darkButtonCell setState: YES]; 595 [_intf_style_brightButtonCell setState: NO]; 596 } else { 597 [_intf_style_darkButtonCell setState: NO]; 598 [_intf_style_brightButtonCell setState: YES]; 599 } 600 601 BOOL httpEnabled = [self hasModule:@"http" inConfig:@"extraintf"]; 602 [_intf_enableluahttpCheckbox setState: httpEnabled ? NSOnState : NSOffState]; 603 _intf_luahttppwdTextField.enabled = httpEnabled; 604 605 [self setupField:_intf_luahttppwdTextField forOption: "http-password"]; 606 607 /****************** 608 * audio settings * 609 ******************/ 610 [self setupButton:_audio_enableCheckbox forBoolValue: "audio"]; 611 612 if (config_GetInt(p_intf, "volume-save")) { 613 [_audio_autosavevol_yesButtonCell setState: NSOnState]; 614 [_audio_autosavevol_noButtonCell setState: NSOffState]; 615 [_audio_volTextField setEnabled: NO]; 616 [_audio_volSlider setEnabled: NO]; 617 618 [_audio_volSlider setIntValue: 100]; 619 [_audio_volTextField setIntValue: 100]; 620 } else { 621 [_audio_autosavevol_yesButtonCell setState: NSOffState]; 622 [_audio_autosavevol_noButtonCell setState: NSOnState]; 623 [_audio_volTextField setEnabled: YES]; 624 [_audio_volSlider setEnabled: YES]; 625 626 i = var_InheritInteger(p_intf, "auhal-volume"); 627 i = i * 200. / AOUT_VOLUME_MAX; 628 [_audio_volSlider setIntValue: i]; 629 [_audio_volTextField setIntValue: i]; 630 } 631 632 [self setupField:_audio_langTextField forOption: "audio-language"]; 633 634 [self setupButton:_audio_visualPopup forModuleList: "audio-visual"]; 635 636 /* Last.FM is optional */ 637 if (module_exists("audioscrobbler")) { 638 [self setupField:_audio_lastuserTextField forOption:"lastfm-username"]; 639 [self setupField:_audio_lastpwdSecureTextField forOption:"lastfm-password"]; 640 641 if (config_ExistIntf(VLC_OBJECT(p_intf), "audioscrobbler")) { 642 [_audio_lastCheckbox setState: NSOnState]; 643 [_audio_lastuserTextField setEnabled: YES]; 644 [_audio_lastpwdSecureTextField setEnabled: YES]; 645 } else { 646 [_audio_lastCheckbox setState: NSOffState]; 647 [_audio_lastuserTextField setEnabled: NO]; 648 [_audio_lastpwdSecureTextField setEnabled: NO]; 649 } 650 } else 651 [_audio_lastCheckbox setEnabled: NO]; 652 653 /****************** 654 * video settings * 655 ******************/ 656 [self setupButton:_video_enableCheckbox forBoolValue: "video"]; 657 [self setupButton:_video_startInFullscreenCheckbox forBoolValue: "fullscreen"]; 658 [self setupButton:_video_onTopCheckbox forBoolValue: "video-on-top"]; 659 [self setupButton:_video_blackScreenCheckbox forBoolValue: "macosx-black"]; 660 [self setupButton:_video_videodecoCheckbox forBoolValue: "video-deco"]; 661 [self setupButton:_video_pauseWhenMinimizedCheckbox forBoolValue: "macosx-pause-minimized"]; 662 663 [_video_devicePopup removeAllItems]; 664 i = 0; 665 y = [[NSScreen screens] count]; 666 [_video_devicePopup addItemWithTitle: _NS("Default")]; 667 [[_video_devicePopup lastItem] setTag: 0]; 668 while (i < y) { 669 NSRect s_rect = [[[NSScreen screens] objectAtIndex:i] frame]; 670 [_video_devicePopup addItemWithTitle: 671 [NSString stringWithFormat: @"%@ %i (%ix%i)", _NS("Screen"), i+1, 672 (int)s_rect.size.width, (int)s_rect.size.height]]; 673 [[_video_devicePopup lastItem] setTag: (int)[[[NSScreen screens] objectAtIndex:i] displayID]]; 674 i++; 675 } 676 [_video_devicePopup selectItemAtIndex: 0]; 677 [_video_devicePopup selectItemWithTag: config_GetInt(p_intf, "macosx-vdev")]; 678 679 [self setupField:_video_snap_folderTextField forOption:"snapshot-path"]; 680 [self setupField:_video_snap_prefixTextField forOption:"snapshot-prefix"]; 681 [self setupButton:_video_snap_seqnumCheckbox forBoolValue: "snapshot-sequential"]; 682 [self setupButton:_video_snap_formatPopup forStringList: "snapshot-format"]; 683 [self setupButton:_video_deinterlacePopup forIntList: "deinterlace"]; 684 [self setupButton:_video_deinterlace_modePopup forStringList: "deinterlace-mode"]; 685 686 // set lion fullscreen mode restrictions 687 [self enableLionFullscreenMode: [_video_nativeFullscreenCheckbox state]]; 688 689 /*************************** 690 * input & codecs settings * 691 ***************************/ 692 [self setupField:_input_recordTextField forOption:"input-record-path"]; 693 694 [self setupButton:_input_hardwareAccelerationCheckbox forBoolValue: "videotoolbox"]; 695 [_input_postprocTextField setIntValue: config_GetInt(p_intf, "postproc-q")]; 696 [_input_postprocTextField setToolTip: _NS(config_GetLabel(p_intf, "postproc-q"))]; 697 [self setupButton:_input_skipFramesCheckbox forBoolValue: "skip-frames"]; 698 [self setupButton:_input_aviPopup forIntList: "avi-index"]; 699 [self setupButton:_input_skipLoopPopup forIntList: "avcodec-skiploopfilter"]; 700 701 [_input_cachelevelPopup removeAllItems]; 702 NSMenuItem *item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Custom") action:nil keyEquivalent:@""]; 703 [item setTag: 0]; 704 item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Lowest Latency") action:nil keyEquivalent:@""]; 705 [item setTag: 100]; 706 item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Low Latency") action:nil keyEquivalent:@""]; 707 [item setTag: 200]; 708 item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Normal") action:nil keyEquivalent:@""]; 709 [item setTag: 300]; 710 item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Higher Latency") action:nil keyEquivalent:@""]; 711 [item setTag: 500]; 712 item = [[_input_cachelevelPopup menu] addItemWithTitle:_NS("Highest Latency") action:nil keyEquivalent:@""]; 713 [item setTag: 1000]; 714 715 #define TestCaC(name, factor) \ 716 cache_equal = cache_equal && \ 717 (i_cache * factor == config_GetInt(p_intf, name)); 718 719 /* Select the accurate value of the PopupButton */ 720 bool cache_equal = true; 721 int i_cache = config_GetInt(p_intf, "file-caching"); 722 723 TestCaC("network-caching", 10/3); 724 TestCaC("disc-caching", 1); 725 TestCaC("live-caching", 1); 726 if (cache_equal) { 727 [_input_cachelevelPopup selectItemWithTag: i_cache]; 728 [_input_cachelevel_customLabel setHidden: YES]; 729 } else { 730 [_input_cachelevelPopup selectItemWithTitle: _NS("Custom")]; 731 [_input_cachelevel_customLabel setHidden: NO]; 732 } 733 #undef TestCaC 734 735 /********************* 736 * subtitle settings * 737 *********************/ 738 [self setupButton:_osd_osdCheckbox forBoolValue: "osd"]; 739 740 [self setupButton:_osd_encodingPopup forStringList: "subsdec-encoding"]; 741 [self setupField:_osd_langTextField forOption: "sub-language" ]; 742 743 [self setupField:_osd_fontTextField forOption: "freetype-font"]; 744 [self setupButton:_osd_font_colorPopup forIntList: "freetype-color"]; 745 [self setupButton:_osd_font_sizePopup forIntList: "freetype-rel-fontsize"]; 746 i = config_GetInt(p_intf, "freetype-opacity") * 100.0 / 255.0 + 0.5; 747 [_osd_opacityTextField setIntValue: i]; 748 [_osd_opacitySlider setIntValue: i]; 749 [_osd_opacitySlider setToolTip: _NS(config_GetLabel(p_intf, "freetype-opacity"))]; 750 [_osd_opacityTextField setToolTip: [_osd_opacitySlider toolTip]]; 751 [self setupButton:_osd_forceboldCheckbox forBoolValue: "freetype-bold"]; 752 [self setupButton:_osd_outline_colorPopup forIntList: "freetype-outline-color"]; 753 [self setupButton:_osd_outline_thicknessPopup forIntList: "freetype-outline-thickness"]; 754 755 /******************** 756 * hotkeys settings * 757 ********************/ 758 _hotkeySettings = [[NSMutableArray alloc] init]; 759 NSMutableArray *tempArray_desc = [[NSMutableArray alloc] init]; 760 NSMutableArray *tempArray_names = [[NSMutableArray alloc] init]; 761 762 /* Get the main Module */ 763 module_t *p_main = module_get_main(); 764 assert(p_main); 765 unsigned confsize; 766 module_config_t *p_config; 767 768 p_config = module_config_get (p_main, &confsize); 769 770 for (size_t i = 0; i < confsize; i++) { 771 module_config_t *p_item = p_config + i; 772 773 if (CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL 774 && !strncmp(p_item->psz_name , "key-", 4) 775 && !EMPTY_STR(p_item->psz_text)) { 776 [tempArray_desc addObject: _NS(p_item->psz_text)]; 777 [tempArray_names addObject: toNSStr(p_item->psz_name)]; 778 if (p_item->value.psz) 779 [_hotkeySettings addObject: toNSStr(p_item->value.psz)]; 780 else 781 [_hotkeySettings addObject: [NSString string]]; 782 } 783 } 784 module_config_free (p_config); 785 786 _hotkeyDescriptions = [[NSArray alloc] initWithArray:tempArray_desc copyItems: YES]; 787 _hotkeyNames = [[NSArray alloc] initWithArray:tempArray_names copyItems: YES]; 788 789 [_hotkeys_listbox reloadData]; 790} 791 792#pragma mark - 793#pragma mark General actions 794 795- (void)showSimplePrefs 796{ 797 /* we want to show the interface settings, if no category was chosen */ 798 if ([[self.window toolbar] selectedItemIdentifier] == nil) { 799 [[self.window toolbar] setSelectedItemIdentifier: VLCIntfSettingToolbarIdentifier]; 800 [self showInterfaceSettings]; 801 } 802 803 [self resetControls]; 804 805 [self.window makeKeyAndOrderFront: self]; 806} 807 808- (void)showSimplePrefsWithLevel:(NSInteger)i_window_level 809{ 810 [self.window setLevel: i_window_level]; 811 [self showSimplePrefs]; 812} 813 814- (IBAction)buttonAction:(id)sender 815{ 816 if (sender == _cancelButton) { 817 [[NSFontPanel sharedFontPanel] close]; 818 [self.window orderOut: sender]; 819 } else if (sender == _saveButton) { 820 [self saveChangedSettings]; 821 [[NSFontPanel sharedFontPanel] close]; 822 [self.window orderOut: sender]; 823 } else if (sender == _showAllButton) { 824 [self.window orderOut: self]; 825 [[[VLCMain sharedInstance] preferences] showPrefsWithLevel:[self.window level]]; 826 } else 827 msg_Warn(p_intf, "unknown buttonAction sender"); 828} 829 830- (IBAction)resetPreferences:(NSControl *)sender 831{ 832 NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"), 833 _NS("Continue"), nil, [sender window], self, 834 @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil, @"%@", 835 _NS("This will reset VLC media player's preferences.\n\n" 836 "Note that VLC will restart during the process, so your current " 837 "playlist will be emptied and eventual playback, streaming or " 838 "transcoding activities will stop immediately.\n\n" 839 "The Media Library will not be affected.\n\n" 840 "Are you sure you want to continue?")); 841} 842 843- (void)sheetDidEnd:(NSWindow *)o_sheet 844 returnCode:(int)i_return 845 contextInfo:(void *)o_context 846{ 847 if (i_return == NSAlertAlternateReturn) { 848 /* reset VLC's config */ 849 config_ResetAll(p_intf); 850 [self resetControls]; 851 852 /* force config file creation, since libvlc won't exit normally */ 853 config_SaveConfigFile(p_intf); 854 855 /* reset OS X defaults */ 856 [[VLCMain sharedInstance] resetAndReinitializeUserDefaults]; 857 858 /* Relaunch now */ 859 const char * path = [[[NSBundle mainBundle] executablePath] UTF8String]; 860 861 /* For some reason we need to fork(), not just execl(), which reports a ENOTSUP then. */ 862 if (fork() != 0) { 863 exit(0); 864 return; 865 } 866 execl(path, path, NULL); 867 } 868} 869 870static inline void save_int_list(intf_thread_t * p_intf, id object, const char * name) 871{ 872 NSNumber *p_valueobject = (NSNumber *)[[object selectedItem] representedObject]; 873 if (p_valueobject) { 874 assert([p_valueobject isKindOfClass:[NSNumber class]]); 875 config_PutInt(p_intf, name, [p_valueobject intValue]); 876 } 877} 878 879static inline void save_string_list(intf_thread_t * p_intf, id object, const char * name) 880{ 881 NSString *p_stringobject = (NSString *)[[object selectedItem] representedObject]; 882 if (p_stringobject) { 883 assert([p_stringobject isKindOfClass:[NSString class]]); 884 config_PutPsz(p_intf, name, [p_stringobject UTF8String]); 885 } 886} 887 888+ (BOOL)updateRightToLeftSettings 889{ 890 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 891 NSString *isoCode = [defaults stringForKey:@"language"]; 892 893 if (!isoCode || [isoCode isEqualToString:@"auto"]) { 894 // Automatic handling of right to left 895 [defaults removeObjectForKey:@"NSForceRightToLeftWritingDirection"]; 896 [defaults removeObjectForKey:@"AppleTextDirection"]; 897 } else { 898 for(int i = 0; i < ARRAY_SIZE(language_map); i++) { 899 if (!strcmp(language_map[i].iso, [isoCode UTF8String])) { 900 [defaults setBool:language_map[i].isRightToLeft forKey:@"NSForceRightToLeftWritingDirection"]; 901 [defaults setBool:language_map[i].isRightToLeft forKey:@"AppleTextDirection"]; 902 return YES; 903 } 904 } 905 } 906 907 return NO; 908} 909 910- (void)saveChangedSettings 911{ 912#define SaveIntList(object, name) save_int_list(p_intf, object, name) 913 914#define SaveStringList(object, name) save_string_list(p_intf, object, name) 915#define SaveModuleList(object, name) SaveStringList(object, name) 916 917 /********************** 918 * interface settings * 919 **********************/ 920 if (_intfSettingChanged) { 921 NSUInteger index = [_intf_languagePopup indexOfSelectedItem]; 922 NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; 923 [defaults setObject:toNSStr(language_map[index].iso) forKey:@"language"]; 924 [VLCSimplePrefsController updateRightToLeftSettings]; 925 [defaults synchronize]; 926 927 config_PutInt(p_intf, "metadata-network-access", [_intf_artCheckbox state]); 928 929 config_PutInt(p_intf, "macosx-appleremote", [_intf_appleremoteCheckbox state]); 930 config_PutInt(p_intf, "macosx-appleremote-sysvol", [_intf_appleremote_sysvolCheckbox state]); 931 config_PutInt(p_intf, "macosx-statusicon", [_intf_statusIconCheckbox state]); 932 config_PutInt(p_intf, "macosx-mediakeys", [_intf_mediakeysCheckbox state]); 933 config_PutInt(p_intf, "macosx-interfacestyle", [_intf_style_darkButtonCell state]); 934 935 [self changeModule:@"growl" inConfig:@"control" enable:[_intf_enableNotificationsCheckbox state] == NSOnState]; 936 937 [self changeModule:@"http" inConfig:@"extraintf" enable:[_intf_enableluahttpCheckbox state] == NSOnState]; 938 config_PutPsz(p_intf, "http-password", [[_intf_luahttppwdTextField stringValue] UTF8String]); 939 940 SaveIntList(_intf_pauseitunesPopup, "macosx-control-itunes"); 941 SaveIntList(_intf_continueplaybackPopup, "macosx-continue-playback"); 942 943 /* activate stuff without restart */ 944 if ([_intf_appleremoteCheckbox state] == YES) 945 [[VLCCoreInteraction sharedInstance] startListeningWithAppleRemote]; 946 else 947 [[VLCCoreInteraction sharedInstance] stopListeningWithAppleRemote]; 948 _intfSettingChanged = NO; 949 } 950 951 /****************** 952 * audio settings * 953 ******************/ 954 if (_audioSettingChanged) { 955 config_PutInt(p_intf, "audio", [_audio_enableCheckbox state]); 956 config_PutInt(p_intf, "volume-save", [_audio_autosavevol_yesButtonCell state]); 957 var_SetBool(p_intf, "volume-save", [_audio_autosavevol_yesButtonCell state]); 958 if ([_audio_volTextField isEnabled]) 959 config_PutInt(p_intf, "auhal-volume", ([_audio_volTextField intValue] * AOUT_VOLUME_MAX) / 200); 960 961 config_PutPsz(p_intf, "audio-language", [[_audio_langTextField stringValue] UTF8String]); 962 963 SaveModuleList(_audio_visualPopup, "audio-visual"); 964 965 /* Last.FM is optional */ 966 if (module_exists("audioscrobbler")) { 967 [_audio_lastCheckbox setEnabled: YES]; 968 if ([_audio_lastCheckbox state] == NSOnState) 969 config_AddIntf(p_intf, "audioscrobbler"); 970 else 971 config_RemoveIntf(p_intf, "audioscrobbler"); 972 973 config_PutPsz(p_intf, "lastfm-username", [[_audio_lastuserTextField stringValue] UTF8String]); 974 config_PutPsz(p_intf, "lastfm-password", [[_audio_lastpwdSecureTextField stringValue] UTF8String]); 975 } 976 else 977 [_audio_lastCheckbox setEnabled: NO]; 978 _audioSettingChanged = NO; 979 } 980 981 /****************** 982 * video settings * 983 ******************/ 984 if (_videoSettingChanged) { 985 config_PutInt(p_intf, "video", [_video_enableCheckbox state]); 986 config_PutInt(p_intf, "fullscreen", [_video_startInFullscreenCheckbox state]); 987 config_PutInt(p_intf, "video-deco", [_video_videodecoCheckbox state]); 988 config_PutInt(p_intf, "video-on-top", [_video_onTopCheckbox state]); 989 config_PutInt(p_intf, "macosx-black", [_video_blackScreenCheckbox state]); 990 991 config_PutInt(p_intf, "macosx-pause-minimized", [_video_pauseWhenMinimizedCheckbox state]); 992 993 config_PutInt(p_intf, "embedded-video", [_video_embeddedCheckbox state]); 994 config_PutInt(p_intf, "macosx-nativefullscreenmode", [_video_nativeFullscreenCheckbox state]); 995 config_PutInt(p_intf, "macosx-vdev", [[_video_devicePopup selectedItem] tag]); 996 997 config_PutPsz(p_intf, "snapshot-path", [[_video_snap_folderTextField stringValue] UTF8String]); 998 config_PutPsz(p_intf, "snapshot-prefix", [[_video_snap_prefixTextField stringValue] UTF8String]); 999 config_PutInt(p_intf, "snapshot-sequential", [_video_snap_seqnumCheckbox state]); 1000 SaveStringList(_video_snap_formatPopup, "snapshot-format"); 1001 SaveIntList(_video_deinterlacePopup, "deinterlace"); 1002 SaveStringList(_video_deinterlace_modePopup, "deinterlace-mode"); 1003 _videoSettingChanged = NO; 1004 } 1005 1006 /*************************** 1007 * input & codecs settings * 1008 ***************************/ 1009 if (_inputSettingChanged) { 1010 config_PutPsz(p_intf, "input-record-path", [[_input_recordTextField stringValue] UTF8String]); 1011 1012 config_PutInt(p_intf, "videotoolbox", [_input_hardwareAccelerationCheckbox state]); 1013 config_PutInt(p_intf, "postproc-q", [_input_postprocTextField intValue]); 1014 config_PutInt(p_intf, "skip-frames", [_input_skipFramesCheckbox state]); 1015 1016 SaveIntList(_input_aviPopup, "avi-index"); 1017 1018 SaveIntList(_input_skipLoopPopup, "avcodec-skiploopfilter"); 1019 1020 #define CaC(name, factor) config_PutInt(p_intf, name, [[_input_cachelevelPopup selectedItem] tag] * factor) 1021 if ([[_input_cachelevelPopup selectedItem] tag] == 0) { 1022 msg_Dbg(p_intf, "Custom chosen, not adjusting cache values"); 1023 } else { 1024 msg_Dbg(p_intf, "Adjusting all cache values to: %i", (int)[[_input_cachelevelPopup selectedItem] tag]); 1025 CaC("file-caching", 1); 1026 CaC("network-caching", 10/3); 1027 CaC("disc-caching", 1); 1028 CaC("live-caching", 1); 1029 } 1030 #undef CaC 1031 _inputSettingChanged = NO; 1032 } 1033 1034 /********************** 1035 * subtitles settings * 1036 **********************/ 1037 if (_osdSettingChanged) { 1038 config_PutInt(p_intf, "osd", [_osd_osdCheckbox state]); 1039 1040 if ([_osd_encodingPopup indexOfSelectedItem] >= 0) 1041 SaveStringList(_osd_encodingPopup, "subsdec-encoding"); 1042 else 1043 config_PutPsz(p_intf, "subsdec-encoding", ""); 1044 1045 config_PutPsz(p_intf, "sub-language", [[_osd_langTextField stringValue] UTF8String]); 1046 1047 config_PutPsz(p_intf, "freetype-font", [[_osd_fontTextField stringValue] UTF8String]); 1048 SaveIntList(_osd_font_colorPopup, "freetype-color"); 1049 SaveIntList(_osd_font_sizePopup, "freetype-rel-fontsize"); 1050 config_PutInt(p_intf, "freetype-opacity", [_osd_opacityTextField intValue] * 255.0 / 100.0 + 0.5); 1051 config_PutInt(p_intf, "freetype-bold", [_osd_forceboldCheckbox state]); 1052 SaveIntList(_osd_outline_colorPopup, "freetype-outline-color"); 1053 SaveIntList(_osd_outline_thicknessPopup, "freetype-outline-thickness"); 1054 _osdSettingChanged = NO; 1055 } 1056 1057 /******************** 1058 * hotkeys settings * 1059 ********************/ 1060 if (_hotkeyChanged) { 1061 NSUInteger hotKeyCount = [_hotkeySettings count]; 1062 for (NSUInteger i = 0; i < hotKeyCount; i++) 1063 config_PutPsz(p_intf, [[_hotkeyNames objectAtIndex:i] UTF8String], [[_hotkeySettings objectAtIndex:i]UTF8String]); 1064 _hotkeyChanged = NO; 1065 } 1066 1067 [[VLCCoreInteraction sharedInstance] fixIntfSettings]; 1068 1069 /* okay, let's save our changes to vlcrc */ 1070 config_SaveConfigFile(p_intf); 1071 1072 [[NSNotificationCenter defaultCenter] postNotificationName:VLCMediaKeySupportSettingChangedNotification object:nil]; 1073 [[NSNotificationCenter defaultCenter] postNotificationName:VLCConfigurationChangedNotification object:nil]; 1074} 1075 1076- (void)showSettingsForCategory:(NSView *)categoryView 1077{ 1078 [_contentView setSubviews:[NSArray array]]; 1079 [_contentView addSubview:categoryView]; 1080 1081 NSDictionary *views = @{ @"view": categoryView }; 1082 NSArray *constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"|[view]|" options:0 metrics:nil views:views]; 1083 [_contentView addConstraints:constraints]; 1084 constraints = [NSLayoutConstraint constraintsWithVisualFormat:@"V:|[view]|" options:0 metrics:nil views:views]; 1085 [_contentView addConstraints:constraints]; 1086 1087 [_scrollView layoutSubtreeIfNeeded]; 1088 [_scrollView flashScrollers]; 1089} 1090 1091#pragma mark - 1092#pragma mark Specific actions 1093 1094// disables some video settings which do not work in lion mode 1095- (void)enableLionFullscreenMode: (BOOL)_value 1096{ 1097 [_video_videodecoCheckbox setEnabled: !_value]; 1098 [_video_blackScreenCheckbox setEnabled: !_value]; 1099 [_video_devicePopup setEnabled: !_value]; 1100 1101 if (_value) { 1102 [_video_videodecoCheckbox setState: NSOnState]; 1103 [_video_blackScreenCheckbox setState: NSOffState]; 1104 1105 NSString *tooltipText = _NS("This setting cannot be changed because the native fullscreen mode is enabled."); 1106 [_video_videodecoCheckbox setToolTip:tooltipText]; 1107 [_video_blackScreenCheckbox setToolTip:tooltipText]; 1108 [_video_devicePopup setToolTip:tooltipText]; 1109 1110 } else { 1111 [self setupButton:_video_videodecoCheckbox forBoolValue: "video-deco"]; 1112 [self setupButton:_video_blackScreenCheckbox forBoolValue: "macosx-black"]; 1113 1114 [_video_devicePopup setToolTip:@""]; 1115 } 1116} 1117 1118- (IBAction)interfaceSettingChanged:(id)sender 1119{ 1120 if (sender == _intf_enableluahttpCheckbox) { 1121 _intf_luahttppwdTextField.enabled = [sender state] == NSOnState; 1122 } 1123 1124 _intfSettingChanged = YES; 1125} 1126 1127- (void)showInterfaceSettings 1128{ 1129 [self showSettingsForCategory:_intfView]; 1130} 1131 1132- (IBAction)audioSettingChanged:(id)sender 1133{ 1134 if (sender == _audio_volSlider) 1135 [_audio_volTextField setIntValue: [_audio_volSlider intValue]]; 1136 1137 if (sender == _audio_volTextField) 1138 [_audio_volSlider setIntValue: [_audio_volTextField intValue]]; 1139 1140 if (sender == _audio_lastCheckbox) { 1141 if ([_audio_lastCheckbox state] == NSOnState) { 1142 [_audio_lastpwdSecureTextField setEnabled: YES]; 1143 [_audio_lastuserTextField setEnabled: YES]; 1144 } else { 1145 [_audio_lastpwdSecureTextField setEnabled: NO]; 1146 [_audio_lastuserTextField setEnabled: NO]; 1147 } 1148 } 1149 1150 if (sender == _audio_autosavevolMatrix) { 1151 BOOL enableVolumeSlider = [_audio_autosavevolMatrix selectedTag] == 1; 1152 [_audio_volTextField setEnabled: enableVolumeSlider]; 1153 [_audio_volSlider setEnabled: enableVolumeSlider]; 1154 } 1155 1156 _audioSettingChanged = YES; 1157} 1158 1159- (void)showAudioSettings 1160{ 1161 [self showSettingsForCategory:_audioView]; 1162} 1163 1164- (IBAction)videoSettingChanged:(id)sender 1165{ 1166 if (sender == _video_nativeFullscreenCheckbox) 1167 [self enableLionFullscreenMode:[sender state]]; 1168 else if (sender == _video_snap_folderButton) { 1169 _selectFolderPanel = [[NSOpenPanel alloc] init]; 1170 [_selectFolderPanel setCanChooseDirectories: YES]; 1171 [_selectFolderPanel setCanChooseFiles: NO]; 1172 [_selectFolderPanel setResolvesAliases: YES]; 1173 [_selectFolderPanel setAllowsMultipleSelection: NO]; 1174 [_selectFolderPanel setMessage: _NS("Choose the folder to save your video snapshots to.")]; 1175 [_selectFolderPanel setCanCreateDirectories: YES]; 1176 [_selectFolderPanel setPrompt: _NS("Choose")]; 1177 [_selectFolderPanel beginSheetModalForWindow:self.window completionHandler: ^(NSInteger returnCode) { 1178 if (returnCode == NSOKButton) { 1179 [_video_snap_folderTextField setStringValue: [[_selectFolderPanel URL] path]]; 1180 } 1181 }]; 1182 } 1183 1184 _videoSettingChanged = YES; 1185} 1186 1187- (void)showVideoSettings 1188{ 1189 [self showSettingsForCategory:_videoView]; 1190} 1191 1192- (IBAction)osdSettingChanged:(id)sender 1193{ 1194 if (sender == _osd_opacityTextField) 1195 [_osd_opacitySlider setIntValue: [_osd_opacityTextField intValue]]; 1196 1197 if (sender == _osd_opacitySlider) 1198 [_osd_opacityTextField setIntValue: [_osd_opacitySlider intValue]]; 1199 1200 _osdSettingChanged = YES; 1201} 1202 1203- (void)showOSDSettings 1204{ 1205 [self showSettingsForCategory:_osdView]; 1206} 1207 1208- (void)controlTextDidChange:(NSNotification *)notification 1209{ 1210 id notificationObject = [notification object]; 1211 if (notificationObject == _audio_langTextField || 1212 notificationObject == _audio_lastpwdSecureTextField || 1213 notificationObject == _audio_lastuserTextField || 1214 notificationObject == _audio_volTextField) 1215 _audioSettingChanged = YES; 1216 else if (notificationObject == _input_recordTextField || 1217 notificationObject == _input_postprocTextField) 1218 _inputSettingChanged = YES; 1219 else if (notificationObject == _osd_fontTextField || 1220 notificationObject == _osd_langTextField || 1221 notificationObject == _osd_opacityTextField) 1222 _osdSettingChanged = YES; 1223 else if (notificationObject == _video_snap_folderTextField || 1224 notificationObject == _video_snap_prefixTextField) 1225 _videoSettingChanged = YES; 1226 else if (notificationObject == _intf_luahttppwdTextField) 1227 _intfSettingChanged = YES; 1228} 1229 1230- (IBAction)showFontPicker:(id)sender 1231{ 1232 char * font = config_GetPsz(p_intf, "freetype-font"); 1233 NSString * fontName = font ? toNSStr(font) : nil; 1234 free(font); 1235 if (fontName) { 1236 NSFont * font = [NSFont fontWithName:fontName size:0.0]; 1237 [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO]; 1238 } 1239 [[NSFontManager sharedFontManager] setTarget: self]; 1240 [[NSFontPanel sharedFontPanel] setDelegate:self]; 1241 [[NSFontPanel sharedFontPanel] makeKeyAndOrderFront:self]; 1242} 1243 1244- (void)changeFont:(id)sender 1245{ 1246 NSFont *someFont = [NSFont systemFontOfSize:12]; 1247 1248 // converts given font to changes in font panel. Original font is irrelevant 1249 NSFont *selectedFont = [sender convertFont:someFont]; 1250 1251 [_osd_fontTextField setStringValue:[selectedFont fontName]]; 1252 [self osdSettingChanged:self]; 1253} 1254 1255- (NSUInteger)validModesForFontPanel:(NSFontPanel *)fontPanel 1256{ 1257 return NSFontPanelFaceModeMask | NSFontPanelCollectionModeMask; 1258} 1259 1260- (IBAction)inputSettingChanged:(id)sender 1261{ 1262 if (sender == _input_cachelevelPopup) { 1263 if ([[[_input_cachelevelPopup selectedItem] title] isEqualToString: _NS("Custom")]) 1264 [_input_cachelevel_customLabel setHidden: NO]; 1265 else 1266 [_input_cachelevel_customLabel setHidden: YES]; 1267 } else if (sender == _input_recordButton) { 1268 _selectFolderPanel = [[NSOpenPanel alloc] init]; 1269 [_selectFolderPanel setCanChooseDirectories: YES]; 1270 [_selectFolderPanel setCanChooseFiles: YES]; 1271 [_selectFolderPanel setResolvesAliases: YES]; 1272 [_selectFolderPanel setAllowsMultipleSelection: NO]; 1273 [_selectFolderPanel setMessage: _NS("Choose the directory or filename where the records will be stored.")]; 1274 [_selectFolderPanel setCanCreateDirectories: YES]; 1275 [_selectFolderPanel setPrompt: _NS("Choose")]; 1276 [_selectFolderPanel beginSheetModalForWindow:self.window completionHandler: ^(NSInteger returnCode) { 1277 if (returnCode == NSOKButton) 1278 { 1279 [_input_recordTextField setStringValue: [[_selectFolderPanel URL] path]]; 1280 _inputSettingChanged = YES; 1281 } 1282 }]; 1283 1284 return; 1285 } 1286 1287 _inputSettingChanged = YES; 1288} 1289 1290- (void)showInputSettings 1291{ 1292 [self showSettingsForCategory:_inputView]; 1293} 1294 1295- (NSString *)bundleIdentifierForApplicationName:(NSString *)appName 1296{ 1297 NSWorkspace * workspace = [NSWorkspace sharedWorkspace]; 1298 NSString * appPath = [workspace fullPathForApplication:appName]; 1299 if (appPath) { 1300 NSBundle * appBundle = [NSBundle bundleWithPath:appPath]; 1301 return [appBundle bundleIdentifier]; 1302 } 1303 return nil; 1304} 1305 1306- (NSString *)applicationNameForBundleIdentifier:(NSString *)bundleIdentifier 1307{ 1308 return [[[NSFileManager defaultManager] displayNameAtPath:[[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:bundleIdentifier]] stringByDeletingPathExtension]; 1309} 1310 1311- (NSImage *)iconForBundleIdentifier:(NSString *)bundleIdentifier 1312{ 1313 NSWorkspace *workspace = [NSWorkspace sharedWorkspace]; 1314 NSSize iconSize = NSMakeSize(16., 16.); 1315 NSImage *icon = [workspace iconForFile:[workspace absolutePathForAppBundleWithIdentifier:bundleIdentifier]]; 1316 [icon setSize:iconSize]; 1317 return icon; 1318} 1319 1320- (IBAction)urlHandlerAction:(id)sender 1321{ 1322 NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier]; 1323 1324 if (sender == _input_urlhandlerButton) { 1325 1326 void (^fillUrlHandlerPopup)(NSString*, NSPopUpButton*) = ^void(NSString *protocol, NSPopUpButton *object) { 1327 1328 NSArray *handlers = (__bridge_transfer NSArray *)LSCopyAllHandlersForURLScheme((__bridge CFStringRef)protocol); 1329 NSMutableArray *rawHandlers = [[NSMutableArray alloc] init]; 1330 [object removeAllItems]; 1331 NSUInteger count = [handlers count]; 1332 for (NSUInteger x = 0; x < count; x++) { 1333 NSString *rawhandler = [handlers objectAtIndex:x]; 1334 NSString *handler = [self applicationNameForBundleIdentifier:rawhandler]; 1335 if (handler && ![handler isEqualToString:@""]) { 1336 [object addItemWithTitle:handler]; 1337 [[object lastItem] setImage: [self iconForBundleIdentifier:[handlers objectAtIndex:x]]]; 1338 [rawHandlers addObject: rawhandler]; 1339 } 1340 } 1341 [object selectItemAtIndex: [rawHandlers indexOfObject:(__bridge_transfer id)LSCopyDefaultHandlerForURLScheme((__bridge CFStringRef)protocol)]]; 1342 }; 1343 1344 fillUrlHandlerPopup(@"ftp", _urlhandler_ftpPopup); 1345 fillUrlHandlerPopup(@"mms", _urlhandler_mmsPopup); 1346 fillUrlHandlerPopup(@"rtmp", _urlhandler_rtmpPopup); 1347 fillUrlHandlerPopup(@"rtp", _urlhandler_rtpPopup); 1348 fillUrlHandlerPopup(@"rtsp", _urlhandler_rtspPopup); 1349 fillUrlHandlerPopup(@"sftp", _urlhandler_sftpPopup); 1350 fillUrlHandlerPopup(@"smb", _urlhandler_smbPopup); 1351 fillUrlHandlerPopup(@"udp", _urlhandler_udpPopup); 1352 1353 1354 [NSApp beginSheet:_urlhandler_win modalForWindow:self.window modalDelegate:self didEndSelector:NULL contextInfo:nil]; 1355 } else { 1356 [_urlhandler_win orderOut:sender]; 1357 [NSApp endSheet:_urlhandler_win]; 1358 1359 if (sender == _urlhandler_saveButton) { 1360 LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_ftpPopup selectedItem] title]]); 1361 LSSetDefaultHandlerForURLScheme(CFSTR("mms"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_mmsPopup selectedItem] title]]); 1362 LSSetDefaultHandlerForURLScheme(CFSTR("mmsh"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_mmsPopup selectedItem] title]]); 1363 LSSetDefaultHandlerForURLScheme(CFSTR("rtmp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtmpPopup selectedItem] title]]); 1364 LSSetDefaultHandlerForURLScheme(CFSTR("rtp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtpPopup selectedItem] title]]); 1365 LSSetDefaultHandlerForURLScheme(CFSTR("rtsp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_rtspPopup selectedItem] title]]); 1366 LSSetDefaultHandlerForURLScheme(CFSTR("sftp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_sftpPopup selectedItem] title]]); 1367 LSSetDefaultHandlerForURLScheme(CFSTR("smb"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_smbPopup selectedItem] title]]); 1368 LSSetDefaultHandlerForURLScheme(CFSTR("udp"), (__bridge CFStringRef)[self bundleIdentifierForApplicationName:[[_urlhandler_udpPopup selectedItem] title]]); 1369 } 1370 } 1371} 1372 1373#pragma mark - 1374#pragma mark Hotkey actions 1375 1376- (void)hotkeyTableDoubleClick:(id)object 1377{ 1378 // -1 is header 1379 if ([_hotkeys_listbox clickedRow] >= 0) 1380 [self hotkeySettingChanged:_hotkeys_listbox]; 1381} 1382 1383- (IBAction)hotkeySettingChanged:(id)sender 1384{ 1385 if (sender == _hotkeys_changeButton || sender == _hotkeys_listbox) { 1386 [_hotkeys_changeLabel setStringValue: [NSString stringWithFormat: _NS("Press new keys for\n\"%@\""), 1387 [_hotkeyDescriptions objectAtIndex:[_hotkeys_listbox selectedRow]]]]; 1388 [_hotkeys_change_keysLabel setStringValue: [[VLCStringUtility sharedInstance] OSXStringKeyToString:[_hotkeySettings objectAtIndex:[_hotkeys_listbox selectedRow]]]]; 1389 [_hotkeys_change_takenLabel setStringValue: @""]; 1390 [_hotkeys_change_win setInitialFirstResponder: [_hotkeys_change_win contentView]]; 1391 [_hotkeys_change_win makeFirstResponder: [_hotkeys_change_win contentView]]; 1392 [NSApp runModalForWindow:_hotkeys_change_win]; 1393 } else if (sender == _hotkeys_change_cancelButton) { 1394 [NSApp stopModal]; 1395 [_hotkeys_change_win close]; 1396 } else if (sender == _hotkeys_change_okButton) { 1397 NSInteger i_returnValue; 1398 if (! _keyInTransition) { 1399 [NSApp stopModal]; 1400 [_hotkeys_change_win close]; 1401 msg_Err(p_intf, "internal error prevented the hotkey switch"); 1402 return; 1403 } 1404 1405 _hotkeyChanged = YES; 1406 1407 i_returnValue = [_hotkeySettings indexOfObject: _keyInTransition]; 1408 if (i_returnValue != NSNotFound) 1409 [_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]]; 1410 NSString *tempString; 1411 tempString = [_keyInTransition stringByReplacingOccurrencesOfString:@"-" withString:@"+"]; 1412 i_returnValue = [_hotkeySettings indexOfObject: tempString]; 1413 if (i_returnValue != NSNotFound) 1414 [_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]]; 1415 1416 [_hotkeySettings replaceObjectAtIndex: [_hotkeys_listbox selectedRow] withObject:_keyInTransition]; 1417 1418 [NSApp stopModal]; 1419 [_hotkeys_change_win close]; 1420 1421 [_hotkeys_listbox reloadData]; 1422 } else if (sender == _hotkeys_clearButton) { 1423 [_hotkeySettings replaceObjectAtIndex: [_hotkeys_listbox selectedRow] withObject: [NSString string]]; 1424 [_hotkeys_listbox reloadData]; 1425 _hotkeyChanged = YES; 1426 } 1427} 1428 1429- (void)showHotkeySettings 1430{ 1431 [self showSettingsForCategory:_hotkeysView]; 1432} 1433 1434- (int)numberOfRowsInTableView:(NSTableView *)aTableView 1435{ 1436 return [_hotkeySettings count]; 1437} 1438 1439- (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex 1440{ 1441 NSString * identifier = [aTableColumn identifier]; 1442 1443 if ([identifier isEqualToString: @"action"]) 1444 return [_hotkeyDescriptions objectAtIndex:rowIndex]; 1445 else if ([identifier isEqualToString: @"shortcut"]) 1446 return [[VLCStringUtility sharedInstance] OSXStringKeyToString:[_hotkeySettings objectAtIndex:rowIndex]]; 1447 else { 1448 msg_Err(p_intf, "unknown TableColumn identifier (%s)!", [identifier UTF8String]); 1449 return NULL; 1450 } 1451} 1452 1453- (BOOL)changeHotkeyTo: (NSString *)theKey 1454{ 1455 NSInteger i_returnValue, i_returnValue2; 1456 i_returnValue = [_hotkeysNonUseableKeys indexOfObject: theKey]; 1457 1458 if (i_returnValue != NSNotFound || [theKey isEqualToString:@""]) { 1459 [_hotkeys_change_keysLabel setStringValue: _NS("Invalid combination")]; 1460 [_hotkeys_change_takenLabel setStringValue: _NS("Regrettably, these keys cannot be assigned as hotkey shortcuts.")]; 1461 [_hotkeys_change_okButton setEnabled: NO]; 1462 return NO; 1463 } else { 1464 [_hotkeys_change_keysLabel setStringValue: [[VLCStringUtility sharedInstance] OSXStringKeyToString:theKey]]; 1465 1466 i_returnValue = [_hotkeySettings indexOfObject: theKey]; 1467 i_returnValue2 = [_hotkeySettings indexOfObject: [theKey stringByReplacingOccurrencesOfString:@"-" withString:@"+"]]; 1468 if (i_returnValue != NSNotFound) 1469 [_hotkeys_change_takenLabel setStringValue: [NSString stringWithFormat: 1470 _NS("This combination is already taken by \"%@\"."), 1471 [_hotkeyDescriptions objectAtIndex:i_returnValue]]]; 1472 else if (i_returnValue2 != NSNotFound) 1473 [_hotkeys_change_takenLabel setStringValue: [NSString stringWithFormat: 1474 _NS("This combination is already taken by \"%@\"."), 1475 [_hotkeyDescriptions objectAtIndex:i_returnValue2]]]; 1476 else 1477 [_hotkeys_change_takenLabel setStringValue: @""]; 1478 1479 [_hotkeys_change_okButton setEnabled: YES]; 1480 _keyInTransition = theKey; 1481 return YES; 1482 } 1483} 1484 1485@end 1486