prefs.m 27.8 KB
Newer Older
1
2
3
4
/*****************************************************************************
 * prefs.m: MacOS X plugin for vlc
 *****************************************************************************
 * Copyright (C) 2002 VideoLAN
5
 * $Id: prefs.m,v 1.14 2003/02/20 18:10:16 hartman Exp $
6
 *
7
 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8
9
10
11
12
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
13
 *
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
 *****************************************************************************/

/*****************************************************************************
 * Preamble
 *****************************************************************************/
#include <stdlib.h>                                      /* malloc(), free() */
#include <sys/param.h>                                    /* for MAXPATHLEN */
#include <string.h>

31
32
#include "intf.h"
#include "prefs.h"
33
34

/*****************************************************************************
35
 * VLCPrefs implementation
36
37
38
39
40
41
42
43
44
45
46
 *****************************************************************************/
@implementation VLCPrefs

- (id)init
{
    self = [super init];

    if( self != nil )
    {
        p_intf = [NSApp getIntf];

47
        o_pref_panels = [[NSMutableDictionary alloc] init];
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
        o_toolbars = [[NSMutableDictionary alloc] init];
        o_scroll_views = [[NSMutableDictionary alloc] init];
        o_panel_views = [[NSMutableDictionary alloc] init];
        o_save_prefs = [[NSMutableDictionary alloc] init];
    }

    return( self );
}

- (void)dealloc
{
    id v1, v2;
    NSEnumerator *o_e1;
    NSEnumerator *o_e2;

#define DIC_REL1(o_dic) \
    { \
    o_e1 = [o_dic objectEnumerator]; \
    while( (v1 = [o_e1 nextObject]) ) \
    { \
        [v1 release]; \
    } \
    [o_dic removeAllObjects]; \
    [o_dic release]; \
    }

#define DIC_REL2(o_dic) \
    { \
        o_e2 = [o_dic objectEnumerator]; \
        while( (v2 = [o_e2 nextObject]) ) \
        { \
            DIC_REL1(v2); \
        } \
        [o_dic removeAllObjects]; \
    }

    DIC_REL1(o_pref_panels);
    DIC_REL2(o_toolbars);
    DIC_REL1(o_scroll_views);
    DIC_REL2(o_panel_views);
    DIC_REL1(o_save_prefs);

#undef DIC_REL1
#undef DIC_REL2

    [super dealloc];
}

- (BOOL)hasPrefs:(NSString *)o_module_name
{
gbazin's avatar
   
gbazin committed
98
    module_t *p_parser;
99
    vlc_list_t *p_list;
100
    char *psz_module_name;
gbazin's avatar
   
gbazin committed
101
    int i_index;
102
103
104
105

    psz_module_name = (char *)[o_module_name lossyCString];

    /* look for module */
106
    p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
107

108
    for( i_index = 0; i_index < p_list->i_count; i_index++ )
109
    {
110
        p_parser = (module_t *)p_list->p_values[i_index].p_object ;
gbazin's avatar
   
gbazin committed
111
112

        if( !strcmp( p_parser->psz_object_name, psz_module_name ) )
113
        {
gbazin's avatar
   
gbazin committed
114
            BOOL b_has_prefs = p_parser->i_config_items != 0;
115
            vlc_list_release( p_list );
116
117
118
119
            return( b_has_prefs );
        }
    }

120
    vlc_list_release( p_list );
121
122
123
124
125
126
127
128
129

    return( NO );
}

