3b6d027f3a97a6ea440417d79bfa1060b8a437d1
[vlc.git] / 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.21 2003/05/11 23:17:30 hartman Exp $
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24 /*****************************************************************************
25  * Preamble
26  *****************************************************************************/
27 #include <stdlib.h>                                      /* malloc(), free() */
28 #include <sys/param.h>                                    /* for MAXPATHLEN */
29 #include <string.h>
30
31 #include "intf.h"
32 #include "prefs.h"
33
34 /*****************************************************************************
35  * VLCPrefs implementation
36  *****************************************************************************/
37 @implementation VLCPrefs
38
39 - (id)init
40 {
41     self = [super init];
42
43     if( self != nil )
44     {
45         p_intf = [NSApp getIntf];
46
47         o_pref_panels = [[NSMutableDictionary alloc] init];
48         o_toolbars = [[NSMutableDictionary alloc] init];
49         o_scroll_views = [[NSMutableDictionary alloc] init];
50         o_panel_views = [[NSMutableDictionary alloc] init];
51         o_save_prefs = [[NSMutableDictionary alloc] init];
52         b_advanced = config_GetInt( p_intf, "advanced" );
53     }
54
55     return( self );
56 }
57
58 - (void)dealloc
59 {
60     id v1, v2;
61     NSEnumerator *o_e1;
62     NSEnumerator *o_e2;
63
64 #define DIC_REL1(o_dic) \
65     { \
66     o_e1 = [o_dic objectEnumerator]; \
67     while( (v1 = [o_e1 nextObject]) ) \
68     { \
69         [v1 release]; \
70     } \
71     [o_dic removeAllObjects]; \
72     [o_dic release]; \
73     }
74
75 #define DIC_REL2(o_dic) \
76     { \
77         o_e2 = [o_dic objectEnumerator]; \
78         while( (v2 = [o_e2 nextObject]) ) \
79         { \
80             DIC_REL1(v2); \
81         } \
82         [o_dic removeAllObjects]; \
83     }
84
85     DIC_REL1(o_pref_panels);
86     DIC_REL2(o_toolbars);
87     DIC_REL1(o_scroll_views);
88     DIC_REL2(o_panel_views);
89     DIC_REL1(o_save_prefs);
90
91 #undef DIC_REL1
92 #undef DIC_REL2
93
94     [super dealloc];
95 }
96
97 - (BOOL)hasPrefs:(NSString *)o_module_name
98 {
99     module_t *p_parser;
100     vlc_list_t *p_list;
101     char *psz_module_name;
102     int i_index;
103
104     psz_module_name = (char *)[o_module_name lossyCString];
105
106     /* look for module */
107     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
108
109     for( i_index = 0; i_index < p_list->i_count; i_index++ )
110     {
111         p_parser = (module_t *)p_list->p_values[i_index].p_object ;
112
113         if( !strcmp( p_parser->psz_object_name, psz_module_name ) )
114         {
115             BOOL b_has_prefs = p_parser->i_config_items != 0;
116             vlc_list_release( p_list );
117             return( b_has_prefs );
118         }
119     }
120
121     vlc_list_release( p_list );
122
123     return( NO );
124 }
125
126 - (void)createPrefPanel:(NSString *)o_module_name
127 {
128     int i_pos;
129     int i_module_tag;
130
131     module_t *p_parser = NULL;
132     vlc_list_t *p_list;
133     module_config_t *p_item;
134     char *psz_module_name;
135     int i_index;
136
137     NSPanel *o_panel;                   /* panel                        */
138     NSRect s_panel_rc;                  /* panel rect                   */
139     NSView *o_panel_view;               /* panel view                   */
140     NSToolbar *o_toolbar;               /* panel toolbar                */
141     NSMutableDictionary *o_tb_items;    /* panel toolbar items          */
142     NSScrollView *o_scroll_view;        /* panel scroll view            */
143     NSRect s_scroll_rc;                 /* panel scroll view rect       */
144     NSMutableDictionary *o_views;       /* panel scroll view docviews   */
145
146     NSRect s_rc;                        /* rect                         */
147     NSView *o_view;                     /* view                         */
148     NSRect s_vrc;                       /* view rect                    */
149     NSButton *o_button;                 /* button                       */
150     NSRect s_brc;                       /* button rect                  */
151     VLCTextField *o_text_field;         /* input field / label          */
152
153     o_panel = [o_pref_panels objectForKey: o_module_name];
154     if( o_panel != nil )
155     {
156         [o_panel center];
157         [o_panel makeKeyAndOrderFront: nil];
158         return;
159     }
160
161     psz_module_name = (char *)[o_module_name lossyCString];
162
163     /* Look for the selected module */
164     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
165
166     for( i_index = 0; i_index < p_list->i_count; i_index++ )
167     {
168         p_parser = (module_t *)p_list->p_values[i_index].p_object ;
169
170         if( psz_module_name
171             && !strcmp( psz_module_name, p_parser->psz_object_name ) )
172         {
173             break;
174         }
175     }
176
177     if( !p_parser || i_index == p_list->i_count )
178     {
179         vlc_list_release( p_list );
180         return;
181     }
182
183     /* We found it, now we can start building its configuration interface */
184
185     s_panel_rc = NSMakeRect( 0, 0, 450, 450 );
186     o_panel = [[NSPanel alloc] initWithContentRect: s_panel_rc
187                                styleMask: NSTitledWindowMask
188                                backing: NSBackingStoreBuffered
189                                defer: YES];
190     o_toolbar = [[NSToolbar alloc] initWithIdentifier: o_module_name];
191     [o_panel setTitle: [NSString stringWithFormat: @"%@ (%@)",
192                                  _NS("Preferences"), o_module_name]];
193     o_panel_view = [o_panel contentView];
194
195     s_scroll_rc = s_panel_rc;
196     s_scroll_rc.size.height -= 55; s_scroll_rc.origin.y += 55;
197     o_scroll_view = [[NSScrollView alloc] initWithFrame: s_scroll_rc];
198     [o_scroll_views setObject: o_scroll_view forKey: o_module_name];
199     [o_scroll_view setBorderType: NSGrooveBorder];
200     [o_scroll_view setHasVerticalScroller: YES];
201     [o_scroll_view setDrawsBackground: NO];
202     [o_scroll_view setRulersVisible: YES];
203     [o_panel_view addSubview: o_scroll_view];
204
205     o_tb_items = [[NSMutableDictionary alloc] init];
206     o_views = [[NSMutableDictionary alloc] init];
207
208     [o_save_prefs setObject: [[NSMutableArray alloc] init]
209                   forKey: o_module_name];
210
211     /* Enumerate config options and add corresponding config boxes */
212     p_item = p_parser->p_config;
213
214     i_pos = 0;
215     o_view = nil;
216     i_module_tag = 3;
217
218 #define X_ORIGIN 20
219 #define Y_ORIGIN (X_ORIGIN - 10)
220
221 #define CHECK_VIEW_HEIGHT \
222     { \
223         float f_new_pos = s_rc.origin.y + s_rc.size.height + X_ORIGIN; \
224         if( f_new_pos > s_vrc.size.height ) \
225         { \
226             s_vrc.size.height = f_new_pos; \
227             [o_view setFrame: s_vrc]; \
228         } \
229     }
230
231 #define CONTROL_LABEL( label ) \
232     { \
233         s_rc.origin.x += s_rc.size.width + 10; \
234         s_rc.size.width = s_vrc.size.width - s_rc.origin.x - X_ORIGIN - 20; \
235         o_text_field = [[NSTextField alloc] initWithFrame: s_rc]; \
236         [o_text_field setDrawsBackground: NO]; \
237         [o_text_field setBordered: NO]; \
238         [o_text_field setEditable: NO]; \
239         [o_text_field setSelectable: NO]; \
240         if ( label ) \
241         { \
242             [o_text_field setStringValue: \
243                 [NSApp localizedString: label]]; \
244         } \
245         [o_text_field sizeToFit]; \
246         [o_view addSubview: [o_text_field autorelease]]; \
247     }
248
249 #define INPUT_FIELD( ctype, cname, label, w, msg, param, tip ) \
250     { \
251         char * psz_duptip = NULL; \
252         if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding ) \
253             psz_duptip = strdup(p_item->psz_longtext); \
254         s_rc.size.height = 25; \
255         s_rc.size.width = w; \
256         s_rc.origin.y += 10; \
257         CHECK_VIEW_HEIGHT; \
258         o_text_field = [[VLCTextField alloc] initWithFrame: s_rc]; \
259         [o_text_field setAlignment: NSRightTextAlignment]; \
260         CONTROL_CONFIG( o_text_field, o_module_name, ctype, cname ); \
261         [o_text_field msg: param]; \
262         if ( psz_duptip != NULL ) \
263         { \
264             [o_text_field setToolTip: [NSApp localizedString: \
265                                        vlc_wraptext(psz_duptip, PREFS_WRAP)]]; \
266             free(psz_duptip);\
267         } \
268         [o_view addSubview: [o_text_field autorelease]]; \
269         [[NSNotificationCenter defaultCenter] addObserver: self \
270             selector: @selector(configChanged:) \
271             name: NSControlTextDidChangeNotification \
272             object: o_text_field]; \
273         CONTROL_LABEL( label ); \
274         s_rc.origin.y += s_rc.size.height; \
275         s_rc.origin.x = X_ORIGIN; \
276     }
277
278 #define INPUT_FIELD_INTEGER( name, label, w, param, tip ) \
279     INPUT_FIELD( CONFIG_ITEM_INTEGER, name, label, w, setIntValue, param, tip )
280 #define INPUT_FIELD_FLOAT( name, label, w, param, tip ) \
281     INPUT_FIELD( CONFIG_ITEM_FLOAT, name, label, w, setFloatValue, param, tip )
282 #define INPUT_FIELD_STRING( name, label, w, param, tip ) \
283     INPUT_FIELD( CONFIG_ITEM_STRING, name, label, w, setStringValue, param, tip )
284
285     if( p_item ) do
286     {
287         if( p_item->b_advanced && !b_advanced )
288         {
289             continue;
290         }
291         switch( p_item->i_type )
292         {
293
294         case CONFIG_HINT_CATEGORY:
295         {
296             NSString *o_key;
297             NSString *o_label;
298             NSToolbarItem *o_tbi;
299
300             o_label = [NSApp localizedString: p_item->psz_text];
301             o_tbi = [[NSToolbarItem alloc] initWithItemIdentifier: o_label];
302             [o_tbi setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
303             [o_tbi setLabel: o_label];
304             [o_tbi setTarget: self];
305             [o_tbi setAction: @selector(selectPrefView:)];
306
307             o_key = [NSString stringWithFormat: @"%02d %@",
308                                                 i_pos, o_label];
309             [o_tb_items setObject: o_tbi forKey: o_key];
310
311             s_vrc = s_scroll_rc; s_vrc.size.height -= 4;
312             o_view = [[VLCFlippedView alloc] initWithFrame: s_vrc];
313             [o_views setObject: o_view forKey: o_label];
314
315             s_rc.origin.x = X_ORIGIN;
316             s_rc.origin.y = Y_ORIGIN;
317
318             i_module_tag = 3;
319
320             if( i_pos == 0 )
321             {
322                 [o_scroll_view setDocumentView: o_view];
323             }
324
325             i_pos++;
326         }
327         break;
328
329         case CONFIG_ITEM_MODULE:
330         {
331             NSBox *o_box;
332             NSRect s_crc;
333             NSView *o_cview;
334             NSPopUpButton *o_modules;
335             NSButton *o_btn_select;
336             NSButton *o_btn_configure;
337             char * psz_duptip = NULL;
338             if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
339                 psz_duptip = strdup(p_item->psz_longtext);
340
341 #define MODULE_BUTTON( button, title, sel ) \
342     { \
343         s_brc.size.height = 32; \
344         s_brc.origin.x += s_brc.size.width + 10; \
345         s_brc.size.width = s_crc.size.width - s_brc.origin.x - 10; \
346         button = [[NSButton alloc] initWithFrame: s_brc]; \
347         [button setButtonType: NSMomentaryPushInButton]; \
348         [button setBezelStyle: NSRoundedBezelStyle]; \
349         [button setTitle: title]; \
350         [button setTag: i_module_tag++]; \
351         [button setTarget: self]; \
352         [button setAction: @selector(sel)]; \
353         [o_cview addSubview: [button autorelease]]; \
354     }
355
356             s_rc.size.height = 107;
357             s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
358             s_rc.origin.y += i_module_tag == 3 ? Y_ORIGIN : 20;
359
360             CHECK_VIEW_HEIGHT;
361
362             o_box = [[NSBox alloc] initWithFrame: s_rc];
363             [o_box setTitle: [NSApp localizedString: p_item->psz_text]];
364             [o_view addSubview: [o_box autorelease]];
365             s_rc.origin.y += s_rc.size.height + 10;
366             o_cview = [[VLCFlippedView alloc] initWithFrame: s_rc];
367             [o_box setContentView: [o_cview autorelease]];
368             s_crc = [o_cview bounds];
369
370             s_brc = NSMakeRect( 5, 10, 200, 30 );
371             o_modules = [[NSPopUpButton alloc] initWithFrame: s_brc];
372             [o_modules setTag: i_module_tag++];
373             [o_modules setTarget: self];
374             [o_modules setAction: @selector(moduleSelected:)];
375             if ( psz_duptip != NULL )
376             {
377                 [o_modules setToolTip: [NSApp localizedString:
378                                         vlc_wraptext(psz_duptip, PREFS_WRAP)]];
379                 free( psz_duptip );
380             }
381             [o_cview addSubview: [o_modules autorelease]];
382
383             MODULE_BUTTON( o_btn_configure, _NS("Configure"),
384                            configureModule: );
385
386             s_brc = NSMakeRect( 8, s_brc.origin.y + s_brc.size.height + 10,
387                                 194, 25 );
388             o_text_field = [[VLCTextField alloc] initWithFrame: s_brc];
389             [o_text_field setTag: i_module_tag++];
390             [o_text_field setAlignment: NSLeftTextAlignment];
391             CONTROL_CONFIG( o_text_field, o_module_name,
392                             CONFIG_ITEM_MODULE, p_item->psz_name );
393             [[NSNotificationCenter defaultCenter] addObserver: self
394                 selector: @selector(configChanged:)
395                 name: NSControlTextDidChangeNotification
396                 object: o_text_field];
397             [o_cview addSubview: [o_text_field autorelease]];
398
399             s_brc.origin.x += 3;
400             MODULE_BUTTON( o_btn_select, _NS("Select"),
401                            selectModule: );
402
403             [o_modules addItemWithTitle: _NS("None")];
404
405             /* build a list of available modules */
406             {
407                 for( i_index = 0; i_index < p_list->i_count; i_index++ )
408                 {
409                     p_parser = (module_t *)p_list->p_values[i_index].p_object ;
410
411                     if( !strcmp( p_parser->psz_capability,
412                                  p_item->psz_type ) )
413                     {
414                         NSString *o_object_name = [NSString
415                             stringWithCString: p_parser->psz_object_name];
416                         [o_modules addItemWithTitle: o_object_name];
417                     }
418                 }
419             }
420
421             if( p_item->psz_value != NULL )
422             {
423                 NSString *o_value =
424                     [NSString stringWithCString: p_item->psz_value];
425
426                 [o_text_field setStringValue: o_value];
427                 [o_modules selectItemWithTitle: o_value];
428                 [o_btn_configure setEnabled: [self hasPrefs: o_value]];
429             }
430             else
431             {
432                 [o_modules selectItemWithTitle: _NS("None")];
433                 [o_btn_configure setEnabled: NO];
434             }
435
436 #undef MODULE_BUTTON
437         }
438         break;
439
440         case CONFIG_ITEM_STRING:
441         case CONFIG_ITEM_FILE:
442         case CONFIG_ITEM_DIRECTORY:
443         {
444
445             if( !p_item->ppsz_list )
446             {
447                 char *psz_value = p_item->psz_value ?
448                                   p_item->psz_value : "";
449
450                 INPUT_FIELD_STRING( p_item->psz_name, p_item->psz_text, 150,
451                                     [NSString stringWithCString: psz_value],
452                                     p_item->psz_longtext );
453             }
454             else
455             {
456                 int i;
457                 VLCComboBox *o_combo_box;
458                 char * psz_duptip = NULL;
459                 if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
460                     psz_duptip = strdup(p_item->psz_longtext);
461
462                 s_rc.size.height = 27;
463                 s_rc.size.width = 150;
464                 s_rc.origin.y += 10;
465
466                 CHECK_VIEW_HEIGHT;
467
468                 o_combo_box = [[VLCComboBox alloc] initWithFrame: s_rc];
469                 CONTROL_CONFIG( o_combo_box, o_module_name,
470                                 CONFIG_ITEM_STRING, p_item->psz_name );
471                 if ( psz_duptip != NULL )
472                 {
473                     [o_combo_box setToolTip: [NSApp localizedString:
474                                         vlc_wraptext(psz_duptip, PREFS_WRAP)]];
475                     free( psz_duptip );
476                 }
477                 [o_view addSubview: [o_combo_box autorelease]];
478                 
479                 for( i=0; p_item->ppsz_list[i]; i++ )
480                 {
481                     [o_combo_box addItemWithObjectValue:
482                         [NSString stringWithCString: p_item->ppsz_list[i]]];
483                 }
484                 [o_combo_box setStringValue: [NSString stringWithCString: 
485                     p_item->psz_value ? p_item->psz_value : ""]];
486
487                 [[NSNotificationCenter defaultCenter] addObserver: self
488                     selector: @selector(configChanged:)
489                     name: NSControlTextDidChangeNotification
490                     object: o_combo_box];
491                 [[NSNotificationCenter defaultCenter] addObserver: self
492                     selector: @selector(configChanged:)
493                     name: NSComboBoxSelectionDidChangeNotification
494                     object: o_combo_box];
495
496                 CONTROL_LABEL( p_item->psz_text );
497
498                 s_rc.origin.y += s_rc.size.height;
499                 s_rc.origin.x = X_ORIGIN;
500             }
501
502         }
503         break;
504
505         case CONFIG_ITEM_INTEGER:
506         {
507             INPUT_FIELD_INTEGER( p_item->psz_name, p_item->psz_text, 70,
508                                  p_item->i_value, p_item->psz_longtext );
509         }
510         break;
511
512         case CONFIG_ITEM_FLOAT:
513         {
514             INPUT_FIELD_FLOAT( p_item->psz_name, p_item->psz_text, 70,
515                                p_item->f_value, p_item->psz_longtext );
516         }
517         break;
518
519         case CONFIG_ITEM_BOOL:
520         {
521             VLCButton *o_btn_bool;
522             char * psz_duptip = NULL;
523             if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
524                 psz_duptip = strdup(p_item->psz_longtext);
525
526             s_rc.size.height = 27;
527             s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
528             s_rc.origin.y += 10;
529
530             CHECK_VIEW_HEIGHT;
531
532             o_btn_bool = [[VLCButton alloc] initWithFrame: s_rc];
533             [o_btn_bool setButtonType: NSSwitchButton];
534             [o_btn_bool setIntValue: p_item->i_value];
535             [o_btn_bool setTitle:
536                 [NSApp localizedString: p_item->psz_text]];
537             if ( psz_duptip != NULL )
538             {
539                 [o_btn_bool setToolTip: [NSApp localizedString:
540                                         vlc_wraptext(psz_duptip, PREFS_WRAP)]];
541                 free( psz_duptip );
542             }
543             [o_btn_bool setTarget: self];
544             [o_btn_bool setAction: @selector(configChanged:)];
545             CONTROL_CONFIG( o_btn_bool, o_module_name,
546                             CONFIG_ITEM_BOOL, p_item->psz_name );
547             [o_view addSubview: [o_btn_bool autorelease]];
548
549             s_rc.origin.y += s_rc.size.height;
550         }
551         break;
552
553         }
554
555 #undef INPUT_FIELD_INTEGER
556 #undef INPUT_FIELD_FLOAT
557 #undef INPUT_FIELD_STRING
558 #undef INPUT_FIELD
559 #undef CHECK_VIEW_HEIGHT
560 #undef CONTROL_LABEL
561 #undef Y_ORIGIN
562 #undef X_ORIGIN
563     }
564     while( p_item->i_type != CONFIG_HINT_END && p_item++ );
565
566     vlc_list_release( p_list );
567
568     [o_toolbars setObject: o_tb_items forKey: o_module_name];
569     [o_toolbar setDelegate: self];
570     [o_panel setToolbar: [o_toolbar autorelease]];
571
572 #define DEF_PANEL_BUTTON( tag, title, sel ) \
573     { \
574         o_button = [[NSButton alloc] initWithFrame: s_rc]; \
575         [o_button setButtonType: NSMomentaryPushInButton]; \
576         [o_button setBezelStyle: NSRoundedBezelStyle]; \
577         [o_button setAction: @selector(sel)]; \
578         [o_button setTarget: self]; \
579         [o_button setTitle: title]; \
580         [o_button setTag: tag]; \
581         [o_panel_view addSubview: [o_button autorelease]]; \
582     }
583
584     s_rc.origin.y = s_panel_rc.origin.y + 14;
585     s_rc.size.height = 25; s_rc.size.width = 105;
586     s_rc.origin.x = s_panel_rc.size.width - s_rc.size.width - 14;
587     DEF_PANEL_BUTTON( 0, _NS("OK"), clickedApplyCancelOK: );
588     [o_panel setDefaultButtonCell: [o_button cell]];
589
590     s_rc.origin.x -= s_rc.size.width;
591     DEF_PANEL_BUTTON( 1, _NS("Cancel"), clickedApplyCancelOK: );
592     [o_button setKeyEquivalent: @"\E"];
593
594     s_rc.origin.x = 20;
595     o_button = [[NSButton alloc] initWithFrame: s_rc];
596     [o_button setButtonType: NSSwitchButton];
597     [o_button setAction: @selector(clickedApplyCancelOK:)];
598     [o_button setTarget: self];
599     [o_button setTitle: _NS("Advanced")];
600     [o_button setTag: 2];
601     [o_panel_view addSubview: [o_button autorelease]];
602     [o_button setState: b_advanced];
603
604 #undef DEF_PANEL_BUTTON
605
606     [o_pref_panels setObject: o_panel forKey: o_module_name];
607     [o_panel_views setObject: o_views forKey: o_module_name];
608
609     [o_panel center];
610     [o_panel makeKeyAndOrderFront: nil];
611 }
612
613 - (void)destroyPrefPanel:(id)o_unknown
614 {
615     id v1;
616     NSPanel *o_panel;
617     NSEnumerator *o_e1;
618     NSMutableArray *o_prefs;
619     NSMutableDictionary *o_dic;
620     NSScrollView *o_scroll_view;
621     NSString *o_module_name;
622
623     o_module_name = (NSString *)([o_unknown isKindOfClass: [NSTimer class]] ?
624                                  [o_unknown userInfo] : o_unknown);
625
626 #define DIC_REL(dic) \
627     { \
628     o_dic = [dic objectForKey: o_module_name]; \
629     [dic removeObjectForKey: o_module_name]; \
630     o_e1 = [o_dic objectEnumerator]; \
631     while( (v1 = [o_e1 nextObject]) ) \
632     { \
633         [v1 release]; \
634     } \
635     [o_dic removeAllObjects]; \
636     [o_dic release]; \
637     }
638
639     o_panel = [o_pref_panels objectForKey: o_module_name];
640     [o_pref_panels removeObjectForKey: o_module_name];
641     [o_panel release];
642
643     DIC_REL(o_toolbars);
644
645     o_scroll_view = [o_scroll_views objectForKey: o_module_name];
646     [o_scroll_views removeObjectForKey: o_module_name];
647     [o_scroll_view release];
648
649     DIC_REL(o_panel_views);
650
651     o_prefs = [o_save_prefs objectForKey: o_module_name];
652     [o_save_prefs removeObjectForKey: o_module_name];
653     [o_prefs removeAllObjects];
654     [o_prefs release];
655
656 #undef DIC_REL
657
658 }
659
660 - (void)selectPrefView:(id)sender
661 {
662     NSView *o_view;
663     NSString *o_module_name;
664     NSScrollView *o_scroll_view;
665     NSMutableDictionary *o_views;
666
667     o_module_name = [[sender toolbar] identifier];
668     o_views = [o_panel_views objectForKey: o_module_name];
669     o_view = [o_views objectForKey: [sender label]];
670
671     o_scroll_view = [o_scroll_views objectForKey: o_module_name];
672     [o_scroll_view setDocumentView: o_view];
673 }
674
675 - (void)moduleSelected:(id)sender
676 {
677     NSButton *o_btn_config;
678     NSString *o_module_name;
679     BOOL b_has_prefs = NO;
680
681     o_module_name = [sender titleOfSelectedItem];
682     o_btn_config = [[sender superview] viewWithTag: [sender tag] + 1];
683
684     if( ![o_module_name isEqualToString: _NS("None")] )
685     {
686         b_has_prefs = [self hasPrefs: o_module_name];
687     }
688
689     [o_btn_config setEnabled: b_has_prefs];
690 }
691
692 - (void)configureModule:(id)sender
693 {
694     NSString *o_module_name;
695     NSPopUpButton *o_modules;
696
697     o_modules = [[sender superview] viewWithTag: [sender tag] - 1];
698     o_module_name = [o_modules titleOfSelectedItem];
699
700     [self createPrefPanel: o_module_name];
701 }
702
703 - (void)selectModule:(id)sender
704 {
705     NSString *o_module_name;
706     NSPopUpButton *o_modules;
707     NSTextField *o_module;
708
709     o_module = [[sender superview] viewWithTag: [sender tag] - 1];
710     o_modules = [[sender superview] viewWithTag: [sender tag] - 3];
711     o_module_name = [o_modules titleOfSelectedItem];
712
713     if( [o_module_name isEqualToString: _NS("None")] )
714     {
715         o_module_name = [NSString string];
716     }
717
718     [o_module setStringValue: o_module_name];
719     [self configChanged: o_module];
720 }
721
722 - (void)configChanged:(id)o_unknown
723 {
724     id o_vlc_config = [o_unknown isKindOfClass: [NSNotification class]] ?
725                       [o_unknown object] : o_unknown;
726
727     NSString *o_module_name = [o_vlc_config moduleName];
728     NSPanel *o_pref_panel = [o_pref_panels objectForKey: o_module_name];
729     NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];
730
731     if( [o_prefs indexOfObjectIdenticalTo: o_vlc_config] == NSNotFound )
732     {
733         NSView *o_pref_view = [o_pref_panel contentView];
734         NSButton *o_btn_apply = [o_pref_view viewWithTag: 2];
735
736         [o_prefs addObject: o_vlc_config];
737         [o_btn_apply setEnabled: YES];
738     }
739 }
740
741 - (void)clickedApplyCancelOK:(id)sender
742 {
743     id o_vlc_control;
744     NSEnumerator *o_enum;
745
746     NSWindow *o_pref_panel = [[sender superview] window];
747     NSString *o_module_name = [[o_pref_panel toolbar] identifier];
748     NSView *o_config_view = [sender superview];
749
750     if ( [[sender title] isEqualToString: _NS("OK")] )
751     {
752         NSWindow *o_config_panel = [o_config_view window];
753         NSString *o_module_name = [[o_config_panel toolbar] identifier];
754         NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];
755     
756         o_enum = [o_prefs objectEnumerator];
757         while( ( o_vlc_control = [o_enum nextObject] ) )
758         {
759             int i_type = [o_vlc_control configType];
760             NSString *o_name = [o_vlc_control configName];
761             char *psz_name = (char *)[o_name lossyCString];
762     
763             switch( i_type )
764             {
765     
766             case CONFIG_ITEM_MODULE:
767             case CONFIG_ITEM_STRING:
768             case CONFIG_ITEM_FILE:
769             case CONFIG_ITEM_DIRECTORY:
770                 {
771                     char *psz_value;
772                     NSString *o_value;
773     
774                     o_value = [o_vlc_control stringValue];
775                     psz_value = (char *)[o_value lossyCString];
776     
777                     config_PutPsz( p_intf, psz_name, psz_value );
778                 }
779                 break;
780     
781             case CONFIG_ITEM_INTEGER:
782             case CONFIG_ITEM_BOOL:
783                 {
784                     int i_value = [o_vlc_control intValue];
785
786                     config_PutInt( p_intf, psz_name, i_value );
787                 }
788                 break;
789     
790             case CONFIG_ITEM_FLOAT:
791                 {
792                     float f_value = [o_vlc_control floatValue];
793     
794                     config_PutFloat( p_intf, psz_name, f_value );
795                 }
796                 break;
797     
798             }
799         }
800     
801         config_SaveConfigFile( p_intf, NULL );
802     }
803     if( [[sender title] isEqualToString: _NS("Advanced")] )
804     {
805         NSButton *o_btn_apply = [o_config_view viewWithTag: 2];
806         b_advanced = !b_advanced;
807         [o_btn_apply setState: b_advanced];
808     }
809     [o_pref_panel close];
810
811     if( [self respondsToSelector: @selector(performSelectorOnMainThread:
812                                             withObject:waitUntilDone:)] )
813     {
814         [self performSelectorOnMainThread: @selector(destroyPrefPanel:)
815                                         withObject: o_module_name
816                                         waitUntilDone: YES];
817     }
818     else
819     {
820         [NSTimer scheduledTimerWithTimeInterval: 0.1
821                 target: self selector: @selector(destroyPrefPanel:)
822                 userInfo: o_module_name repeats: NO];
823     }
824     
825     if( [[sender title] isEqualToString: _NS("Advanced")] )
826     {
827         [self createPrefPanel: o_module_name];
828     }
829 }
830
831 @end
832
833 @implementation VLCPrefs (NSToolbarDelegate)
834
835 - (NSToolbarItem *)toolbar:(NSToolbar *)o_toolbar
836                    itemForItemIdentifier:(NSString *)o_item_id
837                    willBeInsertedIntoToolbar:(BOOL)b_flag
838 {
839     NSMutableDictionary *o_toolbar_items;
840     NSString *o_module_name = [o_toolbar identifier];
841
842     o_toolbar_items = [o_toolbars objectForKey: o_module_name];
843     if( o_toolbar_items == nil )
844     {
845         return( nil );
846     }
847
848     return( [o_toolbar_items objectForKey: o_item_id] );
849 }
850
851 - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)o_toolbar
852 {
853     return( [self toolbarDefaultItemIdentifiers: o_toolbar] );
854 }
855
856 - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)o_toolbar
857 {
858     NSArray *o_ids;
859     NSMutableDictionary *o_toolbar_items;
860     NSString *o_module_name = [o_toolbar identifier];
861
862     o_toolbar_items = [o_toolbars objectForKey: o_module_name];
863     if( o_toolbar_items == nil )
864     {
865         return( nil );
866     }
867
868     o_ids = [[o_toolbar_items allKeys]
869         sortedArrayUsingSelector: @selector(compare:)];
870
871     return( o_ids );
872 }
873
874 @end
875
876 @implementation VLCFlippedView
877
878 - (BOOL)isFlipped
879 {
880     return( YES );
881 }
882
883 @end
884
885 IMPL_CONTROL_CONFIG(Button);
886 IMPL_CONTROL_CONFIG(ComboBox);
887 IMPL_CONTROL_CONFIG(TextField);