]> git.sesse.net Git - vlc/blob - modules/gui/macosx/prefs.m
* fix text encoding of subtitle filenames (and other paths).
[vlc] / modules / gui / macosx / prefs.m
1 /*****************************************************************************
2  * prefs.m: MacOS X module for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2004 VideoLAN
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <sys/param.h>                                    /* for MAXPATHLEN */
30 #include <string.h>
31
32 #include "intf.h"
33 #include "prefs.h"
34 #include "vlc_keys.h"
35
36 /*****************************************************************************
37  * VLCPrefs implementation
38  *****************************************************************************/
39 @implementation VLCPrefs
40
41 - (id)init
42 {
43     self = [super init];
44
45     if( self != nil )
46     {
47         o_empty_view = [[NSView alloc] init];
48         o_save_prefs = [[NSMutableDictionary alloc] init];
49     }
50
51     return( self );
52 }
53
54 - (void)dealloc
55 {
56     [o_empty_view release];
57     [o_save_prefs release];
58     [super dealloc];
59 }
60
61 - (void)awakeFromNib
62 {
63     p_intf = VLCIntf;
64     b_advanced = config_GetInt( p_intf, "advanced" );
65
66     [self initStrings];
67     [o_advanced_ckb setState: b_advanced];
68     [o_prefs_view setBorderType: NSGrooveBorder];
69     [o_prefs_view setHasVerticalScroller: YES];
70     [o_prefs_view setDrawsBackground: NO];
71     [o_prefs_view setRulersVisible: NO];
72     [o_prefs_view setDocumentView: o_empty_view];
73     [o_tree selectRow:0 byExtendingSelection:NO];
74 }
75
76 - (void)initStrings
77 {
78     [o_prefs_window setTitle: _NS("Preferences")];
79     [o_save_btn setTitle: _NS("Save")];
80     [o_cancel_btn setTitle: _NS("Cancel")];
81     [o_reset_btn setTitle: _NS("Reset All")];
82     [o_advanced_ckb setTitle: _NS("Advanced")];
83 }
84
85 - (void)showPrefs
86 {
87     [o_save_prefs release];
88     o_save_prefs = [[NSMutableDictionary alloc] init];
89     [self showViewForID: [[o_tree itemAtRow:[o_tree selectedRow]] getObjectID]
90         andName: [[o_tree itemAtRow:[o_tree selectedRow]] getName]];
91     [o_prefs_window center];
92     [o_prefs_window makeKeyAndOrderFront:self];
93 }
94
95 - (IBAction)savePrefs: (id)sender
96 {
97     id o_vlc_config;
98     NSEnumerator *o_enum;
99
100     o_enum = [o_save_prefs objectEnumerator];
101     while( ( o_vlc_config = [o_enum nextObject] ) )
102     {
103         int i_type = [o_vlc_config configType];
104         NSString *o_name = [o_vlc_config configName];
105         char *psz_name = (char *)[o_name UTF8String];
106
107         switch( i_type )
108         {
109     
110         case CONFIG_ITEM_MODULE:
111             {
112                 char *psz_value;
113                 module_t *p_a_module;
114                 int i_id = [[o_vlc_config selectedItem] tag];
115                 
116                 p_a_module = (module_t *)vlc_object_get( p_intf, i_id );
117                 if( p_a_module == NULL || p_a_module->i_object_type != VLC_OBJECT_MODULE )
118                 {
119                     i_id = -1;
120                 }
121                 
122                 psz_value = ( i_id == -1 ) ? "" :  p_a_module->psz_object_name ;
123                 config_PutPsz( p_intf, psz_name, strdup(psz_value) );
124             }
125             break;
126     
127         case CONFIG_ITEM_STRING:
128             {
129                 char *psz_value;
130                 NSString *o_value = [o_vlc_config stringValue];
131                 psz_value = (char *)[o_value UTF8String];
132     
133                 config_PutPsz( p_intf, psz_name, psz_value );
134             }
135             break;
136
137         case CONFIG_ITEM_FILE:
138         case CONFIG_ITEM_DIRECTORY:
139             {
140                 char *psz_value;
141                 NSString *o_value = [o_vlc_config stringValue];
142                 psz_value = (char *)[o_value fileSystemRepresentation];
143     
144                 config_PutPsz( p_intf, psz_name, psz_value );
145             }
146             break;
147     
148         case CONFIG_ITEM_INTEGER:
149         case CONFIG_ITEM_BOOL:
150             {
151                 int i_value = [o_vlc_config intValue];
152     
153                 config_PutInt( p_intf, psz_name, i_value );
154             }
155             break;
156     
157         case CONFIG_ITEM_FLOAT:
158             {
159                 float f_value = [o_vlc_config floatValue];
160     
161                 config_PutFloat( p_intf, psz_name, f_value );
162             }
163             break;
164
165         case CONFIG_ITEM_KEY:
166             {
167                 unsigned int i_key = config_GetInt( p_intf, psz_name );
168                 unsigned int i_new_key = 0;
169
170                 if( [o_vlc_config class] == [VLCMatrix class] )
171                 {
172                     int i;
173                     NSButtonCell *o_current_cell;
174                     NSArray *o_cells = [o_vlc_config cells];
175                     i_new_key = (i_key & ~KEY_MODIFIER);
176                     for( i = 0; i < [o_cells count]; i++ )
177                     {
178                         o_current_cell = [o_cells objectAtIndex:i];
179                         if( [[o_current_cell title] isEqualToString:_NS("Command")] && 
180                             [o_current_cell state] == NSOnState )
181                                 i_new_key |= KEY_MODIFIER_COMMAND;
182                         if( [[o_current_cell title] isEqualToString:_NS("Control")] && 
183                             [o_current_cell state] == NSOnState )
184                                 i_new_key |= KEY_MODIFIER_CTRL;
185                         if( [[o_current_cell title] isEqualToString:_NS("Option/Alt")] && 
186                             [o_current_cell state] == NSOnState )
187                                 i_new_key |= KEY_MODIFIER_ALT;
188                         if( [[o_current_cell title] isEqualToString:_NS("Shift")] && 
189                             [o_current_cell state] == NSOnState )
190                                 i_new_key |= KEY_MODIFIER_SHIFT;
191                     }
192                 }
193                 else
194                 {
195                     i_new_key = (i_key & KEY_MODIFIER);
196                     i_new_key |= StringToKey([[o_vlc_config stringValue] cString]);
197                 }
198                 config_PutInt( p_intf, psz_name, i_new_key );
199             }
200             break;
201         }
202     }
203     config_SaveConfigFile( p_intf, NULL );
204     [o_prefs_window orderOut:self];
205 }
206
207 - (IBAction)closePrefs: (id)sender
208 {
209     [o_prefs_window orderOut:self];
210 }
211
212 - (IBAction)resetAll: (id)sender
213 {
214     NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"), _NS("Continue"), 
215         nil, o_prefs_window, self, @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil,
216         _NS("Beware this will reset your VLC media player preferences.\n"
217             "Are you sure you want to continue?") );
218 }
219
220 - (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return contextInfo:(void *)o_context
221 {
222     if( i_return == NSAlertAlternateReturn )
223     {
224         config_ResetAll( p_intf );
225         [self showViewForID: [[o_tree itemAtRow:[o_tree selectedRow]] getObjectID]
226             andName: [[o_tree itemAtRow:[o_tree selectedRow]] getName]];
227     }
228 }
229
230 - (IBAction)advancedToggle: (id)sender
231 {
232     b_advanced = !b_advanced;
233     [o_advanced_ckb setState: b_advanced];
234     [self showViewForID: [[o_tree itemAtRow:[o_tree selectedRow]] getObjectID]
235         andName: [[o_tree itemAtRow:[o_tree selectedRow]] getName]];
236 }
237
238 - (IBAction)openFileDialog: (id)sender
239 {
240     NSOpenPanel *o_open_panel = [NSOpenPanel openPanel];
241     
242     [o_open_panel setTitle: _NS("Select file or directory")];
243     [o_open_panel setPrompt: _NS("Select")];
244     [o_open_panel setAllowsMultipleSelection: NO];
245     [o_open_panel setCanChooseFiles: YES];
246     [o_open_panel setCanChooseDirectories: YES];
247     [o_open_panel beginSheetForDirectory:nil
248         file:nil
249         types:nil
250         modalForWindow:[sender window]
251         modalDelegate: self
252         didEndSelector: @selector(pathChosenInPanel: 
253                         withReturn:
254                         contextInfo:)
255         contextInfo: sender];
256 }
257
258 - (void)pathChosenInPanel:(NSOpenPanel *)o_sheet withReturn:(int)i_return_code contextInfo:(void  *)o_context_info
259 {
260     if( i_return_code == NSOKButton )
261     {
262         NSString *o_path = [[o_sheet filenames] objectAtIndex: 0];
263         VLCTextField *o_field = (VLCTextField *)[(VLCButton *)o_context_info tag]; /* FIXME */
264         [o_field setStringValue: o_path];
265         [self configChanged: o_field];
266     }
267 }
268
269 - (void)loadConfigTree
270 {
271     
272 }
273
274 - (void)outlineViewSelectionIsChanging:(NSNotification *)o_notification
275 {
276 }
277
278 - (void)outlineViewSelectionDidChange:(NSNotification *)o_notification
279 {
280     [self showViewForID: [[o_tree itemAtRow:[o_tree selectedRow]] getObjectID]
281         andName: [[o_tree itemAtRow:[o_tree selectedRow]] getName]];
282 }
283
284 - (void)configChanged:(id)o_unknown
285 {
286     id o_vlc_config = [o_unknown isKindOfClass: [NSNotification class]] ?
287                       [o_unknown object] : o_unknown;
288
289     NSString *o_name = [o_vlc_config configName];
290     [o_save_prefs setObject: o_vlc_config forKey: o_name];
291 }
292
293 - (void)showViewForID: (int)i_id andName: (NSString *)o_item_name
294 {
295     vlc_list_t *p_list;
296     module_t *p_parser;
297     module_config_t *p_item;
298     
299     int i_pos, i_module_tag, i_index;
300     
301     NSString *o_module_name;
302     NSRect s_rc;                        /* rect                         */
303     NSView *o_view;                     /* view                         */
304     NSRect s_vrc;                       /* view rect                    */
305     VLCTextField *o_text_field;         /* input field / label          */
306     
307     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
308
309     /* Get a pointer to the module */
310     p_parser = (module_t *)vlc_object_get( p_intf, i_id );
311     if( p_parser->i_object_type != VLC_OBJECT_MODULE )
312     {
313         /* 0OOoo something went really bad */
314         vlc_list_release( p_list );
315         return;
316     }
317     
318     /* Enumerate config options and add corresponding config boxes */
319     o_module_name = [NSString stringWithUTF8String: p_parser->psz_object_name];
320     p_item = p_parser->p_config;
321
322     i_pos = 0;
323     o_view = nil;
324     i_module_tag = 3;
325
326 #define X_ORIGIN 20
327 #define Y_ORIGIN (X_ORIGIN - 10)
328
329 #define CHECK_VIEW_HEIGHT \
330     { \
331         float f_new_pos = s_rc.origin.y + s_rc.size.height + X_ORIGIN; \
332         if( f_new_pos > s_vrc.size.height ) \
333         { \
334             s_vrc.size.height = f_new_pos; \
335             [o_view setFrame: s_vrc]; \
336         } \
337     }
338
339 #define CONTROL_LABEL( label ) \
340     { \
341         s_rc.origin.x += s_rc.size.width + 10; \
342         s_rc.size.width = s_vrc.size.width - s_rc.origin.x - X_ORIGIN - 20; \
343         o_text_field = [[NSTextField alloc] initWithFrame: s_rc]; \
344         [o_text_field setDrawsBackground: NO]; \
345         [o_text_field setBordered: NO]; \
346         [o_text_field setEditable: NO]; \
347         [o_text_field setSelectable: NO]; \
348         if ( label ) \
349         { \
350             [o_text_field setStringValue: \
351                 [[VLCMain sharedInstance] localizedString: label]]; \
352         } \
353         [o_text_field sizeToFit]; \
354         [o_view addSubview: [o_text_field autorelease]]; \
355     }
356
357 #define INPUT_FIELD( ctype, cname, label, w, msg, param, tip ) \
358     { \
359         char * psz_duptip = NULL; \
360         if ( p_item->psz_longtext != NULL ) \
361             psz_duptip = strdup( p_item->psz_longtext ); \
362         s_rc.size.height = 25; \
363         s_rc.size.width = w; \
364         s_rc.origin.y += 10; \
365         CHECK_VIEW_HEIGHT; \
366         o_text_field = [[VLCTextField alloc] initWithFrame: s_rc]; \
367         [o_text_field setAlignment: NSRightTextAlignment]; \
368         CONTROL_CONFIG( o_text_field, o_module_name, ctype, cname ); \
369         [o_text_field msg: param]; \
370         if ( psz_duptip != NULL ) \
371         { \
372             [o_text_field setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: \
373                                        psz_duptip] toWidth: PREFS_WRAP ]]; \
374             free(psz_duptip);\
375         } \
376         [o_view addSubview: [o_text_field autorelease]]; \
377         [[NSNotificationCenter defaultCenter] addObserver: self \
378             selector: @selector(configChanged:) \
379             name: NSControlTextDidChangeNotification \
380             object: o_text_field]; \
381         CONTROL_LABEL( label ); \
382         s_rc.origin.y += s_rc.size.height; \
383         s_rc.origin.x = X_ORIGIN; \
384     }
385
386 #define INPUT_FIELD_INTEGER( name, label, w, param, tip ) \
387     INPUT_FIELD( CONFIG_ITEM_INTEGER, name, label, w, setIntValue, param, tip )
388 #define INPUT_FIELD_FLOAT( name, label, w, param, tip ) \
389     INPUT_FIELD( CONFIG_ITEM_FLOAT, name, label, w, setFloatValue, param, tip )
390 #define INPUT_FIELD_STRING( name, label, w, param, tip ) \
391     INPUT_FIELD( CONFIG_ITEM_STRING, name, label, w, setStringValue, param, tip )
392
393     /* Init View */
394     s_vrc = [[o_prefs_view contentView] bounds]; s_vrc.size.height -= 4;
395     o_view = [[VLCFlippedView alloc] initWithFrame: s_vrc];
396     [o_view setAutoresizingMask: NSViewWidthSizable];
397     s_rc.origin.x = X_ORIGIN;
398     s_rc.origin.y = Y_ORIGIN;
399     BOOL b_right_cat = TRUE;
400
401     if( p_item ) do
402     {
403         if( p_item->i_type == CONFIG_HINT_CATEGORY )
404         {
405             if( !strcmp( p_parser->psz_object_name, "main" ) &&
406                 [o_item_name isEqualToString: [[VLCMain sharedInstance] localizedString: p_item->psz_text]] )
407             {
408                 b_right_cat = TRUE;
409             } else if( strcmp( p_parser->psz_object_name, "main" ) )
410             {
411                  b_right_cat = TRUE;
412             } else b_right_cat = FALSE; 
413         } else if( p_item->i_type == CONFIG_HINT_END && !strcmp( p_parser->psz_object_name, "main" ) )
414         {
415             b_right_cat = FALSE;
416         }
417         
418         if( (p_item->b_advanced && !b_advanced ) || !b_right_cat )
419         {
420             continue;
421         }
422         switch( p_item->i_type )
423         {
424             case CONFIG_ITEM_MODULE:
425             {
426                 VLCPopUpButton *o_modules;
427                 module_t *p_a_module;
428                 char * psz_duptip = NULL;
429
430                 if ( p_item->psz_longtext != NULL )
431                     psz_duptip = strdup( p_item->psz_longtext );
432
433                 s_rc.size.height = 25;
434                 s_rc.size.width = 200;
435                 s_rc.origin.y += 10;
436                 
437                 CHECK_VIEW_HEIGHT;
438     
439                 o_modules = [[VLCPopUpButton alloc] initWithFrame: s_rc];
440                 CONTROL_CONFIG( o_modules, o_module_name,
441                                     CONFIG_ITEM_MODULE, p_item->psz_name );
442                 [o_modules setTarget: self];
443                 [o_modules setAction: @selector(configChanged:)];
444                 [o_modules sendActionOn:NSLeftMouseUpMask];
445                 
446                 if ( psz_duptip != NULL )
447                 {
448                     [o_modules setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
449                     free( psz_duptip );
450                 }
451                 [o_view addSubview: [o_modules autorelease]];
452
453                 [o_modules addItemWithTitle: _NS("Default")];
454                 [[o_modules lastItem] setTag: -1];
455                 [o_modules selectItem: [o_modules lastItem]];
456
457                 /* build a list of available modules */
458                 {
459                     for( i_index = 0; i_index < p_list->i_count; i_index++ )
460                     {
461                         p_a_module = (module_t *)p_list->p_values[i_index].p_object ;
462     
463                         if( !strcmp( p_a_module->psz_capability,
464                                     p_item->psz_type ) )
465                         {
466                             NSString *o_description = [[VLCMain sharedInstance]
467                                 localizedString: p_a_module->psz_longname];
468                             [o_modules addItemWithTitle: o_description];
469                             [[o_modules lastItem] setTag: p_a_module->i_object_id];
470
471                             if( p_item->psz_value &&
472                                 !strcmp( p_item->psz_value, p_a_module->psz_object_name ) )
473                             {
474                                 [o_modules selectItem:[o_modules lastItem]];
475                             }
476                         }
477                     }
478                 }
479
480                 CONTROL_LABEL( p_item->psz_text );
481                 s_rc.origin.y += s_rc.size.height;
482                 s_rc.origin.x = X_ORIGIN;
483             }
484             break;
485
486             case CONFIG_ITEM_FILE:
487             case CONFIG_ITEM_DIRECTORY:
488             {
489                 char *psz_duptip = NULL;
490                 char *psz_value = p_item->psz_value ?
491                                     p_item->psz_value : "";
492
493                 if ( p_item->psz_longtext != NULL )
494                     psz_duptip = strdup( p_item->psz_longtext );
495
496                 s_rc.origin.y += 10;
497                 s_rc.size.width = - 10;
498                 s_rc.size.height = 25;
499                 CHECK_VIEW_HEIGHT;
500                 CONTROL_LABEL( p_item->psz_text );
501                 s_rc.origin.x = X_ORIGIN;
502                 s_rc.origin.y += s_rc.size.height;
503                 CHECK_VIEW_HEIGHT;
504
505                 VLCButton *button = [[VLCButton alloc] initWithFrame: s_rc];
506                 CONTROL_CONFIG( button, o_module_name, CONFIG_ITEM_STRING , p_item->psz_name );
507                 [button setButtonType: NSMomentaryPushInButton];
508                 [button setBezelStyle: NSRoundedBezelStyle];
509                 [button setTitle: _NS("Browse...")];
510                 [button sizeToFit];
511                 [button setAutoresizingMask:NSViewMinXMargin];
512                 [button setFrameOrigin: NSMakePoint( s_vrc.size.width - ( 10 + [button frame].size.width), s_rc.origin.y)];
513
514                 [button setTarget: self];
515                 [button setAction: @selector(openFileDialog:)];
516
517                 s_rc.size.height = 25;
518                 s_rc.size.width = s_vrc.size.width - ( 35 + [button frame].size.width);
519                 
520                 o_text_field = [[VLCTextField alloc] initWithFrame: s_rc];
521                 CONTROL_CONFIG( o_text_field, o_module_name, CONFIG_ITEM_STRING , p_item->psz_name );
522
523                 [o_text_field setStringValue: [[VLCMain sharedInstance] localizedString: psz_value]];
524                 if ( psz_duptip != NULL )
525                 {
526                     [o_text_field setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString:
527                                             psz_duptip] toWidth: PREFS_WRAP ]];
528                     free(psz_duptip);
529                 }
530                 
531                 [[NSNotificationCenter defaultCenter] addObserver: self
532                     selector: @selector(configChanged:)
533                     name: NSControlTextDidChangeNotification
534                     object: o_text_field];
535                 [o_text_field setAutoresizingMask:NSViewWidthSizable];
536                 [button setTag: (int) o_text_field ]; /* FIXME */
537                 
538                 [o_view addSubview: [o_text_field autorelease]];
539                 [o_view addSubview: [button autorelease]];
540                 s_rc.origin.y += s_rc.size.height;
541                 s_rc.origin.x = X_ORIGIN;
542             }
543             break;
544             
545             case CONFIG_ITEM_STRING:            
546             {
547                 if( !p_item->ppsz_list )
548                 {
549                     char *psz_value = p_item->psz_value ?
550                                     p_item->psz_value : "";
551     
552                     INPUT_FIELD_STRING( p_item->psz_name, p_item->psz_text, 200,
553                                         [[VLCMain sharedInstance] localizedString: psz_value],
554                                         p_item->psz_longtext );
555                 }
556                 else
557                 {
558                     int i;
559                     VLCComboBox *o_combo_box;
560                     char * psz_duptip = NULL;
561                     if ( p_item->psz_longtext != NULL )
562                         psz_duptip = strdup( p_item->psz_longtext );
563     
564                     s_rc.size.height = 25;
565                     s_rc.size.width = 200;
566                     s_rc.origin.y += 10;
567     
568                     CHECK_VIEW_HEIGHT;
569     
570                     o_combo_box = [[VLCComboBox alloc] initWithFrame: s_rc];
571                     CONTROL_CONFIG( o_combo_box, o_module_name,
572                                     CONFIG_ITEM_STRING, p_item->psz_name );
573                     [o_combo_box setTarget: self];
574                     [o_combo_box setAction: @selector(configChanged:)];
575                     [o_combo_box sendActionOn:NSLeftMouseUpMask];
576                     [[NSNotificationCenter defaultCenter] addObserver: self
577                         selector: @selector(configChanged:)
578                         name: NSControlTextDidChangeNotification
579                         object: o_combo_box];
580
581                     if ( psz_duptip != NULL )
582                     {
583                         [o_combo_box setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
584                         free( psz_duptip );
585                     }
586                     [o_view addSubview: [o_combo_box autorelease]];
587                     
588                     for( i=0; p_item->ppsz_list[i]; i++ )
589                     {
590                         [o_combo_box addItemWithObjectValue:
591                             [[VLCMain sharedInstance] localizedString: p_item->ppsz_list[i]]];
592                     }
593                     [o_combo_box setStringValue: [[VLCMain sharedInstance] localizedString: 
594                         p_item->psz_value ? p_item->psz_value : ""]];
595     
596                     CONTROL_LABEL( p_item->psz_text );
597     
598                     s_rc.origin.y += s_rc.size.height;
599                     s_rc.origin.x = X_ORIGIN;
600                 }
601     
602             }
603             break;
604     
605             case CONFIG_ITEM_INTEGER:
606             {
607                 if( p_item->i_min == p_item->i_max )
608                 {
609                     INPUT_FIELD_INTEGER( p_item->psz_name, p_item->psz_text, 70,
610                         p_item->i_value, p_item->psz_longtext );
611                 }
612                 else
613                 {
614                     /*create a slider */
615                     VLCSlider *o_slider;
616                     char * psz_duptip = NULL;
617                     if ( p_item->psz_longtext != NULL )
618                         psz_duptip = strdup( p_item->psz_longtext );
619         
620                     s_rc.size.height = 27;
621                     s_rc.size.width = 200;
622                     s_rc.origin.y += 10;
623         
624                     CHECK_VIEW_HEIGHT;
625         
626                     o_slider = [[VLCSlider alloc] initWithFrame: s_rc];
627                     [o_slider setMinValue: p_item->i_min];
628                     [o_slider setMaxValue: p_item->i_max];
629                     [o_slider setIntValue: p_item->i_value];
630
631                     if ( psz_duptip != NULL )
632                     {
633                         [o_slider setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
634                         free( psz_duptip );
635                     }
636                     [o_slider setTarget: self];
637                     [o_slider setAction: @selector(configChanged:)];
638                     [o_slider sendActionOn:NSLeftMouseUpMask];
639                     CONTROL_CONFIG( o_slider, o_module_name,
640                                     CONFIG_ITEM_INTEGER, p_item->psz_name );
641                     [o_view addSubview: [o_slider autorelease]];
642                     CONTROL_LABEL( p_item->psz_text );
643         
644                     s_rc.origin.y += s_rc.size.height;
645                     s_rc.origin.x = X_ORIGIN;
646                 }
647             }
648             break;
649     
650             case CONFIG_ITEM_FLOAT:
651             {
652                 if( p_item->f_min == p_item->f_max )
653                 {
654                     INPUT_FIELD_FLOAT( p_item->psz_name, p_item->psz_text, 70,
655                         p_item->f_value, p_item->psz_longtext );
656                 }
657                 else
658                 {
659                     /* create a slider */
660                     VLCSlider *o_slider;
661                     char * psz_duptip = NULL;
662                     if ( p_item->psz_longtext != NULL )
663                         psz_duptip = strdup( p_item->psz_longtext );
664         
665                     s_rc.size.height = 27;
666                     s_rc.size.width = 200;
667                     s_rc.origin.y += 10;
668         
669                     CHECK_VIEW_HEIGHT;
670         
671                     o_slider = [[VLCSlider alloc] initWithFrame: s_rc];
672                     [o_slider setMinValue: p_item->f_min];
673                     [o_slider setMaxValue: p_item->f_max];
674                     [o_slider setFloatValue: p_item->f_value];
675
676                     if ( psz_duptip != NULL )
677                     {
678                         [o_slider setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
679                         free( psz_duptip );
680                     }
681                     [o_slider setTarget: self];
682                     [o_slider setAction: @selector(configChanged:)];
683                     [o_slider sendActionOn:NSLeftMouseUpMask];
684                     CONTROL_CONFIG( o_slider, o_module_name,
685                                     CONFIG_ITEM_FLOAT, p_item->psz_name );
686                     [o_view addSubview: [o_slider autorelease]];
687                     CONTROL_LABEL( p_item->psz_text );
688         
689                     s_rc.origin.y += s_rc.size.height;
690                     s_rc.origin.x = X_ORIGIN;
691                 }
692             }
693             break;
694     
695             case CONFIG_ITEM_BOOL:
696             {
697                 VLCButton *o_btn_bool;
698                 char * psz_duptip = NULL;
699
700                 if ( p_item->psz_longtext != NULL )
701                     psz_duptip = strdup( p_item->psz_longtext );
702     
703                 s_rc.size.height = 27;
704                 s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
705                 s_rc.origin.y += 10;
706     
707                 CHECK_VIEW_HEIGHT;
708     
709                 o_btn_bool = [[VLCButton alloc] initWithFrame: s_rc];
710                 [o_btn_bool setButtonType: NSSwitchButton];
711                 [o_btn_bool setIntValue: p_item->i_value];
712                 [o_btn_bool setTitle: [[VLCMain sharedInstance] localizedString: p_item->psz_text]];
713                 if ( psz_duptip != NULL )
714                 {
715                     [o_btn_bool setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
716                     free( psz_duptip );
717                 }
718                 [o_btn_bool setTarget: self];
719                 [o_btn_bool setAction: @selector(configChanged:)];
720                 CONTROL_CONFIG( o_btn_bool, o_module_name,
721                                 CONFIG_ITEM_BOOL, p_item->psz_name );
722                 [o_view addSubview: [o_btn_bool autorelease]];
723     
724                 s_rc.origin.y += s_rc.size.height;
725             }
726             break;
727
728             case CONFIG_ITEM_KEY:
729             {
730                 int i;
731                 char *psz_duptip = NULL;
732                 VLCComboBox *o_combo_box;
733
734                 if ( p_item->psz_longtext != NULL )
735                     psz_duptip = strdup( p_item->psz_longtext );
736
737                 s_rc.origin.y += 10;
738                 s_rc.size.width = - 10;
739                 s_rc.size.height = 20;
740                 CHECK_VIEW_HEIGHT;
741                 CONTROL_LABEL( p_item->psz_text );
742                 s_rc.origin.x = X_ORIGIN;
743                 s_rc.origin.y += s_rc.size.height;
744                 s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2;
745                 CHECK_VIEW_HEIGHT;
746                 VLCMatrix *o_matrix = [[VLCMatrix alloc] initWithFrame: s_rc mode: NSHighlightModeMatrix cellClass: [NSButtonCell class] numberOfRows:2 numberOfColumns:2];
747                 NSArray *o_cells = [o_matrix cells];
748                 for( i=0; i < [o_cells count]; i++ )
749                 {
750                     NSButtonCell *o_current_cell = [o_cells objectAtIndex:i];
751                     [o_current_cell setButtonType: NSSwitchButton];
752                     [o_current_cell setControlSize: NSSmallControlSize];
753                     if( psz_duptip != NULL )
754                     {
755                         [o_matrix setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP] forCell: o_current_cell];
756                     }
757                     switch( i )
758                     {
759                         case 0:
760                             [o_current_cell setTitle:_NS("Command")];
761                             [o_current_cell setState: p_item->i_value & KEY_MODIFIER_COMMAND];
762                             break;
763                         case 1:
764                             [o_current_cell setTitle:_NS("Control")];
765                             [o_current_cell setState: p_item->i_value & KEY_MODIFIER_CTRL];
766                             break;
767                         case 2:
768                             [o_current_cell setTitle:_NS("Option/Alt")];
769                             [o_current_cell setState: p_item->i_value & KEY_MODIFIER_ALT];
770                             break;
771                         case 3:
772                             [o_current_cell setTitle:_NS("Shift")];
773                             [o_current_cell setState: p_item->i_value & KEY_MODIFIER_SHIFT];
774                             break;
775                     }
776                     [o_current_cell setTarget: self];
777                     [o_current_cell setAction: @selector(configChanged:)];
778                     [o_current_cell sendActionOn:NSLeftMouseUpMask];
779                 }
780                 CONTROL_CONFIG( o_matrix, o_module_name,
781                                 CONFIG_ITEM_KEY, p_item->psz_name );
782                 [o_matrix sizeToCells];
783                 [o_view addSubview: [o_matrix autorelease]];
784
785                 s_rc.origin.x += [o_matrix frame].size.width + 20;
786                 s_rc.size.height = 25;
787                 s_rc.size.width = 100;
788
789                 CHECK_VIEW_HEIGHT;
790
791                 o_combo_box = [[VLCComboBox alloc] initWithFrame: s_rc];
792                 CONTROL_CONFIG( o_combo_box, o_module_name,
793                                 CONFIG_ITEM_KEY, p_item->psz_name );
794                 [o_combo_box setTarget: self];
795                 [o_combo_box setAction: @selector(configChanged:)];
796                 [o_combo_box sendActionOn:NSLeftMouseUpMask];
797                 [[NSNotificationCenter defaultCenter] addObserver: self
798                         selector: @selector(configChanged:)
799                         name: NSControlTextDidChangeNotification
800                         object: o_combo_box];
801
802                 if ( psz_duptip != NULL )
803                 {
804                     [o_combo_box setToolTip: [[VLCMain sharedInstance] wrapString: [[VLCMain sharedInstance] localizedString: psz_duptip] toWidth: PREFS_WRAP]];
805                 }
806                 [o_view addSubview: [o_combo_box autorelease]];
807                 
808                 for( i = 0; i < sizeof(vlc_keys) / sizeof(key_descriptor_t); i++ )
809                 {
810                     
811                     if( vlc_keys[i].psz_key_string && *vlc_keys[i].psz_key_string )
812                     [o_combo_box addItemWithObjectValue: [[VLCMain sharedInstance] localizedString:vlc_keys[i].psz_key_string]];
813                 }
814                 
815                 [o_combo_box setStringValue: [[VLCMain sharedInstance] localizedString:KeyToString(( ((unsigned int)p_item->i_value) & ~KEY_MODIFIER ))]];
816                 
817                 s_rc.origin.y += s_rc.size.height;
818                 s_rc.origin.x = X_ORIGIN;
819                 if( psz_duptip ) free( psz_duptip );
820             }
821             break;
822     
823             }
824     
825     #undef INPUT_FIELD_INTEGER
826     #undef INPUT_FIELD_FLOAT
827     #undef INPUT_FIELD_STRING
828     #undef INPUT_FIELD
829     #undef CHECK_VIEW_HEIGHT
830     #undef CONTROL_LABEL
831     #undef Y_ORIGIN
832     #undef X_ORIGIN
833         }
834         while( p_item->i_type != CONFIG_HINT_END && p_item++ );
835         vlc_object_release( p_parser );
836         vlc_list_release( p_list );
837     
838     [o_prefs_view setDocumentView: o_view];
839     [o_prefs_view setNeedsDisplay: TRUE];
840 }
841
842
843 @end
844
845 @implementation VLCPrefs (NSTableDataSource)
846
847 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
848     return (item == nil) ? [[VLCTreeItem rootItem] numberOfChildren] : [item numberOfChildren];
849 }
850
851 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item {
852     return (item == nil) ? YES : ([item numberOfChildren] != -1);
853 }
854
855 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
856     return (item == nil) ? [[VLCTreeItem rootItem] childAtIndex:index] : [item childAtIndex:index];
857 }
858
859 - (id)outlineView:(NSOutlineView *)outlineView objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item {
860     return (item == nil) ? @"" : (id)[item getName];
861 }
862
863 @end
864
865 @implementation VLCTreeItem
866
867 static VLCTreeItem *o_root_item = nil;
868
869 #define IsALeafNode ((id)-1)
870
871 - (id)initWithName: (NSString *)o_item_name ID: (int)i_id parent:(VLCTreeItem *)o_parent_item
872 {
873     self = [super init];
874
875     if( self != nil )
876     {
877         o_name = [o_item_name copy];
878         i_object_id = i_id;
879         o_parent = o_parent_item;
880     }
881     return( self );
882 }
883
884 + (VLCTreeItem *)rootItem {
885    if (o_root_item == nil) o_root_item = [[VLCTreeItem alloc] initWithName:@"main" ID: 0 parent:nil];
886    return o_root_item;       
887 }
888
889 - (void)dealloc
890 {
891     if (o_children != IsALeafNode) [o_children release];
892     [o_name release];
893     [super dealloc];
894 }
895
896 /* Creates and returns the array of children
897  * Loads children incrementally */
898 - (NSArray *)children {
899     if (o_children == NULL) {
900         intf_thread_t *p_intf = VLCIntf;
901         vlc_list_t      *p_list;
902         module_t        *p_module = NULL;
903         module_config_t *p_item;
904         int i_index,j;
905
906         /* List the modules */
907         p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
908         if( !p_list ) return nil;
909
910         if( [[self getName] isEqualToString: @"main"] )
911         {
912             /*
913             * Build a tree of the main options
914             */
915             for( i_index = 0; i_index < p_list->i_count; i_index++ )
916             {
917                 p_module = (module_t *)p_list->p_values[i_index].p_object;
918                 if( !strcmp( p_module->psz_object_name, "main" ) )
919                     break;
920             }
921             if( p_module == NULL )
922             {
923                 msg_Err( p_intf, "could not find the main module in our preferences" );
924                 return nil;
925             }
926             if( i_index < p_list->i_count )
927             {
928                 /* We found the main module */
929         
930                 /* Enumerate config categories and store a reference so we can
931                  * generate their config panel them when it is asked by the user. */
932                 p_item = p_module->p_config;
933                 o_children = [[NSMutableArray alloc] initWithCapacity:10];
934
935                 if( p_item ) do
936                 {
937                     NSString *o_child_name;
938                     
939                     switch( p_item->i_type )
940                     {
941                     case CONFIG_HINT_CATEGORY:
942                         o_child_name = [[VLCMain sharedInstance] localizedString: p_item->psz_text];
943                         [o_children addObject:[[VLCTreeItem alloc] initWithName: o_child_name
944                             ID: p_module->i_object_id parent:self]];
945                         break;
946                     }
947                 }
948                 while( p_item->i_type != CONFIG_HINT_END && p_item++ );
949                 
950                 /* Add the modules item */
951                 [o_children addObject:[[VLCTreeItem alloc] initWithName: _NS("Modules")
952                     ID: 0 parent:self]];
953             }
954             else
955             {
956                 o_children = IsALeafNode;
957             }
958         }
959         else if( [[self getName] isEqualToString: _NS("Modules")] )
960         {
961             /* Add the capabilities */
962             o_children = [[NSMutableArray alloc] initWithCapacity:10];
963             for( i_index = 0; i_index < p_list->i_count; i_index++ )
964             {
965                 p_module = (module_t *)p_list->p_values[i_index].p_object;
966         
967                 /* Exclude the main module */
968                 if( !strcmp( p_module->psz_object_name, "main" ) )
969                     continue;
970         
971                 /* Exclude empty modules */
972                 p_item = p_module->p_config;
973                 if( !p_item ) continue;
974                 do
975                 {
976                     if( p_item->i_type & CONFIG_ITEM )
977                         break;
978                 }
979                 while( p_item->i_type != CONFIG_HINT_END && p_item++ );
980                 if( p_item->i_type == CONFIG_HINT_END ) continue;
981         
982                 /* Create the capability tree if it doesn't already exist */
983                 NSString *o_capability;
984                 o_capability = [[VLCMain sharedInstance] localizedString: p_module->psz_capability];
985                 if( !p_module->psz_capability || !*p_module->psz_capability )
986                 {
987                     /* Empty capability ? Let's look at the submodules */
988                     module_t * p_submodule;
989                     for( j = 0; j < p_module->i_children; j++ )
990                     {
991                         p_submodule = (module_t*)p_module->pp_children[ j ];
992                         if( p_submodule->psz_capability && *p_submodule->psz_capability )
993                         {
994                             o_capability = [[VLCMain sharedInstance] localizedString: p_submodule->psz_capability];
995                             BOOL b_found = FALSE;
996                             for( j = 0; j < (int)[o_children count]; j++ )
997                             {
998                                 if( [[[o_children objectAtIndex:j] getName] isEqualToString: o_capability] )
999                                 {
1000                                     b_found = TRUE;
1001                                     break;
1002                                 }
1003                             }
1004                             if( !b_found )
1005                             {
1006                                 [o_children addObject:[[VLCTreeItem alloc] initWithName: o_capability
1007                                 ID: 0 parent:self]];
1008                             }
1009                         }
1010                     }
1011                 }
1012
1013                 BOOL b_found = FALSE;
1014                 for( j = 0; j < (int)[o_children count]; j++ )
1015                 {
1016                     if( [[[o_children objectAtIndex:j] getName] isEqualToString: o_capability] )
1017                     {
1018                         b_found = TRUE;
1019                         break;
1020                     }
1021                 }
1022                 if( !b_found )
1023                 {
1024                     [o_children addObject:[[VLCTreeItem alloc] initWithName: o_capability
1025                     ID: 0 parent:self]];
1026                 }
1027             }
1028         }
1029         else if( [[o_parent getName] isEqualToString: _NS("Modules")] )
1030         {
1031             /* Now add the modules */
1032             o_children = [[NSMutableArray alloc] initWithCapacity:10];
1033             for( i_index = 0; i_index < p_list->i_count; i_index++ )
1034             {
1035                 p_module = (module_t *)p_list->p_values[i_index].p_object;
1036         
1037                 /* Exclude the main module */
1038                 if( !strcmp( p_module->psz_object_name, "main" ) )
1039                     continue;
1040         
1041                 /* Exclude empty modules */
1042                 p_item = p_module->p_config;
1043                 if( !p_item ) continue;
1044                 do
1045                 {
1046                     if( p_item->i_type & CONFIG_ITEM )
1047                         break;
1048                 }
1049                 while( p_item->i_type != CONFIG_HINT_END && p_item++ );
1050                 if( p_item->i_type == CONFIG_HINT_END ) continue;
1051         
1052                 /* Check the capability */
1053                 NSString *o_capability;
1054                 o_capability = [[VLCMain sharedInstance] localizedString: p_module->psz_capability];
1055                 if( !p_module->psz_capability || !*p_module->psz_capability )
1056                 {
1057                     /* Empty capability ? Let's look at the submodules */
1058                     module_t * p_submodule;
1059                     for( j = 0; j < p_module->i_children; j++ )
1060                     {
1061                         p_submodule = (module_t*)p_module->pp_children[ j ];
1062                         if( p_submodule->psz_capability && *p_submodule->psz_capability )
1063                         {
1064                             o_capability = [[VLCMain sharedInstance] localizedString: p_submodule->psz_capability];
1065                             if( [o_capability isEqualToString: [self getName]] )
1066                             {
1067                             [o_children addObject:[[VLCTreeItem alloc] initWithName:
1068                                 [[VLCMain sharedInstance] localizedString: p_module->psz_object_name ]
1069                                 ID: p_module->i_object_id parent:self]];
1070                             }
1071                         }
1072                     }
1073                 }
1074                 else if( [o_capability isEqualToString: [self getName]] )
1075                 {
1076                     [o_children addObject:[[VLCTreeItem alloc] initWithName:
1077                         [[VLCMain sharedInstance] localizedString: p_module->psz_object_name ]
1078                         ID: p_module->i_object_id parent:self]];
1079                 }
1080             }
1081         }
1082         else
1083         {
1084             /* all the other stuff are leafs */
1085             o_children = IsALeafNode;
1086         }
1087         vlc_list_release( p_list );
1088     }
1089     return o_children;
1090 }
1091
1092 - (int)getObjectID
1093 {
1094     return i_object_id;
1095 }
1096
1097 - (NSString *)getName
1098 {
1099     return o_name;
1100 }
1101
1102 - (VLCTreeItem *)childAtIndex:(int)i_index {
1103     return [[self children] objectAtIndex:i_index];
1104 }
1105
1106 - (int)numberOfChildren {
1107     id i_tmp = [self children];
1108     return (i_tmp == IsALeafNode) ? (-1) : (int)[i_tmp count];
1109 }
1110
1111 - (BOOL)hasPrefs:(NSString *)o_module_name
1112 {
1113     intf_thread_t *p_intf = VLCIntf;
1114     module_t *p_parser;
1115     vlc_list_t *p_list;
1116     char *psz_module_name;
1117     int i_index;
1118
1119     psz_module_name = (char *)[o_module_name UTF8String];
1120
1121     /* look for module */
1122     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
1123
1124     for( i_index = 0; i_index < p_list->i_count; i_index++ )
1125     {
1126         p_parser = (module_t *)p_list->p_values[i_index].p_object ;
1127
1128         if( !strcmp( p_parser->psz_object_name, psz_module_name ) )
1129         {
1130             BOOL b_has_prefs = p_parser->i_config_items != 0;
1131             vlc_list_release( p_list );
1132             return( b_has_prefs );
1133         }
1134     }
1135
1136     vlc_list_release( p_list );
1137
1138     return( NO );
1139 }
1140
1141 @end
1142
1143
1144 @implementation VLCFlippedView
1145
1146 - (BOOL)isFlipped
1147 {
1148     return( YES );
1149 }
1150
1151 @end
1152
1153 IMPL_CONTROL_CONFIG(Button);
1154 IMPL_CONTROL_CONFIG(PopUpButton);
1155 IMPL_CONTROL_CONFIG(ComboBox);
1156 IMPL_CONTROL_CONFIG(TextField);
1157 IMPL_CONTROL_CONFIG(Slider);
1158 IMPL_CONTROL_CONFIG(Matrix);