- (void)createPrefPanel:(NSString *)o_module_name
{
    int i_pos;
    int i_module_tag;

130
    module_t *p_parser = NULL;
131
    vlc_list_t *p_list;
132
133
    module_config_t *p_item;
    char *psz_module_name;
gbazin's avatar
   
gbazin committed
134
    int i_index;
135
136
137
138
139
140

    NSPanel *o_panel;                   /* panel                        */
    NSRect s_panel_rc;                  /* panel rect                   */
    NSView *o_panel_view;               /* panel view                   */
    NSToolbar *o_toolbar;               /* panel toolbar                */
    NSMutableDictionary *o_tb_items;    /* panel toolbar items          */
141
    NSScrollView *o_scroll_view;        /* panel scroll view            */
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    NSRect s_scroll_rc;                 /* panel scroll view rect       */
    NSMutableDictionary *o_views;       /* panel scroll view docviews   */

    NSRect s_rc;                        /* rect                         */
    NSView *o_view;                     /* view                         */
    NSRect s_vrc;                       /* view rect                    */
    NSButton *o_button;                 /* button                       */
    NSRect s_brc;                       /* button rect                  */
    VLCTextField *o_text_field;         /* input field / label          */

    o_panel = [o_pref_panels objectForKey: o_module_name];
    if( o_panel != nil )
    {
        [o_panel center];
        [o_panel makeKeyAndOrderFront: nil];
        return;
158
    }
159
160
161
162

    psz_module_name = (char *)[o_module_name lossyCString];

    /* Look for the selected module */
163
    p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
164

165
    for( i_index = 0; i_index < p_list->i_count; i_index++ )
166
    {
167
        p_parser = (module_t *)p_list->p_values[i_index].p_object ;
gbazin's avatar
   
gbazin committed
168

169
        if( psz_module_name
gbazin's avatar
   
gbazin committed
170
            && !strcmp( psz_module_name, p_parser->psz_object_name ) )
171
172
173
174
175
        {
            break;
        }
    }

176
    if( !p_parser || i_index == p_list->i_count )
177
    {
178
        vlc_list_release( p_list );
179
180
181
182
183
        return;
    }

    /* We found it, now we can start building its configuration interface */

184
    s_panel_rc = NSMakeRect( 0, 0, 450, 450 );
185
186
187
188
189
    o_panel = [[NSPanel alloc] initWithContentRect: s_panel_rc
                               styleMask: NSTitledWindowMask
                               backing: NSBackingStoreBuffered
                               defer: YES];
    o_toolbar = [[NSToolbar alloc] initWithIdentifier: o_module_name];
190
    [o_panel setTitle: [NSString stringWithFormat: @"%@ (%@)",
191
192
193
                                 _NS("Preferences"), o_module_name]];
    o_panel_view = [o_panel contentView];

194
    s_scroll_rc = s_panel_rc;
195
196
197
    s_scroll_rc.size.height -= 55; s_scroll_rc.origin.y += 55;
    o_scroll_view = [[NSScrollView alloc] initWithFrame: s_scroll_rc];
    [o_scroll_views setObject: o_scroll_view forKey: o_module_name];
198
    [o_scroll_view setBorderType: NSGrooveBorder];
199
200
201
202
203
204
205
206
207
208
209
210
    [o_scroll_view setHasVerticalScroller: YES];
    [o_scroll_view setDrawsBackground: NO];
    [o_scroll_view setRulersVisible: YES];
    [o_panel_view addSubview: o_scroll_view];

    o_tb_items = [[NSMutableDictionary alloc] init];
    o_views = [[NSMutableDictionary alloc] init];

    [o_save_prefs setObject: [[NSMutableArray alloc] init]
                  forKey: o_module_name];

    /* Enumerate config options and add corresponding config boxes */
gbazin's avatar
   
gbazin committed
211
    p_item = p_parser->p_config;
212
213
214
215
216
217

    i_pos = 0;
    o_view = nil;
    i_module_tag = 3;

#define X_ORIGIN 20
218
#define Y_ORIGIN (X_ORIGIN - 10)
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239

#define CHECK_VIEW_HEIGHT \
    { \
        float f_new_pos = s_rc.origin.y + s_rc.size.height + X_ORIGIN; \
        if( f_new_pos > s_vrc.size.height ) \
        { \
            s_vrc.size.height = f_new_pos; \
            [o_view setFrame: s_vrc]; \
        } \
    }

#define CONTROL_LABEL( label ) \
    { \
        s_rc.origin.x += s_rc.size.width + 10; \
        s_rc.size.width = s_vrc.size.width - s_rc.origin.x - X_ORIGIN - 20; \
        o_text_field = [[NSTextField alloc] initWithFrame: s_rc]; \
        [o_text_field setDrawsBackground: NO]; \
        [o_text_field setBordered: NO]; \
        [o_text_field setEditable: NO]; \
        [o_text_field setSelectable: NO]; \
        [o_text_field setStringValue: \
Christophe Massiot's avatar
Christophe Massiot committed
240
            [NSApp localizedString: label]]; \
241
        [o_text_field sizeToFit]; \
242
243
244
        [o_view addSubview: [o_text_field autorelease]]; \
    }

Christophe Massiot's avatar
Christophe Massiot committed
245
#define INPUT_FIELD( ctype, cname, label, w, msg, param, tip ) \
246
    { \
247
        char * psz_duptip = NULL; \
248
        if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding ) \
249
            psz_duptip = strdup(p_item->psz_longtext); \
Christophe Massiot's avatar
Christophe Massiot committed
250
        s_rc.size.height = 25; \
251
252
253
254
255
256
257
        s_rc.size.width = w; \
        s_rc.origin.y += 10; \
        CHECK_VIEW_HEIGHT; \
        o_text_field = [[VLCTextField alloc] initWithFrame: s_rc]; \
        [o_text_field setAlignment: NSRightTextAlignment]; \
        CONTROL_CONFIG( o_text_field, o_module_name, ctype, cname ); \
        [o_text_field msg: param]; \
258
259
260
261
262
263
        if ( psz_duptip != NULL ) \
        { \
            [o_text_field setToolTip: [NSApp localizedString: \
                                       vlc_wraptext(psz_duptip, PREFS_WRAP)]]; \
            free(psz_duptip);\
        } \
264
265
266
267
268
269
270
271
272
273
        [o_view addSubview: [o_text_field autorelease]]; \
        [[NSNotificationCenter defaultCenter] addObserver: self \
            selector: @selector(configChanged:) \
            name: NSControlTextDidChangeNotification \
            object: o_text_field]; \
        CONTROL_LABEL( label ); \
        s_rc.origin.y += s_rc.size.height; \
        s_rc.origin.x = X_ORIGIN; \
    }

