]> git.sesse.net Git - vlc/blob - modules/gui/macosx/prefs.m
* modules/gui/macosx/prefs.?:
[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.18 2003/05/09 00:41:11 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                 [[NSNotificationCenter defaultCenter] addObserver: self
479                     selector: @selector(configChanged:)
480                     name: NSControlTextDidChangeNotification
481                     object: o_combo_box];
482                 [[NSNotificationCenter defaultCenter] addObserver: self
483                     selector: @selector(configChanged:)
484                     name: NSComboBoxSelectionDidChangeNotification
485                     object: o_combo_box];
486
487                 for( i=0; p_item->ppsz_list[i]; i++ )
488                 {
489                     [o_combo_box addItemWithObjectValue:
490                         [NSString stringWithCString: p_item->ppsz_list[i]]];
491                 }
492
493                 CONTROL_LABEL( p_item->psz_text );
494
495                 s_rc.origin.y += s_rc.size.height;
496                 s_rc.origin.x = X_ORIGIN;
497             }
498
499         }
500         break;
501
502         case CONFIG_ITEM_INTEGER:
503         {
504             INPUT_FIELD_INTEGER( p_item->psz_name, p_item->psz_text, 70,
505                                  p_item->i_value, p_item->psz_longtext );
506         }
507         break;
508
509         case CONFIG_ITEM_FLOAT:
510         {
511             INPUT_FIELD_FLOAT( p_item->psz_name, p_item->psz_text, 70,
512                                p_item->f_value, p_item->psz_longtext );
513         }
514         break;
515
516         case CONFIG_ITEM_BOOL:
517         {
518             VLCButton *o_btn_bool;
519             char * psz_duptip = NULL;
520             if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
521                 psz_duptip = strdup(p_item->psz_longtext);
522
523             s_rc.size.height = 27;
524             s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
525             s_rc.origin.y += 10;
526
527             CHECK_VIEW_HEIGHT;
528
529             o_btn_bool = [[VLCButton alloc] initWithFrame: s_rc];
530             [o_btn_bool setButtonType: NSSwitchButton];
531             [o_btn_bool setIntValue: p_item->i_value];
532             [o_btn_bool setTitle:
533                 [NSApp localizedString: p_item->psz_text]];
534             if ( psz_duptip != NULL )
535             {
536                 [o_btn_bool setToolTip: [NSApp localizedString:
537                                         vlc_wraptext(psz_duptip, PREFS_WRAP)]];
538                 free( psz_duptip );
539             }
540             [o_btn_bool setTarget: self];
541             [o_btn_bool setAction: @selector(configChanged:)];
542             CONTROL_CONFIG( o_btn_bool, o_module_name,
543                             CONFIG_ITEM_BOOL, p_item->psz_name );
544             [o_view addSubview: [o_btn_bool autorelease]];
545
546             s_rc.origin.y += s_rc.size.height;
547         }
548         break;
549
550         }
551
552 #undef INPUT_FIELD_INTEGER
553 #undef INPUT_FIELD_FLOAT
554 #undef INPUT_FIELD_STRING
555 #undef INPUT_FIELD
556 #undef CHECK_VIEW_HEIGHT
557 #undef CONTROL_LABEL
558 #undef Y_ORIGIN
559 #undef X_ORIGIN
560     }
561     while( p_item->i_type != CONFIG_HINT_END && p_item++ );
562
563     vlc_list_release( p_list );
564
565     [o_toolbars setObject: o_tb_items forKey: o_module_name];
566     [o_toolbar setDelegate: self];
567     [o_panel setToolbar: [o_toolbar autorelease]];
568
569 #define DEF_PANEL_BUTTON( tag, title, sel ) \
570     { \
571         o_button = [[NSButton alloc] initWithFrame: s_rc]; \
572         [o_button setButtonType: NSMomentaryPushInButton]; \
573         [o_button setBezelStyle: NSRoundedBezelStyle]; \
574         [o_button setAction: @selector(sel)]; \
575         [o_button setTarget: self]; \
576         [o_button setTitle: title]; \
577         [o_button setTag: tag]; \
578         [o_panel_view addSubview: [o_button autorelease]]; \
579     }
580
581     s_rc.origin.y = s_panel_rc.origin.y + 14;
582     s_rc.size.height = 25; s_rc.size.width = 105;
583     s_rc.origin.x = s_panel_rc.size.width - s_rc.size.width - 14;
584     DEF_PANEL_BUTTON( 0, _NS("OK"), clickedApplyCancelOK: );
585     [o_panel setDefaultButtonCell: [o_button cell]];
586
587     s_rc.origin.x -= s_rc.size.width;
588     DEF_PANEL_BUTTON( 1, _NS("Cancel"), clickedApplyCancelOK: );
589     [o_button setKeyEquivalent: @"\E"];
590
591     s_rc.origin.x = 20;
592     o_button = [[NSButton alloc] initWithFrame: s_rc];
593     [o_button setButtonType: NSSwitchButton];
594     [o_button setAction: @selector(clickedApplyCancelOK:)];
595     [o_button setTarget: self];
596     [o_button setTitle: _NS("Advanced")];
597     [o_button setTag: 2];
598     [o_panel_view addSubview: [o_button autorelease]];
599     [o_button setState: b_advanced];
600
601 #undef DEF_PANEL_BUTTON
602
603     [o_pref_panels setObject: o_panel forKey: o_module_name];
604     [o_panel_views setObject: o_views forKey: o_module_name];
605
606     [o_panel center];
607     [o_panel makeKeyAndOrderFront: nil];
608 }
609
610 - (void)destroyPrefPanel:(id)o_unknown
611 {
612     id v1;
613     NSPanel *o_panel;
614     NSEnumerator *o_e1;
615     NSMutableArray *o_prefs;
616     NSMutableDictionary *o_dic;
617     NSScrollView *o_scroll_view;
618     NSString *o_module_name;
619
620     o_module_name = (NSString *)([o_unknown isKindOfClass: [NSTimer class]] ?
621                                  [o_unknown userInfo] : o_unknown);
622
623 #define DIC_REL(dic) \
624     { \
625     o_dic = [dic objectForKey: o_module_name]; \
626     [dic removeObjectForKey: o_module_name]; \
627     o_e1 = [o_dic objectEnumerator]; \
628     while( (v1 = [o_e1 nextObject]) ) \
629     { \
630         [v1 release]; \
631     } \
632     [o_dic removeAllObjects]; \
633     [o_dic release]; \
634     }
635
636     o_panel = [o_pref_panels objectForKey: o_module_name];
637     [o_pref_panels removeObjectForKey: o_module_name];
638     [o_panel release];
639
640     DIC_REL(o_toolbars);
641
642     o_scroll_view = [o_scroll_views objectForKey: o_module_name];
643     [o_scroll_views removeObjectForKey: o_module_name];
644     [o_scroll_view release];
645
646     DIC_REL(o_panel_views);
647
648     o_prefs = [o_save_prefs objectForKey: o_module_name];
649     [o_save_prefs removeObjectForKey: o_module_name];
650     [o_prefs removeAllObjects];
651     [o_prefs release];
652
653 #undef DIC_REL
654
655 }
656
657 - (void)selectPrefView:(id)sender
658 {
659     NSView *o_view;
660     NSString *o_module_name;
661     NSScrollView *o_scroll_view;
662     NSMutableDictionary *o_views;
663
664     o_module_name = [[sender toolbar] identifier];
665     o_views = [o_panel_views objectForKey: o_module_name];
666     o_view = [o_views objectForKey: [sender label]];
667
668     o_scroll_view = [o_scroll_views objectForKey: o_module_name];
669     [o_scroll_view setDocumentView: o_view];
670 }
671
672 - (void)moduleSelected:(id)sender
673 {
674     NSButton *o_btn_config;
675     NSString *o_module_name;
676     BOOL b_has_prefs = NO;
677
678     o_module_name = [sender titleOfSelectedItem];
679     o_btn_config = [[sender superview] viewWithTag: [sender tag] + 1];
680
681     if( ![o_module_name isEqualToString: _NS("None")] )
682     {
683         b_has_prefs = [self hasPrefs: o_module_name];
684     }
685
686     [o_btn_config setEnabled: b_has_prefs];
687 }
688
689 - (void)configureModule:(id)sender
690 {
691     NSString *o_module_name;
692     NSPopUpButton *o_modules;
693
694     o_modules = [[sender superview] viewWithTag: [sender tag] - 1];
695     o_module_name = [o_modules titleOfSelectedItem];
696
697     [self createPrefPanel: o_module_name];
698 }
699
700 - (void)selectModule:(id)sender
701 {
702     NSString *o_module_name;
703     NSPopUpButton *o_modules;
704     NSTextField *o_module;
705
706     o_module = [[sender superview] viewWithTag: [sender tag] - 1];
707     o_modules = [[sender superview] viewWithTag: [sender tag] - 3];
708     o_module_name = [o_modules titleOfSelectedItem];
709
710     if( [o_module_name isEqualToString: _NS("None")] )
711     {
712         o_module_name = [NSString string];
713     }
714
715     [o_module setStringValue: o_module_name];
716     [self configChanged: o_module];
717 }
718
719 - (void)configChanged:(id)o_unknown
720 {
721     id o_vlc_config = [o_unknown isKindOfClass: [NSNotification class]] ?
722                       [o_unknown object] : o_unknown;
723
724     NSString *o_module_name = [o_vlc_config moduleName];
725     NSPanel *o_pref_panel = [o_pref_panels objectForKey: o_module_name];
726     NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];
727
728     if( [o_prefs indexOfObjectIdenticalTo: o_vlc_config] == NSNotFound )
729     {
730         NSView *o_pref_view = [o_pref_panel contentView];
731         NSButton *o_btn_apply = [o_pref_view viewWithTag: 2];
732
733         [o_prefs addObject: o_vlc_config];
734         [o_btn_apply setEnabled: YES];
735     }
736 }
737
738 - (void)clickedApplyCancelOK:(id)sender
739 {
740     id o_vlc_control;
741     NSEnumerator *o_enum;
742
743     NSWindow *o_pref_panel = [[sender superview] window];
744     NSString *o_module_name = [[o_pref_panel toolbar] identifier];
745     NSView *o_config_view = [sender superview];
746
747     if ( [[sender title] isEqualToString: _NS("OK")] )
748     {
749         NSWindow *o_config_panel = [o_config_view window];
750         NSString *o_module_name = [[o_config_panel toolbar] identifier];
751         NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];
752     
753         o_enum = [o_prefs objectEnumerator];
754         while( ( o_vlc_control = [o_enum nextObject] ) )
755         {
756             int i_type = [o_vlc_control configType];
757             NSString *o_name = [o_vlc_control configName];
758             char *psz_name = (char *)[o_name lossyCString];
759     
760             switch( i_type )
761             {
762     
763             case CONFIG_ITEM_MODULE:
764             case CONFIG_ITEM_STRING:
765             case CONFIG_ITEM_FILE:
766             case CONFIG_ITEM_DIRECTORY:
767                 {
768                     char *psz_value;
769                     NSString *o_value;
770     
771                     o_value = [o_vlc_control stringValue];
772                     psz_value = (char *)[o_value lossyCString];
773     
774                     config_PutPsz( p_intf, psz_name,
775                                    *psz_value ? psz_value : NULL );
776                 }
777                 break;
778     
779             case CONFIG_ITEM_INTEGER:
780             case CONFIG_ITEM_BOOL:
781                 {
782                     int i_value = [o_vlc_control intValue];
783
784                     config_PutInt( p_intf, psz_name, i_value );
785                 }
786                 break;
787     
788             case CONFIG_ITEM_FLOAT:
789                 {
790                     float f_value = [o_vlc_control floatValue];
791     
792                     config_PutFloat( p_intf, psz_name, f_value );
793                 }
794                 break;
795     
796             }
797         }
798     
799         [o_prefs removeAllObjects];
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);