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