Christophe Massiot's avatar
Christophe Massiot committed
274
275
276
277
278
279
#define INPUT_FIELD_INTEGER( name, label, w, param, tip ) \
    INPUT_FIELD( CONFIG_ITEM_INTEGER, name, label, w, setIntValue, param, tip )
#define INPUT_FIELD_FLOAT( name, label, w, param, tip ) \
    INPUT_FIELD( CONFIG_ITEM_FLOAT, name, label, w, setFloatValue, param, tip )
#define INPUT_FIELD_STRING( name, label, w, param, tip ) \
    INPUT_FIELD( CONFIG_ITEM_STRING, name, label, w, setStringValue, param, tip )
280
281
282

    if( p_item ) do
    {
283
284
285
286
        if( p_item->b_advanced && !config_GetInt( p_intf, "advanced" ))
        {
            continue;
        }
287
288
289
290
291
292
293
294
295
        switch( p_item->i_type )
        {

        case CONFIG_HINT_CATEGORY:
        {
            NSString *o_key;
            NSString *o_label;
            NSToolbarItem *o_tbi;

Christophe Massiot's avatar
Christophe Massiot committed
296
            o_label = [NSApp localizedString: p_item->psz_text];
297
            o_tbi = [[NSToolbarItem alloc] initWithItemIdentifier: o_label];
298
299
300
301
302
303
            [o_tbi setImage: [NSImage imageNamed: @"NSApplicationIcon"]];
            [o_tbi setLabel: o_label];
            [o_tbi setTarget: self];
            [o_tbi setAction: @selector(selectPrefView:)];

            o_key = [NSString stringWithFormat: @"%02d %@",
304
                                                i_pos, o_label];
305
306
307
308
309
310
311
312
313
314
315
316
317
            [o_tb_items setObject: o_tbi forKey: o_key];

            s_vrc = s_scroll_rc; s_vrc.size.height -= 4;
            o_view = [[VLCFlippedView alloc] initWithFrame: s_vrc];
            [o_views setObject: o_view forKey: o_label];

            s_rc.origin.x = X_ORIGIN;
            s_rc.origin.y = Y_ORIGIN;

            i_module_tag = 3;

            if( i_pos == 0 )
            {
318
                [o_scroll_view setDocumentView: o_view];
319
320
321
322
323
324
325
326
327
328
329
330
331
332
            }

            i_pos++;
        }
        break;

        case CONFIG_ITEM_MODULE:
        {
            NSBox *o_box;
            NSRect s_crc;
            NSView *o_cview;
            NSPopUpButton *o_modules;
            NSButton *o_btn_select;
            NSButton *o_btn_configure;
333
            char * psz_duptip = NULL;
334
            if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
335
                psz_duptip = strdup(p_item->psz_longtext);
336
337
338

#define MODULE_BUTTON( button, title, sel ) \
    { \
Christophe Massiot's avatar
Christophe Massiot committed
339
        s_brc.size.height = 32; \
340
341
342
343
344
345
346
347
348
349
350
351
        s_brc.origin.x += s_brc.size.width + 10; \
        s_brc.size.width = s_crc.size.width - s_brc.origin.x - 10; \
        button = [[NSButton alloc] initWithFrame: s_brc]; \
        [button setButtonType: NSMomentaryPushInButton]; \
        [button setBezelStyle: NSRoundedBezelStyle]; \
        [button setTitle: title]; \
        [button setTag: i_module_tag++]; \
        [button setTarget: self]; \
        [button setAction: @selector(sel)]; \
        [o_cview addSubview: [button autorelease]]; \
    }

Christophe Massiot's avatar
Christophe Massiot committed
352
            s_rc.size.height = 107;
353
354
355
356
357
358
            s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
            s_rc.origin.y += i_module_tag == 3 ? Y_ORIGIN : 20;

            CHECK_VIEW_HEIGHT;

            o_box = [[NSBox alloc] initWithFrame: s_rc];
Christophe Massiot's avatar
Christophe Massiot committed
359
            [o_box setTitle: [NSApp localizedString: p_item->psz_text]];
360
361
362
363
364
365
            [o_view addSubview: [o_box autorelease]];
            s_rc.origin.y += s_rc.size.height + 10;
            o_cview = [[VLCFlippedView alloc] initWithFrame: s_rc];
            [o_box setContentView: [o_cview autorelease]];
            s_crc = [o_cview bounds];

366
            s_brc = NSMakeRect( 5, 10, 200, 30 );
367
368
369
370
            o_modules = [[NSPopUpButton alloc] initWithFrame: s_brc];
            [o_modules setTag: i_module_tag++];
            [o_modules setTarget: self];
            [o_modules setAction: @selector(moduleSelected:)];
371
372
373
374
375
376
            if ( psz_duptip != NULL )
            {
                [o_modules setToolTip: [NSApp localizedString:
                                        vlc_wraptext(psz_duptip, PREFS_WRAP)]];
                free( psz_duptip );
            }
377
            [o_cview addSubview: [o_modules autorelease]];
378

379
            MODULE_BUTTON( o_btn_configure, _NS("Configure"),
380
381
                           configureModule: );

382
383
            s_brc = NSMakeRect( 8, s_brc.origin.y + s_brc.size.height + 10,
                                194, 25 );
384
385
386
            o_text_field = [[VLCTextField alloc] initWithFrame: s_brc];
            [o_text_field setTag: i_module_tag++];
            [o_text_field setAlignment: NSLeftTextAlignment];
387
            CONTROL_CONFIG( o_text_field, o_module_name,
388
389
390
391
392
393
394
395
                            CONFIG_ITEM_MODULE, p_item->psz_name );
            [[NSNotificationCenter defaultCenter] addObserver: self
                selector: @selector(configChanged:)
                name: NSControlTextDidChangeNotification
                object: o_text_field];
            [o_cview addSubview: [o_text_field autorelease]];

            s_brc.origin.x += 3;
396
            MODULE_BUTTON( o_btn_select, _NS("Select"),
397
398
399
400
401
402
                           selectModule: );

            [o_modules addItemWithTitle: _NS("None")];

            /* build a list of available modules */
            {
403
                for( i_index = 0; i_index < p_list->i_count; i_index++ )
404
                {
405
                    p_parser = (module_t *)p_list->p_values[i_index].p_object ;
gbazin's avatar
   
gbazin committed
406
407

                    if( !strcmp( p_parser->psz_capability,
408
409
                                 p_item->psz_type ) )
                    {
410
                        NSString *o_object_name = [NSString
gbazin's avatar
   
gbazin committed
411
                            stringWithCString: p_parser->psz_object_name];
412
413
414
415
416
417
418
419
420
421
                        [o_modules addItemWithTitle: o_object_name];
                    }
                }
            }

            if( p_item->psz_value != NULL )
            {
                NSString *o_value =
                    [NSString stringWithCString: p_item->psz_value];

422
423
424
                [o_text_field setStringValue: o_value];
                [o_modules selectItemWithTitle: o_value];
                [o_btn_configure setEnabled: [self hasPrefs: o_value]];
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
            }
            else
            {
                [o_modules selectItemWithTitle: _NS("None")];
                [o_btn_configure setEnabled: NO];
            }

#undef MODULE_BUTTON
        }
        break;

        case CONFIG_ITEM_STRING:
        case CONFIG_ITEM_FILE:
        {

            if( !p_item->ppsz_list )
            {
                char *psz_value = p_item->psz_value ?
                                  p_item->psz_value : "";

                INPUT_FIELD_STRING( p_item->psz_name, p_item->psz_text, 150,
Christophe Massiot's avatar
Christophe Massiot committed
446
447
                                    [NSString stringWithCString: psz_value],
                                    p_item->psz_longtext );
448
449
450
451
452
            }
            else
            {
                int i;
                VLCComboBox *o_combo_box;
453
                char * psz_duptip = NULL;
454
                if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
455
                    psz_duptip = strdup(p_item->psz_longtext);
456

Christophe Massiot's avatar
Christophe Massiot committed
457
                s_rc.size.height = 27;
458
459
460
461
462
463
                s_rc.size.width = 150;
                s_rc.origin.y += 10;

                CHECK_VIEW_HEIGHT;

                o_combo_box = [[VLCComboBox alloc] initWithFrame: s_rc];
464
                CONTROL_CONFIG( o_combo_box, o_module_name,
465
                                CONFIG_ITEM_STRING, p_item->psz_name );
466
467
468
469
470
471
                if ( psz_duptip != NULL )
                {
                    [o_combo_box setToolTip: [NSApp localizedString:
                                        vlc_wraptext(psz_duptip, PREFS_WRAP)]];
                    free( psz_duptip );
                }
472
473
474
475
476
477
478
                [o_view addSubview: [o_combo_box autorelease]];
                [[NSNotificationCenter defaultCenter] addObserver: self
                    selector: @selector(configChanged:)
                    name: NSControlTextDidChangeNotification
                    object: o_combo_box];
                [[NSNotificationCenter defaultCenter] addObserver: self
                    selector: @selector(configChanged:)
479
                    name: NSComboBoxSelectionDidChangeNotification
480
481
482
483
484
                    object: o_combo_box];

                for( i=0; p_item->ppsz_list[i]; i++ )
                {
                    [o_combo_box addItemWithObjectValue:
485
                        [NSString stringWithCString: p_item->ppsz_list[i]]];
486
487
                }

488
                CONTROL_LABEL( p_item->psz_text );
489
490
491
492
493
494
495
496
497
498

                s_rc.origin.y += s_rc.size.height;
                s_rc.origin.x = X_ORIGIN;
            }

        }
        break;

        case CONFIG_ITEM_INTEGER:
        {
499
            INPUT_FIELD_INTEGER( p_item->psz_name, p_item->psz_text, 70,
Christophe Massiot's avatar
Christophe Massiot committed
500
                                 p_item->i_value, p_item->psz_longtext );
501
502
503
504
505
506
        }
        break;

        case CONFIG_ITEM_FLOAT:
        {
            INPUT_FIELD_FLOAT( p_item->psz_name, p_item->psz_text, 70,
Christophe Massiot's avatar
Christophe Massiot committed
507
                               p_item->f_value, p_item->psz_longtext );
508
509
510
511
512
513
        }
        break;

        case CONFIG_ITEM_BOOL:
        {
            VLCButton *o_btn_bool;
514
            char * psz_duptip = NULL;
515
            if ( p_item->psz_longtext != NULL && [NSApp getEncoding] == NSISOLatin1StringEncoding )
516
                psz_duptip = strdup(p_item->psz_longtext);
517

Christophe Massiot's avatar
Christophe Massiot committed
518
            s_rc.size.height = 27;
519
520
521
522
523
524
525
526
            s_rc.size.width = s_vrc.size.width - X_ORIGIN * 2 - 20;
            s_rc.origin.y += 10;

            CHECK_VIEW_HEIGHT;

            o_btn_bool = [[VLCButton alloc] initWithFrame: s_rc];
            [o_btn_bool setButtonType: NSSwitchButton];
            [o_btn_bool setIntValue: p_item->i_value];
527
            [o_btn_bool setTitle:
Christophe Massiot's avatar
Christophe Massiot committed
528
                [NSApp localizedString: p_item->psz_text]];
529
530
531
532
533
534
            if ( psz_duptip != NULL )
            {
                [o_btn_bool setToolTip: [NSApp localizedString:
                                        vlc_wraptext(psz_duptip, PREFS_WRAP)]];
                free( psz_duptip );
            }
535
536
            [o_btn_bool setTarget: self];
            [o_btn_bool setAction: @selector(configChanged:)];
537
538
            CONTROL_CONFIG( o_btn_bool, o_module_name,
                            CONFIG_ITEM_BOOL, p_item->psz_name );
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
            [o_view addSubview: [o_btn_bool autorelease]];

            s_rc.origin.y += s_rc.size.height;
        }
        break;

        }

#undef INPUT_FIELD_INTEGER
#undef INPUT_FIELD_FLOAT
#undef INPUT_FIELD_STRING
#undef INPUT_FIELD
#undef CHECK_VIEW_HEIGHT
#undef CONTROL_LABEL
#undef Y_ORIGIN
#undef X_ORIGIN
    }
    while( p_item->i_type != CONFIG_HINT_END && p_item++ );

