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