]> git.sesse.net Git - vlc/blob - modules/gui/macosx/simple_prefs.m
macosx: fixes a memory leak.
[vlc] / modules / gui / macosx / simple_prefs.m
1 /*****************************************************************************
2 * simple_prefs.m: Simple Preferences for Mac OS X
3 *****************************************************************************
4 * Copyright (C) 2008-2011 VLC authors and VideoLAN
5 * $Id$
6 *
7 * Authors: Felix Paul Kühne <fkuehne at videolan dot org>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
23
24 #import "CompatibilityFixes.h"
25 #import "simple_prefs.h"
26 #import "prefs.h"
27 #import <vlc_keys.h>
28 #import <vlc_interface.h>
29 #import <vlc_dialog.h>
30 #import <vlc_modules.h>
31 #import "misc.h"
32 #import "intf.h"
33 #import "AppleRemote.h"
34 #import <Sparkle/Sparkle.h>                        //for o_intf_last_update_lbl
35
36 static NSString* VLCSPrefsToolbarIdentifier = @"Our Simple Preferences Toolbar Identifier";
37 static NSString* VLCIntfSettingToolbarIdentifier = @"Intf Settings Item Identifier";
38 static NSString* VLCAudioSettingToolbarIdentifier = @"Audio Settings Item Identifier";
39 static NSString* VLCVideoSettingToolbarIdentifier = @"Video Settings Item Identifier";
40 static NSString* VLCOSDSettingToolbarIdentifier = @"Subtitles Settings Item Identifier";
41 static NSString* VLCInputSettingToolbarIdentifier = @"Input Settings Item Identifier";
42 static NSString* VLCHotkeysSettingToolbarIdentifier = @"Hotkeys Settings Item Identifier";
43
44 @implementation VLCSimplePrefs
45
46 static VLCSimplePrefs *_o_sharedInstance = nil;
47
48 + (VLCSimplePrefs *)sharedInstance
49 {
50     return _o_sharedInstance ? _o_sharedInstance : [[self alloc] init];
51 }
52
53 - (id)init
54 {
55     if (_o_sharedInstance) {
56         [self dealloc];
57     } else {
58         _o_sharedInstance = [super init];
59         p_intf = VLCIntf;
60     }
61
62     return _o_sharedInstance;
63 }
64
65 - (void)dealloc
66 {
67     [o_currentlyShownCategoryView release];
68
69     [o_hotkeySettings release];
70     [o_hotkeyDescriptions release];
71     [o_hotkeyNames release];
72     [o_hotkeysNonUseableKeys release];
73
74     [o_keyInTransition release];
75
76     [super dealloc];
77 }
78
79 - (NSString *)OSXStringKeyToString:(NSString *)theString
80 {
81     if (![theString isEqualToString:@""]) {
82         /* remove cruft */
83         if ([theString characterAtIndex:([theString length] - 1)] != 0x2b)
84             theString = [theString stringByReplacingOccurrencesOfString:@"+" withString:@""];
85         else
86         {
87             theString = [theString stringByReplacingOccurrencesOfString:@"+" withString:@""];
88             theString = [NSString stringWithFormat:@"%@+", theString];
89         }
90         if ([theString characterAtIndex:([theString length] - 1)] != 0x2d)
91             theString = [theString stringByReplacingOccurrencesOfString:@"-" withString:@""];
92         else
93         {
94             theString = [theString stringByReplacingOccurrencesOfString:@"-" withString:@""];
95             theString = [NSString stringWithFormat:@"%@-", theString];
96         }
97         /* modifiers */
98         theString = [theString stringByReplacingOccurrencesOfString:@"Command" withString: [NSString stringWithUTF8String: "\xE2\x8C\x98"]];
99         theString = [theString stringByReplacingOccurrencesOfString:@"Alt" withString: [NSString stringWithUTF8String: "\xE2\x8C\xA5"]];
100         theString = [theString stringByReplacingOccurrencesOfString:@"Shift" withString: [NSString stringWithUTF8String: "\xE2\x87\xA7"]];
101         theString = [theString stringByReplacingOccurrencesOfString:@"Ctrl" withString: [NSString stringWithUTF8String: "\xE2\x8C\x83"]];
102         /* show non-character keys correctly */
103         theString = [theString stringByReplacingOccurrencesOfString:@"Right" withString:[NSString stringWithUTF8String:"\xE2\x86\x92"]];
104         theString = [theString stringByReplacingOccurrencesOfString:@"Left" withString:[NSString stringWithUTF8String:"\xE2\x86\x90"]];
105         theString = [theString stringByReplacingOccurrencesOfString:@"Up" withString:[NSString stringWithUTF8String:"\xE2\x86\x91"]];
106         theString = [theString stringByReplacingOccurrencesOfString:@"Down" withString:[NSString stringWithUTF8String:"\xE2\x86\x93"]];
107         theString = [theString stringByReplacingOccurrencesOfString:@"Enter" withString:[NSString stringWithUTF8String:"\xe2\x86\xb5"]];
108         theString = [theString stringByReplacingOccurrencesOfString:@"Tab" withString:[NSString stringWithUTF8String:"\xe2\x87\xa5"]];
109         theString = [theString stringByReplacingOccurrencesOfString:@"Delete" withString:[NSString stringWithUTF8String:"\xe2\x8c\xab"]];        /* capitalize plain characters to suit the menubar's look */
110         theString = [theString capitalizedString];
111     }
112     else
113         theString = [NSString stringWithString:_NS("Not Set")];
114     return theString;
115 }
116
117 - (void)awakeFromNib
118 {
119     [self initStrings];
120
121     /* setup the toolbar */
122     NSToolbar * o_sprefs_toolbar = [[[NSToolbar alloc] initWithIdentifier: VLCSPrefsToolbarIdentifier] autorelease];
123     [o_sprefs_toolbar setAllowsUserCustomization: NO];
124     [o_sprefs_toolbar setAutosavesConfiguration: NO];
125     [o_sprefs_toolbar setDisplayMode: NSToolbarDisplayModeIconAndLabel];
126     [o_sprefs_toolbar setSizeMode: NSToolbarSizeModeRegular];
127     [o_sprefs_toolbar setDelegate: self];
128     [o_sprefs_win setToolbar: o_sprefs_toolbar];
129
130     if (OSX_LION)
131         [o_sprefs_win setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
132     else
133         [o_intf_nativefullscreen_ckb setEnabled:NO];
134
135     /* setup useful stuff */
136     o_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-z", @"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-?", nil] retain];
137 }
138
139 #define CreateToolbarItem( o_name, o_desc, o_img, sel ) \
140     o_toolbarItem = create_toolbar_item(o_itemIdent, o_name, o_desc, o_img, self, @selector(sel));
141 static inline NSToolbarItem *
142 create_toolbar_item( NSString * o_itemIdent, NSString * o_name, NSString * o_desc, NSString * o_img, id target, SEL selector )
143 {
144     NSToolbarItem *o_toolbarItem = [[[NSToolbarItem alloc] initWithItemIdentifier: o_itemIdent] autorelease]; \
145
146     [o_toolbarItem setLabel: o_name];
147     [o_toolbarItem setPaletteLabel: o_desc];
148
149     [o_toolbarItem setToolTip: o_desc];
150     [o_toolbarItem setImage: [NSImage imageNamed: o_img]];
151
152     [o_toolbarItem setTarget: target];
153     [o_toolbarItem setAction: selector];
154
155     [o_toolbarItem setEnabled: YES];
156     [o_toolbarItem setAutovalidates: YES];
157
158     return o_toolbarItem;
159 }
160
161 - (NSToolbarItem *) toolbar: (NSToolbar *)o_sprefs_toolbar
162       itemForItemIdentifier: (NSString *)o_itemIdent
163   willBeInsertedIntoToolbar: (BOOL)b_willBeInserted
164 {
165     NSToolbarItem *o_toolbarItem = nil;
166
167     if( [o_itemIdent isEqual: VLCIntfSettingToolbarIdentifier] )
168     {
169         CreateToolbarItem( _NS("Interface"), _NS("Interface Settings"), @"spref_cone_Interface_64", showInterfaceSettings );
170     }
171     else if( [o_itemIdent isEqual: VLCAudioSettingToolbarIdentifier] )
172     {
173         CreateToolbarItem( _NS("Audio"), _NS("General Audio Settings"), @"spref_cone_Audio_64", showAudioSettings );
174     }
175     else if( [o_itemIdent isEqual: VLCVideoSettingToolbarIdentifier] )
176     {
177         CreateToolbarItem( _NS("Video"), _NS("General Video Settings"), @"spref_cone_Video_64", showVideoSettings );
178     }
179     else if( [o_itemIdent isEqual: VLCOSDSettingToolbarIdentifier] )
180     {
181         CreateToolbarItem( _NS("Subtitles & OSD"), _NS("Subtitles & On Screen Display Settings"), @"spref_cone_Subtitles_64", showOSDSettings );
182     }
183     else if( [o_itemIdent isEqual: VLCInputSettingToolbarIdentifier] )
184     {
185         CreateToolbarItem( _NS("Input & Codecs"), _NS("Input & Codec settings"), @"spref_cone_Input_64", showInputSettings );
186     }
187     else if( [o_itemIdent isEqual: VLCHotkeysSettingToolbarIdentifier] )
188     {
189         CreateToolbarItem( _NS("Hotkeys"), _NS("Hotkeys settings"), @"spref_cone_Hotkeys_64", showHotkeySettings );
190     }
191
192     return o_toolbarItem;
193 }
194
195 - (NSArray *)toolbarDefaultItemIdentifiers: (NSToolbar *)toolbar
196 {
197     return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
198         VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, NSToolbarFlexibleSpaceItemIdentifier, nil];
199 }
200
201 - (NSArray *)toolbarAllowedItemIdentifiers: (NSToolbar *)toolbar
202 {
203     return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
204         VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, NSToolbarFlexibleSpaceItemIdentifier, nil];
205 }
206
207 - (NSArray *)toolbarSelectableItemIdentifiers:(NSToolbar *)toolbar
208 {
209     return [NSArray arrayWithObjects: VLCIntfSettingToolbarIdentifier, VLCAudioSettingToolbarIdentifier, VLCVideoSettingToolbarIdentifier,
210         VLCOSDSettingToolbarIdentifier, VLCInputSettingToolbarIdentifier, VLCHotkeysSettingToolbarIdentifier, nil];
211 }
212
213 - (void)initStrings
214 {
215     /* audio */
216     [o_audio_dolby_txt setStringValue: _NS("Force detection of Dolby Surround")];
217     [o_audio_effects_box setTitle: _NS("Effects")];
218     [o_audio_enable_ckb setTitle: _NS("Enable Audio")];
219     [o_audio_general_box setTitle: _NS("General Audio")];
220     [o_audio_lang_txt setStringValue: _NS("Preferred Audio language")];
221     [o_audio_last_ckb setTitle: _NS("Enable Last.fm submissions")];
222     [o_audio_lastpwd_txt setStringValue: _NS("Password")];
223     [o_audio_lastuser_txt setStringValue: _NS("User name")];
224     [o_audio_spdif_ckb setTitle: _NS("Use S/PDIF when available")];
225     [o_audio_visual_txt setStringValue: _NS("Visualization")];
226     [o_audio_vol_txt setStringValue: _NS("Default Volume")];
227
228     /* hotkeys */
229     [o_hotkeys_change_btn setTitle: _NS("Change")];
230     [o_hotkeys_change_win setTitle: _NS("Change Hotkey")];
231     [o_hotkeys_change_cancel_btn setTitle: _NS("Cancel")];
232     [o_hotkeys_change_ok_btn setTitle: _NS("OK")];
233     [o_hotkeys_clear_btn setTitle: _NS("Clear")];
234     [o_hotkeys_lbl setStringValue: _NS("Select an action to change the associated hotkey:")];
235     [[[o_hotkeys_listbox tableColumnWithIdentifier: @"action"] headerCell] setStringValue: _NS("Action")];
236     [[[o_hotkeys_listbox tableColumnWithIdentifier: @"shortcut"] headerCell] setStringValue: _NS("Shortcut")];
237
238     /* input */
239     [o_input_record_box setTitle: _NS("Record directory or filename")];
240     [o_input_record_btn setTitle: _NS("Browse...")];
241     [o_input_record_btn setToolTip: _NS("Directory or filename where the records will be stored")];
242     [o_input_avi_txt setStringValue: _NS("Repair AVI Files")];
243     [o_input_cachelevel_txt setStringValue: _NS("Default Caching Level")];
244     [o_input_caching_box setTitle: _NS("Caching")];
245     [o_input_cachelevel_custom_txt setStringValue: _NS("Use the complete preferences to configure custom caching values for each access module.")];
246     [o_input_httpproxy_txt setStringValue: _NS("HTTP Proxy")];
247     [o_input_httpproxypwd_txt setStringValue: _NS("Password for HTTP Proxy")];
248     [o_input_mux_box setTitle: _NS("Codecs / Muxers")];
249     [o_input_net_box setTitle: _NS("Network")];
250     [o_input_postproc_txt setStringValue: _NS("Post-Processing Quality")];
251     [o_input_rtsp_ckb setTitle: _NS("Use RTP over RTSP (TCP)")];
252     [o_input_skipLoop_txt setStringValue: _NS("Skip the loop filter for H.264 decoding")];
253
254     /* interface */
255     [o_intf_style_txt setStringValue: _NS("Interface style")];
256     [o_intf_style_dark_bcell setTitle: _NS("Dark")];
257     [o_intf_style_bright_bcell setTitle: _NS("Bright")];
258     [o_intf_art_txt setStringValue: _NS("Album art download policy")];
259     [o_intf_embedded_ckb setTitle: _NS("Show video within the main window")];
260     [o_intf_nativefullscreen_ckb setTitle: _NS("Use the native fullscreen mode on OS X Lion")];
261     [o_intf_fspanel_ckb setTitle: _NS("Show Fullscreen Controller")];
262     [o_intf_lang_txt setStringValue: _NS("Language")];
263     [o_intf_network_box setTitle: _NS("Privacy / Network Interaction")];
264         [o_intf_appleremote_ckb setTitle: _NS("Control playback with the Apple Remote")];
265         [o_intf_mediakeys_ckb setTitle: _NS("Control playback with media keys")];
266     [o_intf_update_ckb setTitle: _NS("Automatically check for updates")];
267     [o_intf_last_update_lbl setStringValue: @""];
268     [o_intf_enableGrowl_ckb setTitle: _NS("Enable Growl notifications (on playlist item change)")];
269
270     /* Subtitles and OSD */
271     [o_osd_encoding_txt setStringValue: _NS("Default Encoding")];
272     [o_osd_font_box setTitle: _NS("Display Settings")];
273     [o_osd_font_btn setTitle: _NS("Choose...")];
274     [o_osd_font_color_txt setStringValue: _NS("Font Color")];
275     [o_osd_font_size_txt setStringValue: _NS("Font Size")];
276     [o_osd_font_txt setStringValue: _NS("Font")];
277     [o_osd_lang_box setTitle: _NS("Subtitle Languages")];
278     [o_osd_lang_txt setStringValue: _NS("Preferred Subtitle Language")];
279     [o_osd_osd_box setTitle: _NS("On Screen Display")];
280     [o_osd_osd_ckb setTitle: _NS("Enable OSD")];
281     [o_osd_opacity_txt setStringValue: _NS("Opacity")];
282     [o_osd_forcebold_ckb setTitle: _NS("Force Bold")];
283     [o_osd_moreoptions_txt setStringValue: _NS("More options on background, shadow and outline are available in the advanced preferences.")];
284
285     /* video */
286     [o_video_black_ckb setTitle: _NS("Black screens in Fullscreen mode")];
287     [o_video_device_txt setStringValue: _NS("Fullscreen Video Device")];
288     [o_video_display_box setTitle: _NS("Display")];
289     [o_video_enable_ckb setTitle: _NS("Enable Video")];
290     [o_video_fullscreen_ckb setTitle: _NS("Fullscreen")];
291     [o_video_onTop_ckb setTitle: _NS("Always on top")];
292     [o_video_output_txt setStringValue: _NS("Output module")];
293     [o_video_skipFrames_ckb setTitle: _NS("Skip frames")];
294     [o_video_snap_box setTitle: _NS("Video snapshots")];
295     [o_video_snap_folder_btn setTitle: _NS("Browse...")];
296     [o_video_snap_folder_txt setStringValue: _NS("Folder")];
297     [o_video_snap_format_txt setStringValue: _NS("Format")];
298     [o_video_snap_prefix_txt setStringValue: _NS("Prefix")];
299     [o_video_snap_seqnum_ckb setTitle: _NS("Sequential numbering")];
300
301     /* generic stuff */
302     [o_sprefs_showAll_btn setTitle: _NS("Show All")];
303     [o_sprefs_cancel_btn setTitle: _NS("Cancel")];
304     [o_sprefs_reset_btn setTitle: _NS("Reset All")];
305     [o_sprefs_save_btn setTitle: _NS("Save")];
306     [o_sprefs_win setTitle: _NS("Preferences")];
307 }
308
309 /* TODO: move this part to core */
310 #define config_GetLabel(a,b) __config_GetLabel(VLC_OBJECT(a),b)
311 static inline char * __config_GetLabel( vlc_object_t *p_this, const char *psz_name )
312 {
313     module_config_t *p_config;
314
315     p_config = config_FindConfig( p_this, psz_name );
316
317     /* sanity checks */
318     if( !p_config )
319     {
320         msg_Err( p_this, "option %s does not exist", psz_name );
321         return NULL;
322     }
323
324     if ( p_config->psz_longtext )
325         return p_config->psz_longtext;
326     else if( p_config->psz_text )
327         return p_config->psz_text;
328     else
329         msg_Warn( p_this, "option %s does not include any help", psz_name );
330
331     return NULL;
332 }
333
334 - (void)setupButton: (NSPopUpButton *)object forStringList: (const char *)name
335 {
336     module_config_t *p_item;
337
338     [object removeAllItems];
339     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
340
341     /* serious problem, if no item found */
342     assert( p_item );
343
344     for( int i = 0; i < p_item->i_list; i++ )
345     {
346         NSMenuItem *mi;
347         if( p_item->ppsz_list_text != NULL )
348             mi = [[NSMenuItem alloc] initWithTitle: _NS( p_item->ppsz_list_text[i] ) action:NULL keyEquivalent: @""];
349         else if( p_item->ppsz_list[i] && strcmp(p_item->ppsz_list[i],"") == 0 )
350         {
351             [[object menu] addItem: [NSMenuItem separatorItem]];
352             continue;
353         }
354         else if( p_item->ppsz_list[i] )
355             mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithUTF8String: p_item->ppsz_list[i]] action:NULL keyEquivalent: @""];
356         else
357             msg_Err( p_intf, "item %d of pref %s failed to be created", i, name );
358         [mi setRepresentedObject:[NSString stringWithUTF8String: p_item->ppsz_list[i]]];
359         [[object menu] addItem: [mi autorelease]];
360         if( p_item->value.psz && !strcmp( p_item->value.psz, p_item->ppsz_list[i] ) )
361             [object selectItem:[object lastItem]];
362     }
363     [object setToolTip: _NS( p_item->psz_longtext )];
364 }
365
366 - (void)setupButton: (NSPopUpButton *)object forIntList: (const char *)name
367 {
368     module_config_t *p_item;
369
370     [object removeAllItems];
371     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
372
373     /* serious problem, if no item found */
374     assert( p_item );
375
376     for( int i = 0; i < p_item->i_list; i++ )
377     {
378         NSMenuItem *mi;
379         if( p_item->ppsz_list_text != NULL)
380             mi = [[NSMenuItem alloc] initWithTitle: _NS( p_item->ppsz_list_text[i] ) action:NULL keyEquivalent: @""];
381         else if( p_item->pi_list[i] )
382             mi = [[NSMenuItem alloc] initWithTitle: [NSString stringWithFormat: @"%d", p_item->pi_list[i]] action:NULL keyEquivalent: @""];
383         else
384             msg_Err( p_intf, "item %d of pref %s failed to be created", i, name);
385         [mi setRepresentedObject:[NSNumber numberWithInt: p_item->pi_list[i]]];
386         [[object menu] addItem: [mi autorelease]];
387         if( p_item->value.i == p_item->pi_list[i] )
388             [object selectItem:[object lastItem]];
389     }
390     [object setToolTip: _NS( p_item->psz_longtext )];
391 }
392
393 - (void)setupButton: (NSPopUpButton *)object forModuleList: (const char *)name
394 {
395     module_config_t *p_item;
396     module_t *p_parser, **p_list;
397     int y = 0;
398
399     [object removeAllItems];
400
401     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
402     p_list = module_list_get( NULL );
403     if( !p_item ||!p_list )
404     {
405         if( p_list ) module_list_free(p_list);
406         msg_Err( p_intf, "serious problem, item or list not found" );
407         return;
408     }
409
410     [object addItemWithTitle: _NS("Default")];
411     for( size_t i_index = 0; p_list[i_index]; i_index++ )
412     {
413         p_parser = p_list[i_index];
414         if( module_provides( p_parser, p_item->psz_type ) )
415         {
416             [object addItemWithTitle: [NSString stringWithUTF8String: module_GetLongName( p_parser ) ?: ""]];
417             if( p_item->value.psz && !strcmp( p_item->value.psz, module_get_object( p_parser ) ) )
418                 [object selectItem: [object lastItem]];
419         }
420     }
421     module_list_free( p_list );
422     [object setToolTip: _NS(p_item->psz_longtext)];
423 }
424
425 - (void)setupButton: (NSButton *)object forBoolValue: (const char *)name
426 {
427     [object setState: config_GetInt( p_intf, name )];
428     [object setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, name )]];
429 }
430
431 - (void)setupField:(NSTextField *)o_object forOption:(const char *)psz_option
432 {
433     char *psz_tmp = config_GetPsz( p_intf, psz_option );
434     [o_object setStringValue: [NSString stringWithUTF8String: psz_tmp ?: ""]];
435     [o_object setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, psz_option )]];
436     free( psz_tmp );
437 }
438
439 - (void)resetControls
440 {
441     module_config_t *p_item;
442     int i, y = 0;
443     char *psz_tmp;
444
445     /**********************
446      * interface settings *
447      **********************/
448     [self setupButton: o_intf_lang_pop forStringList: "language"];
449     [self setupButton: o_intf_art_pop forIntList: "album-art"];
450
451     [self setupButton: o_intf_fspanel_ckb forBoolValue: "macosx-fspanel"];
452     [self setupButton: o_intf_nativefullscreen_ckb forBoolValue: "macosx-nativefullscreenmode"];
453     [self setupButton: o_intf_embedded_ckb forBoolValue: "embedded-video"];
454         [self setupButton: o_intf_appleremote_ckb forBoolValue: "macosx-appleremote"];
455         [self setupButton: o_intf_mediakeys_ckb forBoolValue: "macosx-mediakeys"];
456     if( [[SUUpdater sharedUpdater] lastUpdateCheckDate] != NULL )
457         [o_intf_last_update_lbl setStringValue: [NSString stringWithFormat: _NS("Last check on: %@"), [[[SUUpdater sharedUpdater] lastUpdateCheckDate] descriptionWithLocale: [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]]]];
458     else
459         [o_intf_last_update_lbl setStringValue: _NS("No check was performed yet.")];
460     psz_tmp = config_GetPsz( p_intf, "control" );
461     if (psz_tmp) {
462         [o_intf_enableGrowl_ckb setState: (NSInteger)strstr( psz_tmp, "growl")];
463         free( psz_tmp );
464     }
465     else
466         [o_intf_enableGrowl_ckb setState: NSOffState];
467     if (config_GetInt( p_intf, "macosx-interfacestyle" ))
468     {
469         [o_intf_style_dark_bcell setState: YES];
470         [o_intf_style_bright_bcell setState: NO];
471     }
472     else
473     {
474         [o_intf_style_dark_bcell setState: NO];
475         [o_intf_style_bright_bcell setState: YES];
476     }
477
478     /******************
479      * audio settings *
480      ******************/
481     [self setupButton: o_audio_enable_ckb forBoolValue: "audio"];
482     i = (config_GetInt( p_intf, "volume" ) * 0.390625);
483     [o_audio_vol_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "volume")]];
484     [o_audio_vol_fld setIntValue: i];
485     [o_audio_vol_sld setToolTip: [o_audio_vol_fld toolTip]];
486     [o_audio_vol_sld setIntValue: i];
487
488     [self setupButton: o_audio_spdif_ckb forBoolValue: "spdif"];
489
490     [self setupButton: o_audio_dolby_pop forIntList: "force-dolby-surround"];
491     [self setupField: o_audio_lang_fld forOption: "audio-language"];
492
493     [self setupButton: o_audio_visual_pop forModuleList: "audio-visual"];
494
495     /* Last.FM is optional */
496     if( module_exists( "audioscrobbler" ) )
497     {
498         [self setupField: o_audio_lastuser_fld forOption:"lastfm-username"];
499         [self setupField: o_audio_lastpwd_sfld forOption:"lastfm-password"];
500
501         if( config_ExistIntf( VLC_OBJECT( p_intf ), "audioscrobbler" ) )
502         {
503             [o_audio_last_ckb setState: NSOnState];
504             [o_audio_lastuser_fld setEnabled: YES];
505             [o_audio_lastpwd_sfld setEnabled: YES];
506         }
507         else
508         {
509             [o_audio_last_ckb setState: NSOffState];
510             [o_audio_lastuser_fld setEnabled: NO];
511             [o_audio_lastpwd_sfld setEnabled: NO];
512         }
513     }
514     else
515         [o_audio_last_ckb setEnabled: NO];
516
517     /******************
518      * video settings *
519      ******************/
520     [self setupButton: o_video_enable_ckb forBoolValue: "video"];
521     [self setupButton: o_video_fullscreen_ckb forBoolValue: "fullscreen"];
522     [self setupButton: o_video_onTop_ckb forBoolValue: "video-on-top"];
523     [self setupButton: o_video_skipFrames_ckb forBoolValue: "skip-frames"];
524     [self setupButton: o_video_black_ckb forBoolValue: "macosx-black"];
525
526     [self setupButton: o_video_output_pop forModuleList: "vout"];
527
528     [o_video_device_pop removeAllItems];
529     i = 0;
530     y = [[NSScreen screens] count];
531     [o_video_device_pop addItemWithTitle: _NS("Default")];
532     [[o_video_device_pop lastItem] setTag: 0];
533     while( i < y )
534     {
535         NSRect s_rect = [[[NSScreen screens] objectAtIndex: i] frame];
536         [o_video_device_pop addItemWithTitle:
537          [NSString stringWithFormat: @"%@ %i (%ix%i)", _NS("Screen"), i+1,
538                    (int)s_rect.size.width, (int)s_rect.size.height]];
539         [[o_video_device_pop lastItem] setTag: (int)[[[NSScreen screens] objectAtIndex: i] displayID]];
540         i++;
541     }
542     [o_video_device_pop selectItemAtIndex: 0];
543     [o_video_device_pop selectItemWithTag: config_GetInt( p_intf, "macosx-vdev" )];
544
545     [self setupField: o_video_snap_folder_fld forOption:"snapshot-path"];
546     [self setupField: o_video_snap_prefix_fld forOption:"snapshot-prefix"];
547     [self setupButton: o_video_snap_seqnum_ckb forBoolValue: "snapshot-sequential"];
548     [self setupButton: o_video_snap_format_pop forStringList: "snapshot-format"];
549
550     /***************************
551      * input & codecs settings *
552      ***************************/
553     [self setupField: o_input_record_fld forOption:"input-record-path"];
554     [self setupField: o_input_httpproxy_fld forOption:"http-proxy"];
555     [self setupField: o_input_httpproxypwd_sfld forOption:"http-proxy-pwd"];
556     [o_input_postproc_fld setIntValue: config_GetInt( p_intf, "postproc-q")];
557     [o_input_postproc_fld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "postproc-q")]];
558
559     [self setupButton: o_input_avi_pop forIntList: "avi-index"];
560
561     [self setupButton: o_input_rtsp_ckb forBoolValue: "rtsp-tcp"];
562     [self setupButton: o_input_skipLoop_pop forIntList: "ffmpeg-skiploopfilter"];
563
564     [o_input_cachelevel_pop removeAllItems];
565     [o_input_cachelevel_pop addItemsWithTitles:
566         [NSArray arrayWithObjects: _NS("Custom"), _NS("Lowest latency"), _NS("Low latency"), _NS("Normal"),
567             _NS("High latency"), _NS("Higher latency"), nil]];
568     [[o_input_cachelevel_pop itemAtIndex: 0] setTag: 0];
569     [[o_input_cachelevel_pop itemAtIndex: 1] setTag: 100];
570     [[o_input_cachelevel_pop itemAtIndex: 2] setTag: 200];
571     [[o_input_cachelevel_pop itemAtIndex: 3] setTag: 300];
572     [[o_input_cachelevel_pop itemAtIndex: 4] setTag: 400];
573     [[o_input_cachelevel_pop itemAtIndex: 5] setTag: 500];
574
575 #define TestCaC( name ) \
576     b_cache_equal =  b_cache_equal && \
577         ( i_cache == config_GetInt( p_intf, name ) )
578
579 #define TestCaCi( name, int ) \
580         b_cache_equal = b_cache_equal &&  \
581         ( ( i_cache * int ) == config_GetInt( p_intf, name ) )
582
583     /* Select the accurate value of the PopupButton */
584     bool b_cache_equal = true;
585     int i_cache = config_GetInt( p_intf, "file-caching");
586
587     TestCaC( "network-caching" );
588     TestCaC( "disc-caching" );
589     TestCaC( "live-caching" );
590     if( b_cache_equal )
591     {
592         [o_input_cachelevel_pop selectItemWithTag: i_cache];
593         [o_input_cachelevel_custom_txt setHidden: YES];
594     }
595     else
596     {
597         [o_input_cachelevel_pop selectItemWithTitle: _NS("Custom")];
598         [o_input_cachelevel_custom_txt setHidden: NO];
599     }
600
601     /*********************
602      * subtitle settings *
603      *********************/
604     [self setupButton: o_osd_osd_ckb forBoolValue: "osd"];
605
606     [self setupButton: o_osd_encoding_pop forStringList: "subsdec-encoding"];
607     [self setupField: o_osd_lang_fld forOption: "sub-language" ];
608
609     [self setupField: o_osd_font_fld forOption: "freetype-font"];
610     [self setupButton: o_osd_font_color_pop forIntList: "freetype-color"];
611     [self setupButton: o_osd_font_size_pop forIntList: "freetype-rel-fontsize"];
612     i = config_GetInt( p_intf, "freetype-opacity" );
613     [o_osd_opacity_fld setIntValue: i];
614     [o_osd_opacity_sld setIntValue: i];
615     [o_osd_opacity_sld setToolTip: [NSString stringWithUTF8String: config_GetLabel( p_intf, "freetype-opacity")]];
616     [o_osd_opacity_fld setToolTip: [o_osd_opacity_sld toolTip]];
617     [self setupButton: o_osd_forcebold_ckb forBoolValue: "freetype-bold"];
618
619     /********************
620      * hotkeys settings *
621      ********************/
622     const struct hotkey *p_hotkeys = p_intf->p_libvlc->p_hotkeys;
623     [o_hotkeySettings release];
624     o_hotkeySettings = [[NSMutableArray alloc] init];
625     NSMutableArray *o_tempArray_desc = [[NSMutableArray alloc] init];
626     NSMutableArray *o_tempArray_names = [[NSMutableArray alloc] init];
627
628     /* Get the main Module */
629     module_t *p_main = module_get_main();
630     assert( p_main );
631     unsigned confsize;
632     module_config_t *p_config;
633     
634     p_config = module_config_get (p_main, &confsize);
635     
636     for (size_t i = 0; i < confsize; i++)
637     {
638         module_config_t *p_item = p_config + i;
639
640         if( CONFIG_ITEM(p_item->i_type) && p_item->psz_name != NULL
641            && !strncmp( p_item->psz_name , "key-", 4 )
642            && !EMPTY_STR( p_item->psz_text ) )
643         {
644             [o_tempArray_desc addObject: _NS( p_item->psz_text )];
645             [o_tempArray_names addObject: [NSString stringWithUTF8String:p_item->psz_name]];
646             if (p_item->value.psz)
647                 [o_hotkeySettings addObject: [NSString stringWithUTF8String:p_item->value.psz]];
648             else
649                 [o_hotkeySettings addObject: [NSString string]];
650         }
651     }
652     module_config_free (p_config);
653
654     [o_hotkeyDescriptions release];
655     o_hotkeyDescriptions = [[NSArray alloc] initWithArray: o_tempArray_desc copyItems: YES];
656     [o_tempArray_desc release];
657     [o_hotkeyNames release];
658     o_hotkeyNames = [[NSArray alloc] initWithArray: o_tempArray_names copyItems: YES];
659     [o_tempArray_names release];
660     [o_hotkeys_listbox reloadData];
661 }
662
663 - (void)showSimplePrefs
664 {
665     /* we want to show the interface settings, if no category was chosen */
666     if( [[o_sprefs_win toolbar] selectedItemIdentifier] == nil )
667     {
668         [[o_sprefs_win toolbar] setSelectedItemIdentifier: VLCIntfSettingToolbarIdentifier];
669         [self showInterfaceSettings];
670     }
671
672     [self resetControls];
673
674     [o_sprefs_win center];
675     [o_sprefs_win makeKeyAndOrderFront: self];
676 }
677
678 - (IBAction)buttonAction:(id)sender
679 {
680     if( sender == o_sprefs_cancel_btn )
681         [o_sprefs_win orderOut: sender];
682     else if( sender == o_sprefs_save_btn )
683     {
684         [self saveChangedSettings];
685         [o_sprefs_win orderOut: sender];
686     }
687     else if( sender == o_sprefs_reset_btn )
688         NSBeginInformationalAlertSheet( _NS("Reset Preferences"), _NS("Cancel"),
689                                         _NS("Continue"), nil, o_sprefs_win, self,
690                                         @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil,
691                                         _NS("Beware this will reset the VLC media player preferences.\n"
692                                             "Are you sure you want to continue?") );
693     else if( sender == o_sprefs_showAll_btn )
694     {
695         [o_sprefs_win orderOut: self];
696         [[[VLCMain sharedInstance] preferences] showPrefs];
697     }
698     else
699         msg_Warn( p_intf, "unknown buttonAction sender" );
700 }
701
702 - (void)sheetDidEnd:(NSWindow *)o_sheet
703          returnCode:(int)i_return
704         contextInfo:(void *)o_context
705 {
706     if( i_return == NSAlertAlternateReturn )
707     {
708         config_ResetAll( p_intf );
709         [self resetControls];
710         config_SaveConfigFile( p_intf );
711     }
712 }
713
714 static inline void save_int_list( intf_thread_t * p_intf, id object, const char * name )
715 {
716     NSNumber *p_valueobject;
717     module_config_t *p_item;
718     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
719     p_valueobject = (NSNumber *)[[object selectedItem] representedObject];
720     assert([p_valueobject isKindOfClass:[NSNumber class]]);
721     if( p_valueobject) config_PutInt( p_intf, name, [p_valueobject intValue] );
722 }
723
724 static inline void save_string_list( intf_thread_t * p_intf, id object, const char * name )
725 {
726     NSString *p_stringobject;
727     module_config_t *p_item;
728     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
729     p_stringobject = (NSString *)[[object selectedItem] representedObject];
730     assert([p_stringobject isKindOfClass:[NSString class]]);
731     if( p_stringobject )
732     {
733         config_PutPsz( p_intf, name, [p_stringobject UTF8String] );
734     }
735 }
736
737 static inline void save_module_list( intf_thread_t * p_intf, id object, const char * name )
738 {
739     module_config_t *p_item;
740     module_t *p_parser, **p_list;
741
742     p_item = config_FindConfig( VLC_OBJECT(p_intf), name );
743
744     p_list = module_list_get( NULL );
745     for( size_t i_module_index = 0; p_list[i_module_index]; i_module_index++ )
746     {
747         p_parser = p_list[i_module_index];
748
749         if( p_item->i_type == CONFIG_ITEM_MODULE && module_provides( p_parser, p_item->psz_type ) )
750         {
751             if( [[[object selectedItem] title] isEqualToString: _NS( module_GetLongName( p_parser ) )] )
752             {
753                 config_PutPsz( p_intf, name, strdup( module_get_object( p_parser )));
754                 break;
755             }
756         }
757     }
758     module_list_free( p_list );
759     if( [[[object selectedItem] title] isEqualToString: _NS( "Default" )] )
760         config_PutPsz( p_intf, name, "" );
761 }
762
763 - (void)saveChangedSettings
764 {
765     NSString *tmpString;
766     NSRange tmpRange;
767
768 #define SaveIntList( object, name ) save_int_list( p_intf, object, name )
769
770 #define SaveStringList( object, name ) save_string_list( p_intf, object, name )
771
772 #define SaveModuleList( object, name ) save_module_list( p_intf, object, name )
773
774 #define getString( name ) [NSString stringWithFormat:@"%s", config_GetPsz( p_intf, name )]
775
776     /**********************
777      * interface settings *
778      **********************/
779     if( b_intfSettingChanged )
780     {
781         SaveStringList( o_intf_lang_pop, "language" );
782         SaveIntList( o_intf_art_pop, "album-art" );
783
784         config_PutInt( p_intf, "macosx-fspanel", [o_intf_fspanel_ckb state] );
785         config_PutInt( p_intf, "embedded-video", [o_intf_embedded_ckb state] );
786                 config_PutInt( p_intf, "macosx-appleremote", [o_intf_appleremote_ckb state] );
787         config_PutInt( p_intf, "macosx-mediakeys", [o_intf_mediakeys_ckb state] );
788         config_PutInt( p_intf, "macosx-interfacestyle", [o_intf_style_dark_bcell state] );
789         config_PutInt( p_intf, "macosx-nativefullscreenmode", [o_intf_nativefullscreen_ckb state] );
790         if( [o_intf_enableGrowl_ckb state] == NSOnState )
791         {
792             tmpString = getString( "control" );
793             tmpRange = [tmpString rangeOfString:@"growl"];
794             if( [tmpString length] > 0 && tmpRange.location == NSNotFound )
795             {
796                 tmpString = [tmpString stringByAppendingString: @":growl"];
797                 config_PutPsz( p_intf, "control", [tmpString UTF8String] );
798             }
799             else
800                 config_PutPsz( p_intf, "control", "growl" );
801         }
802         else
803         {
804             tmpString = getString( "control" );
805             if(! [tmpString isEqualToString:@""] )
806             {
807                 tmpString = [tmpString stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@":growl"]];
808                 tmpString = [tmpString stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"growl:"]];
809                 tmpString = [tmpString stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:@"growl"]];
810                 config_PutPsz( p_intf, "control", [tmpString UTF8String] );
811             }
812         }
813
814                 /* activate stuff without restart */
815                 if( [o_intf_appleremote_ckb state] == YES )
816                         [[[VLCMain sharedInstance] appleRemoteController] startListening: [VLCMain sharedInstance]];
817                 else
818                         [[[VLCMain sharedInstance] appleRemoteController] stopListening: [VLCMain sharedInstance]];
819         b_intfSettingChanged = NO;
820     }
821
822     /******************
823      * audio settings *
824      ******************/
825     if( b_audioSettingChanged )
826     {
827         config_PutInt( p_intf, "audio", [o_audio_enable_ckb state] );
828         config_PutInt( p_intf, "volume", ([o_audio_vol_sld intValue] * 2.56));
829         config_PutInt( p_intf, "spdif", [o_audio_spdif_ckb state] );
830
831         SaveIntList( o_audio_dolby_pop, "force-dolby-surround" );
832
833         config_PutPsz( p_intf, "audio-language", [[o_audio_lang_fld stringValue] UTF8String] );
834
835         SaveModuleList( o_audio_visual_pop, "audio-visual" );
836
837         /* Last.FM is optional */
838         if( module_exists( "audioscrobbler" ) )
839         {
840             [o_audio_last_ckb setEnabled: YES];
841             if( [o_audio_last_ckb state] == NSOnState )
842                 config_AddIntf( p_intf, "audioscrobbler" );
843             else
844                 config_RemoveIntf( p_intf, "audioscrobbler" );
845
846             config_PutPsz( p_intf, "lastfm-username", [[o_audio_lastuser_fld stringValue] UTF8String] );
847             config_PutPsz( p_intf, "lastfm-password", [[o_audio_lastpwd_sfld stringValue] UTF8String] );
848         }
849         else
850             [o_audio_last_ckb setEnabled: NO];
851         b_audioSettingChanged = NO;
852     }
853
854     /******************
855      * video settings *
856      ******************/
857     if( b_videoSettingChanged )
858     {
859         config_PutInt( p_intf, "video", [o_video_enable_ckb state] );
860         config_PutInt( p_intf, "fullscreen", [o_video_fullscreen_ckb state] );
861         config_PutInt( p_intf, "video-on-top", [o_video_onTop_ckb state] );
862         config_PutInt( p_intf, "skip-frames", [o_video_skipFrames_ckb state] );
863         config_PutInt( p_intf, "macosx-black", [o_video_black_ckb state] );
864
865         SaveModuleList( o_video_output_pop, "vout" );
866         config_PutInt( p_intf, "macosx-vdev", [[o_video_device_pop selectedItem] tag] );
867
868         config_PutPsz( p_intf, "snapshot-path", [[o_video_snap_folder_fld stringValue] UTF8String] );
869         config_PutPsz( p_intf, "snapshot-prefix", [[o_video_snap_prefix_fld stringValue] UTF8String] );
870         config_PutInt( p_intf, "snapshot-sequential", [o_video_snap_seqnum_ckb state] );
871         SaveStringList( o_video_snap_format_pop, "snapshot-format" );
872         b_videoSettingChanged = NO;
873     }
874
875     /***************************
876      * input & codecs settings *
877      ***************************/
878     if( b_inputSettingChanged )
879     {
880         config_PutPsz( p_intf, "input-record-path", [[o_input_record_fld stringValue] UTF8String] );
881         config_PutPsz( p_intf, "http-proxy", [[o_input_httpproxy_fld stringValue] UTF8String] );
882         config_PutPsz( p_intf, "http-proxy-pwd", [[o_input_httpproxypwd_sfld stringValue] UTF8String] );
883         config_PutInt( p_intf, "postproc-q", [o_input_postproc_fld intValue] );
884
885         SaveIntList( o_input_avi_pop, "avi-index" );
886
887         config_PutInt( p_intf, "rtsp-tcp", [o_input_rtsp_ckb state] );
888         SaveIntList( o_input_skipLoop_pop, "ffmpeg-skiploopfilter" );
889
890         #define CaCi( name, int ) config_PutInt( p_intf, name, int * [[o_input_cachelevel_pop selectedItem] tag] )
891         #define CaC( name ) CaCi( name, 1 )
892         msg_Dbg( p_intf, "Adjusting all cache values to: %i", (int)[[o_input_cachelevel_pop selectedItem] tag] );
893         CaC( "network-caching" );
894         CaC( "disc-caching" );
895         CaC( "live-caching" );
896         b_inputSettingChanged = NO;
897     }
898
899     /**********************
900      * subtitles settings *
901      **********************/
902     if( b_osdSettingChanged )
903     {
904         config_PutInt( p_intf, "osd", [o_osd_osd_ckb state] );
905
906         if( [o_osd_encoding_pop indexOfSelectedItem] >= 0 )
907             SaveStringList( o_osd_encoding_pop, "subsdec-encoding" );
908         else
909             config_PutPsz( p_intf, "subsdec-encoding", "" );
910
911         config_PutPsz( p_intf, "sub-language", [[o_osd_lang_fld stringValue] UTF8String] );
912
913         config_PutPsz( p_intf, "freetype-font", [[o_osd_font_fld stringValue] UTF8String] );
914         SaveIntList( o_osd_font_color_pop, "freetype-color" );
915         SaveIntList( o_osd_font_size_pop, "freetype-rel-fontsize" );
916         config_PutInt( p_intf, "freetype-opacity", [o_osd_opacity_sld intValue] );
917         config_PutInt( p_intf, "freetype-bold", [o_osd_forcebold_ckb state] );
918         b_osdSettingChanged = NO;
919     }
920
921     /********************
922      * hotkeys settings *
923      ********************/
924     if( b_hotkeyChanged )
925     {
926         NSUInteger hotKeyCount = [o_hotkeySettings count];
927         for( NSUInteger i = 0; i < hotKeyCount; i++ )
928             config_PutPsz( p_intf, [[o_hotkeyNames objectAtIndex:i] UTF8String], [[o_hotkeySettings objectAtIndex:i]UTF8String] );
929         b_hotkeyChanged = NO;
930     }
931
932     /* okay, let's save our changes to vlcrc */
933     config_SaveConfigFile( p_intf );
934
935     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
936                                                             object: nil
937                                                           userInfo: nil];
938 }
939
940 - (void)showSettingsForCategory: (id)o_new_category_view
941 {
942     NSRect o_win_rect, o_view_rect, o_old_view_rect;
943     o_win_rect = [o_sprefs_win frame];
944     o_view_rect = [o_new_category_view frame];
945
946     if( o_currentlyShownCategoryView != nil )
947     {
948         /* restore our window's height, if we've shown another category previously */
949         o_old_view_rect = [o_currentlyShownCategoryView frame];
950         o_win_rect.size.height = o_win_rect.size.height - o_old_view_rect.size.height;
951         o_win_rect.origin.y = ( o_win_rect.origin.y + o_old_view_rect.size.height ) - o_view_rect.size.height;
952     }
953
954     o_win_rect.size.height = o_win_rect.size.height + o_view_rect.size.height;
955
956     [o_new_category_view setFrame: NSMakeRect( 0,
957                                                [o_sprefs_controls_box frame].size.height,
958                                                o_view_rect.size.width,
959                                                o_view_rect.size.height )];
960     [o_new_category_view setAutoresizesSubviews: YES];
961     if (o_currentlyShownCategoryView)
962     {
963         [[[o_sprefs_win contentView] animator] replaceSubview: o_currentlyShownCategoryView with: o_new_category_view];
964         [o_currentlyShownCategoryView release];
965         [[o_sprefs_win animator] setFrame: o_win_rect display:YES];
966     }
967     else
968     {
969         [[o_sprefs_win contentView] addSubview: o_new_category_view];
970         [o_sprefs_win setFrame: o_win_rect display:YES animate:NO];
971     }
972
973     /* keep our current category for further reference */
974     o_currentlyShownCategoryView = o_new_category_view;
975     [o_currentlyShownCategoryView retain];
976 }
977
978 - (IBAction)interfaceSettingChanged:(id)sender
979 {
980     b_intfSettingChanged = YES;
981 }
982
983 - (void)showInterfaceSettings
984 {
985     [self showSettingsForCategory: o_intf_view];
986 }
987
988 - (IBAction)audioSettingChanged:(id)sender
989 {
990     if( sender == o_audio_vol_sld )
991         [o_audio_vol_fld setIntValue: [o_audio_vol_sld intValue]];
992
993     if( sender == o_audio_vol_fld )
994         [o_audio_vol_sld setIntValue: [o_audio_vol_fld intValue]];
995
996     if( sender == o_audio_last_ckb )
997     {
998         if( [o_audio_last_ckb state] == NSOnState )
999         {
1000             [o_audio_lastpwd_sfld setEnabled: YES];
1001             [o_audio_lastuser_fld setEnabled: YES];
1002         }
1003         else
1004         {
1005             [o_audio_lastpwd_sfld setEnabled: NO];
1006             [o_audio_lastuser_fld setEnabled: NO];
1007         }
1008     }
1009
1010     b_audioSettingChanged = YES;
1011 }
1012
1013 - (void)showAudioSettings
1014 {
1015     [self showSettingsForCategory: o_audio_view];
1016 }
1017
1018 - (IBAction)videoSettingChanged:(id)sender
1019 {
1020     if( sender == o_video_snap_folder_btn )
1021     {
1022         o_selectFolderPanel = [[NSOpenPanel alloc] init];
1023         [o_selectFolderPanel setCanChooseDirectories: YES];
1024         [o_selectFolderPanel setCanChooseFiles: NO];
1025         [o_selectFolderPanel setResolvesAliases: YES];
1026         [o_selectFolderPanel setAllowsMultipleSelection: NO];
1027         [o_selectFolderPanel setMessage: _NS("Choose the folder to save your video snapshots to.")];
1028         [o_selectFolderPanel setCanCreateDirectories: YES];
1029         [o_selectFolderPanel setPrompt: _NS("Choose")];
1030         [o_selectFolderPanel beginSheetForDirectory: nil file: nil modalForWindow: o_sprefs_win
1031                                       modalDelegate: self
1032                                      didEndSelector: @selector(savePanelDidEnd:returnCode:contextInfo:)
1033                                         contextInfo: o_video_snap_folder_btn];
1034     }
1035     else
1036         b_videoSettingChanged = YES;
1037 }
1038
1039 - (void)savePanelDidEnd:(NSOpenPanel * )panel returnCode: (int)returnCode contextInfo: (void *)contextInfo
1040 {
1041     if( returnCode == NSOKButton )
1042     {
1043         if( contextInfo == o_video_snap_folder_btn )
1044         {
1045             [o_video_snap_folder_fld setStringValue: [[o_selectFolderPanel URL] path]];
1046             b_videoSettingChanged = YES;
1047         }
1048         else if( contextInfo == o_input_record_btn )
1049         {
1050             [o_input_record_fld setStringValue: [[o_selectFolderPanel URL] path]];
1051             b_inputSettingChanged = YES;
1052         }
1053     }
1054
1055     [o_selectFolderPanel release];
1056 }
1057
1058 - (void)showVideoSettings
1059 {
1060     [self showSettingsForCategory: o_video_view];
1061 }
1062
1063 - (IBAction)osdSettingChanged:(id)sender
1064 {
1065     if( sender == o_osd_opacity_fld )
1066         [o_osd_opacity_sld setIntValue: [o_osd_opacity_fld intValue]];
1067
1068     if( sender == o_osd_opacity_sld )
1069         [o_osd_opacity_fld setIntValue: [o_osd_opacity_sld intValue]];
1070
1071     b_osdSettingChanged = YES;
1072 }
1073
1074 - (void)showOSDSettings
1075 {
1076     [self showSettingsForCategory: o_osd_view];
1077 }
1078
1079 - (IBAction)showFontPicker:(id)sender
1080 {
1081     char * font = config_GetPsz( p_intf, "freetype-font" );
1082     NSString * fontFamilyName = font ? [NSString stringWithUTF8String: font] : nil;
1083     free(font);
1084     if( fontFamilyName )
1085     {
1086         NSFontDescriptor * fd = [NSFontDescriptor fontDescriptorWithFontAttributes:nil];
1087         NSFont * font = [NSFont fontWithDescriptor:[fd fontDescriptorWithFamily:fontFamilyName] textTransform:nil];
1088         [[NSFontManager sharedFontManager] setSelectedFont:font isMultiple:NO];
1089     }
1090     [[NSFontManager sharedFontManager] setTarget: self];
1091     [[NSFontPanel sharedFontPanel] orderFront:self];
1092 }
1093
1094 - (void)changeFont:(id)sender
1095 {
1096     NSFont * font = [sender convertFont:[[NSFontManager sharedFontManager] selectedFont]];
1097     [o_osd_font_fld setStringValue:[font familyName]];
1098     [self osdSettingChanged:self];
1099 }
1100
1101 - (IBAction)inputSettingChanged:(id)sender
1102 {
1103     if( sender == o_input_cachelevel_pop )
1104     {
1105         if( [[[o_input_cachelevel_pop selectedItem] title] isEqualToString: _NS("Custom")] )
1106             [o_input_cachelevel_custom_txt setHidden: NO];
1107         else
1108             [o_input_cachelevel_custom_txt setHidden: YES];
1109     }
1110     else if( sender == o_input_record_btn )
1111     {
1112         o_selectFolderPanel = [[NSOpenPanel alloc] init];
1113         [o_selectFolderPanel setCanChooseDirectories: YES];
1114         [o_selectFolderPanel setCanChooseFiles: YES];
1115         [o_selectFolderPanel setResolvesAliases: YES];
1116         [o_selectFolderPanel setAllowsMultipleSelection: NO];
1117         [o_selectFolderPanel setMessage: _NS("Choose the directory or filename where the records will be stored.")];
1118         [o_selectFolderPanel setCanCreateDirectories: YES];
1119         [o_selectFolderPanel setPrompt: _NS("Choose")];
1120         [o_selectFolderPanel beginSheetForDirectory: nil file: nil modalForWindow: o_sprefs_win
1121                                       modalDelegate: self
1122                                      didEndSelector: @selector(savePanelDidEnd:returnCode:contextInfo:)
1123                                         contextInfo: o_input_record_btn];
1124         return;
1125     }
1126
1127     b_inputSettingChanged = YES;
1128 }
1129
1130 - (void)showInputSettings
1131 {
1132     [self showSettingsForCategory: o_input_view];
1133 }
1134
1135 - (IBAction)hotkeySettingChanged:(id)sender
1136 {
1137     if( sender == o_hotkeys_change_btn || sender == o_hotkeys_listbox )
1138     {
1139         [o_hotkeys_change_lbl setStringValue: [NSString stringWithFormat: _NS("Press new keys for\n\"%@\""),
1140                                                [o_hotkeyDescriptions objectAtIndex: [o_hotkeys_listbox selectedRow]]]];
1141         [o_hotkeys_change_keys_lbl setStringValue: [self OSXStringKeyToString:[o_hotkeySettings objectAtIndex: [o_hotkeys_listbox selectedRow]]]];
1142         [o_hotkeys_change_taken_lbl setStringValue: @""];
1143         [o_hotkeys_change_win setInitialFirstResponder: [o_hotkeys_change_win contentView]];
1144         [o_hotkeys_change_win makeFirstResponder: [o_hotkeys_change_win contentView]];
1145         [NSApp runModalForWindow: o_hotkeys_change_win];
1146     }
1147     else if( sender == o_hotkeys_change_cancel_btn )
1148     {
1149         [NSApp stopModal];
1150         [o_hotkeys_change_win close];
1151     }
1152     else if( sender == o_hotkeys_change_ok_btn )
1153     {
1154         NSInteger i_returnValue;
1155         if(! o_keyInTransition )
1156         {
1157             [NSApp stopModal];
1158             [o_hotkeys_change_win close];
1159             msg_Err( p_intf, "internal error prevented the hotkey switch" );
1160             return;
1161         }
1162
1163         b_hotkeyChanged = YES;
1164
1165         i_returnValue = [o_hotkeySettings indexOfObject: o_keyInTransition];
1166         if( i_returnValue != NSNotFound )
1167             [o_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]];
1168         NSString *tempString;
1169         tempString = [o_keyInTransition stringByReplacingOccurrencesOfString:@"-" withString:@"+"];
1170         i_returnValue = [o_hotkeySettings indexOfObject: tempString];
1171         if( i_returnValue != NSNotFound )
1172             [o_hotkeySettings replaceObjectAtIndex: i_returnValue withObject: [NSString string]];
1173
1174         [o_hotkeySettings replaceObjectAtIndex: [o_hotkeys_listbox selectedRow] withObject: [o_keyInTransition retain]];
1175
1176         [NSApp stopModal];
1177         [o_hotkeys_change_win close];
1178
1179         [o_hotkeys_listbox reloadData];
1180     }
1181     else if( sender == o_hotkeys_clear_btn )
1182     {
1183         [o_hotkeySettings replaceObjectAtIndex: [o_hotkeys_listbox selectedRow] withObject: [NSString string]];
1184         [o_hotkeys_listbox reloadData];
1185         b_hotkeyChanged = YES;
1186     }
1187
1188     [[NSNotificationCenter defaultCenter] postNotificationName: @"VLCMediaKeySupportSettingChanged"
1189                                                         object: nil
1190                                                       userInfo: nil];
1191 }
1192
1193 - (void)showHotkeySettings
1194 {
1195     [self showSettingsForCategory: o_hotkeys_view];
1196 }
1197
1198 - (int)numberOfRowsInTableView:(NSTableView *)aTableView
1199 {
1200     return [o_hotkeySettings count];
1201 }
1202
1203 - (id)tableView:(NSTableView *)aTableView objectValueForTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex
1204 {
1205     if( [[aTableColumn identifier] isEqualToString: @"action"] )
1206         return [o_hotkeyDescriptions objectAtIndex: rowIndex];
1207     else if( [[aTableColumn identifier] isEqualToString: @"shortcut"] )
1208         return [self OSXStringKeyToString:[o_hotkeySettings objectAtIndex: rowIndex]];
1209     else
1210     {
1211         msg_Err( p_intf, "unknown TableColumn identifier (%s)!", [[aTableColumn identifier] UTF8String] );
1212         return NULL;
1213     }
1214 }
1215
1216 - (BOOL)changeHotkeyTo: (NSString *)theKey
1217 {
1218     NSInteger i_returnValue, i_returnValue2;
1219     i_returnValue = [o_hotkeysNonUseableKeys indexOfObject: theKey];
1220
1221     if( i_returnValue != NSNotFound || [theKey isEqualToString:@""] )
1222     {
1223         [o_hotkeys_change_keys_lbl setStringValue: _NS("Invalid combination")];
1224         [o_hotkeys_change_taken_lbl setStringValue: _NS("Regrettably, these keys cannot be assigned as hotkey shortcuts.")];
1225         [o_hotkeys_change_ok_btn setEnabled: NO];
1226         return NO;
1227     }
1228     else
1229     {
1230         [o_hotkeys_change_keys_lbl setStringValue: [self OSXStringKeyToString:theKey]];
1231
1232         i_returnValue = [o_hotkeySettings indexOfObject: theKey];
1233         i_returnValue2 = [o_hotkeySettings indexOfObject: [theKey stringByReplacingOccurrencesOfString:@"-" withString:@"+"]];
1234         if( i_returnValue != NSNotFound )
1235             [o_hotkeys_change_taken_lbl setStringValue: [NSString stringWithFormat:
1236                                                          _NS("This combination is already taken by \"%@\"."),
1237                                                          [o_hotkeyDescriptions objectAtIndex: i_returnValue]]];
1238         else if( i_returnValue2 != NSNotFound )
1239             [o_hotkeys_change_taken_lbl setStringValue: [NSString stringWithFormat:
1240                                                          _NS("This combination is already taken by \"%@\"."),
1241                                                          [o_hotkeyDescriptions objectAtIndex: i_returnValue2]]];
1242         
1243         else
1244             [o_hotkeys_change_taken_lbl setStringValue: @""];
1245
1246         [o_hotkeys_change_ok_btn setEnabled: YES];
1247         [o_keyInTransition release];
1248         o_keyInTransition = theKey;
1249         [o_keyInTransition retain];
1250         return YES;
1251     }
1252 }
1253
1254 @end
1255
1256 /********************
1257  * hotkeys settings *
1258  ********************/
1259
1260 @implementation VLCHotkeyChangeWindow
1261
1262 - (BOOL)acceptsFirstResponder
1263 {
1264     return YES;
1265 }
1266
1267 - (BOOL)becomeFirstResponder
1268 {
1269     return YES;
1270 }
1271
1272 - (BOOL)resignFirstResponder
1273 {
1274     /* We need to stay the first responder or we'll miss the user's input */
1275     return NO;
1276 }
1277
1278 - (BOOL)performKeyEquivalent:(NSEvent *)o_theEvent
1279 {
1280     NSMutableString *tempString = [[[NSMutableString alloc] init] autorelease];
1281     NSString *keyString = [o_theEvent characters];
1282         unichar key = [keyString characterAtIndex:0];
1283
1284     /* modifiers */
1285     if( [o_theEvent modifierFlags] & NSControlKeyMask )
1286         [tempString appendString:@"Ctrl-"];
1287     if( [o_theEvent modifierFlags] & NSAlternateKeyMask  )
1288         [tempString appendString:@"Alt-"];
1289     if( [o_theEvent modifierFlags] & NSShiftKeyMask )
1290         [tempString appendString:@"Shift-"];
1291     if( [o_theEvent modifierFlags] & NSCommandKeyMask )
1292         [tempString appendString:@"Command-"];
1293
1294     /* non character keys */
1295     if( key == NSUpArrowFunctionKey )
1296         [tempString appendString:@"Up"];
1297     else if( key == NSDownArrowFunctionKey )
1298         [tempString appendString:@"Down"];
1299     else if( key == NSLeftArrowFunctionKey )
1300         [tempString appendString:@"Left"];
1301     else if( key == NSRightArrowFunctionKey )
1302         [tempString appendString:@"Right"];
1303     else if( key == NSF1FunctionKey )
1304         [tempString appendString:@"F1"];
1305     else if( key == NSF2FunctionKey )
1306         [tempString appendString:@"F2"];
1307     else if( key == NSF3FunctionKey )
1308         [tempString appendString:@"F3"];
1309     else if( key == NSF4FunctionKey )
1310         [tempString appendString:@"F4"];
1311     else if( key == NSF5FunctionKey )
1312         [tempString appendString:@"F5"];
1313     else if( key == NSF6FunctionKey )
1314         [tempString appendString:@"F6"];
1315     else if( key == NSF7FunctionKey )
1316         [tempString appendString:@"F7"];
1317     else if( key == NSF8FunctionKey )
1318         [tempString appendString:@"F8"];
1319     else if( key == NSF9FunctionKey )
1320         [tempString appendString:@"F9"];
1321     else if( key == NSF10FunctionKey )
1322         [tempString appendString:@"F10"];
1323     else if( key == NSF11FunctionKey )
1324         [tempString appendString:@"F11"];
1325     else if( key == NSF12FunctionKey )
1326         [tempString appendString:@"F12"];
1327     else if( key == NSInsertFunctionKey )
1328         [tempString appendString:@"Insert"];
1329     else if( key == NSHomeFunctionKey )
1330         [tempString appendString:@"Home"];
1331     else if( key == NSEndFunctionKey )
1332         [tempString appendString:@"End"];
1333     else if( key == NSPageUpFunctionKey )
1334         [tempString appendString:@"Pageup"];
1335     else if( key == NSPageDownFunctionKey )
1336         [tempString appendString:@"Pagedown"];
1337     else if( key == NSMenuFunctionKey )
1338         [tempString appendString:@"Menu"];
1339     else if( key == NSTabCharacter )
1340         [tempString appendString:@"Tab"];
1341     else if( key == NSCarriageReturnCharacter )
1342         [tempString appendString:@"Enter"];
1343     else if( key == NSEnterCharacter )
1344         [tempString appendString:@"Enter"];
1345     else if( key == NSDeleteCharacter )
1346         [tempString appendString:@"Delete"];
1347     else if( key == NSBackspaceCharacter )
1348         [tempString appendString:@"Backspace"];
1349     else if (![[[o_theEvent charactersIgnoringModifiers] lowercaseString] isEqualToString:@""]) //plain characters
1350         [tempString appendString:[[o_theEvent charactersIgnoringModifiers] lowercaseString]];
1351     else
1352         return NO;
1353
1354     return [[[VLCMain sharedInstance] simplePreferences] changeHotkeyTo: tempString];
1355 }
1356
1357 @end
1358
1359 @implementation VLCSimplePrefsWindow
1360
1361 - (BOOL)acceptsFirstResponder
1362 {
1363     return YES;
1364 }
1365
1366 - (void)changeFont:(id)sender
1367 {
1368     [[[VLCMain sharedInstance] simplePreferences] changeFont: sender];
1369 }
1370 @end