558
    vlc_list_release( p_list );
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576

    [o_toolbars setObject: o_tb_items forKey: o_module_name];
    [o_toolbar setDelegate: self];
    [o_panel setToolbar: [o_toolbar autorelease]];

#define DEF_PANEL_BUTTON( tag, title, sel ) \
    { \
        o_button = [[NSButton alloc] initWithFrame: s_rc]; \
        [o_button setButtonType: NSMomentaryPushInButton]; \
        [o_button setBezelStyle: NSRoundedBezelStyle]; \
        [o_button setAction: @selector(sel)]; \
        [o_button setTarget: self]; \
        [o_button setTitle: title]; \
        [o_button setTag: tag]; \
        [o_panel_view addSubview: [o_button autorelease]]; \
    }

    s_rc.origin.y = s_panel_rc.origin.y + 14;
577
    s_rc.size.height = 25; s_rc.size.width = 105;
578
    s_rc.origin.x = s_panel_rc.size.width - s_rc.size.width - 14;
579
    DEF_PANEL_BUTTON( 0, _NS("OK"), clickedCancelOK: );
580
581
582
583
    [o_panel setDefaultButtonCell: [o_button cell]];

    s_rc.origin.x -= s_rc.size.width;
    DEF_PANEL_BUTTON( 1, _NS("Cancel"), clickedCancelOK: );
584
    [o_button setKeyEquivalent: @"\E"];
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657

    s_rc.origin.x -= s_rc.size.width;
    DEF_PANEL_BUTTON( 2, _NS("Apply"), clickedApply: );
    [o_button setEnabled: NO];

#undef DEF_PANEL_BUTTON

    [o_pref_panels setObject: o_panel forKey: o_module_name];
    [o_panel_views setObject: o_views forKey: o_module_name];

    [o_panel center];
    [o_panel makeKeyAndOrderFront: nil];
}

- (void)destroyPrefPanel:(id)o_unknown
{
    id v1;
    NSPanel *o_panel;
    NSEnumerator *o_e1;
    NSMutableArray *o_prefs;
    NSMutableDictionary *o_dic;
    NSScrollView *o_scroll_view;
    NSString *o_module_name;

    o_module_name = (NSString *)([o_unknown isKindOfClass: [NSTimer class]] ?
                                 [o_unknown userInfo] : o_unknown);

#define DIC_REL(dic) \
    { \
    o_dic = [dic objectForKey: o_module_name]; \
    [dic removeObjectForKey: o_module_name]; \
    o_e1 = [o_dic objectEnumerator]; \
    while( (v1 = [o_e1 nextObject]) ) \
    { \
        [v1 release]; \
    } \
    [o_dic removeAllObjects]; \
    [o_dic release]; \
    }

    o_panel = [o_pref_panels objectForKey: o_module_name];
    [o_pref_panels removeObjectForKey: o_module_name];
    [o_panel release];

    DIC_REL(o_toolbars);

    o_scroll_view = [o_scroll_views objectForKey: o_module_name];
    [o_scroll_views removeObjectForKey: o_module_name];
    [o_scroll_view release];

    DIC_REL(o_panel_views);

    o_prefs = [o_save_prefs objectForKey: o_module_name];
    [o_save_prefs removeObjectForKey: o_module_name];
    [o_prefs removeAllObjects];
    [o_prefs release];

#undef DIC_REL

}

- (void)selectPrefView:(id)sender
{
    NSView *o_view;
    NSString *o_module_name;
    NSScrollView *o_scroll_view;
    NSMutableDictionary *o_views;

    o_module_name = [[sender toolbar] identifier];
    o_views = [o_panel_views objectForKey: o_module_name];
    o_view = [o_views objectForKey: [sender label]];

    o_scroll_view = [o_scroll_views objectForKey: o_module_name];
658
    [o_scroll_view setDocumentView: o_view];
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
}

- (void)moduleSelected:(id)sender
{
    NSButton *o_btn_config;
    NSString *o_module_name;
    BOOL b_has_prefs = NO;

    o_module_name = [sender titleOfSelectedItem];
    o_btn_config = [[sender superview] viewWithTag: [sender tag] + 1];

    if( ![o_module_name isEqualToString: _NS("None")] )
    {
        b_has_prefs = [self hasPrefs: o_module_name];
    }

    [o_btn_config setEnabled: b_has_prefs];
}

- (void)configureModule:(id)sender
{
    NSString *o_module_name;
    NSPopUpButton *o_modules;

683
    o_modules = [[sender superview] viewWithTag: [sender tag] - 1];
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
    o_module_name = [o_modules titleOfSelectedItem];

    [self createPrefPanel: o_module_name];
}

- (void)selectModule:(id)sender
{
    NSString *o_module_name;
    NSPopUpButton *o_modules;
    NSTextField *o_module;

    o_module = [[sender superview] viewWithTag: [sender tag] - 1];
    o_modules = [[sender superview] viewWithTag: [sender tag] - 3];
    o_module_name = [o_modules titleOfSelectedItem];

    if( [o_module_name isEqualToString: _NS("None")] )
    {
        o_module_name = [NSString string];
    }

    [o_module setStringValue: o_module_name];
    [self configChanged: o_module];
}

- (void)configChanged:(id)o_unknown
{
    id o_vlc_config = [o_unknown isKindOfClass: [NSNotification class]] ?
                      [o_unknown object] : o_unknown;

713
714
    NSString *o_module_name = [o_vlc_config moduleName];
    NSPanel *o_pref_panel = [o_pref_panels objectForKey: o_module_name];
715
716
717
718
719
    NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];

    if( [o_prefs indexOfObjectIdenticalTo: o_vlc_config] == NSNotFound )
    {
        NSView *o_pref_view = [o_pref_panel contentView];
720
        NSButton *o_btn_apply = [o_pref_view viewWithTag: 2];
721
722
723
724
725
726
727
728
729
730
731
732

        [o_prefs addObject: o_vlc_config];
        [o_btn_apply setEnabled: YES];
    }
}

- (void)clickedApply:(id)sender
{
    id o_vlc_control;
    NSEnumerator *o_enum;

    NSView *o_config_view = [sender superview];
733
734
    NSWindow *o_config_panel = [o_config_view window];
    NSButton *o_btn_apply = [o_config_view viewWithTag: 2];
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
    NSString *o_module_name = [[o_config_panel toolbar] identifier];
    NSMutableArray *o_prefs = [o_save_prefs objectForKey: o_module_name];

    o_enum = [o_prefs objectEnumerator];
    while( ( o_vlc_control = [o_enum nextObject] ) )
    {
        int i_type = [o_vlc_control configType];
        NSString *o_name = [o_vlc_control configName];
        char *psz_name = (char *)[o_name lossyCString];

        switch( i_type )
        {

        case CONFIG_ITEM_MODULE:
        case CONFIG_ITEM_STRING:
        case CONFIG_ITEM_FILE:
            {
                char *psz_value;
                NSString *o_value;

                o_value = [o_vlc_control stringValue];
                psz_value = (char *)[o_value lossyCString];

758
                config_PutPsz( p_intf, psz_name,
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
                               *psz_value ? psz_value : NULL );
            }
            break;

        case CONFIG_ITEM_INTEGER:
        case CONFIG_ITEM_BOOL:
            {
                int i_value = [o_vlc_control intValue];

                config_PutInt( p_intf, psz_name, i_value );
            }
            break;

        case CONFIG_ITEM_FLOAT:
            {
                float f_value = [o_vlc_control floatValue];

                config_PutFloat( p_intf, psz_name, f_value );
            }
            break;

        }
    }

    [o_btn_apply setEnabled: NO];
    [o_prefs removeAllObjects];

    config_SaveConfigFile( p_intf, NULL );
}

- (void)clickedCancelOK:(id)sender
{
    NSWindow *o_pref_panel = [[sender superview] window];
    NSString *o_module_name = [[o_pref_panel toolbar] identifier];

    if( [[sender title] isEqualToString: _NS("OK")] )
    {
        [self clickedApply: sender];
    }

    [o_pref_panel close];

    if( [self respondsToSelector: @selector(performSelectorOnMainThread:
802
                                            withObject:waitUntilDone:)] )
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
    {
        [self performSelectorOnMainThread: @selector(destroyPrefPanel:)
                                           withObject: o_module_name
                                           waitUntilDone: NO];
    }
    else
    {
        [NSTimer scheduledTimerWithTimeInterval: 0.1
                 target: self selector: @selector(destroyPrefPanel:)
                 userInfo: o_module_name repeats: NO];
    }
}

@end

@implementation VLCPrefs (NSToolbarDelegate)

820
821
- (NSToolbarItem *)toolbar:(NSToolbar *)o_toolbar
                   itemForItemIdentifier:(NSString *)o_item_id
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
                   willBeInsertedIntoToolbar:(BOOL)b_flag
{
    NSMutableDictionary *o_toolbar_items;
    NSString *o_module_name = [o_toolbar identifier];

    o_toolbar_items = [o_toolbars objectForKey: o_module_name];
    if( o_toolbar_items == nil )
    {
        return( nil );
    }

    return( [o_toolbar_items objectForKey: o_item_id] );
}

- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)o_toolbar
{
    return( [self toolbarDefaultItemIdentifiers: o_toolbar] );
}

- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)o_toolbar
{
    NSArray *o_ids;
    NSMutableDictionary *o_toolbar_items;
    NSString *o_module_name = [o_toolbar identifier];

    o_toolbar_items = [o_toolbars objectForKey: o_module_name];
    if( o_toolbar_items == nil )
    {
        return( nil );
851
    }
852

853
    o_ids = [[o_toolbar_items allKeys]
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
        sortedArrayUsingSelector: @selector(compare:)];

    return( o_ids );
}

@end

@implementation VLCFlippedView

- (BOOL)isFlipped
{
    return( YES );
}

@end

IMPL_CONTROL_CONFIG(Button);
IMPL_CONTROL_CONFIG(ComboBox);
IMPL_CONTROL_CONFIG(TextField);