]> git.sesse.net Git - vlc/blob - modules/gui/macosx/prefs.m
Removed the 'Advanced' function from the complete prefs because we want them to be...
[vlc] / modules / gui / macosx / prefs.m
1 /*****************************************************************************
2  * prefs.m: MacOS X module for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2006 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8  *          Derk-Jan Hartman <hartman at videolan dot org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /* VLCPrefs manages the main preferences dialog
26    the class is related to wxwindows intf, PrefsPanel */
27 /* VLCTreeItem should contain:
28    - the children of the treeitem
29    - the associated prefs widgets
30    - the documentview with all the prefs widgets in it
31    - a saveChanges action
32    - a revertChanges action
33    - a redraw view action
34    - the children action should generate a list of the treeitems children (to be used by VLCPrefs datasource)
35
36    The class is sort of a mix of wxwindows intfs, PrefsTreeCtrl and ConfigTreeData
37 */
38 /* VLCConfigControl are subclassed NSView's containing and managing individual config items
39    the classes are VERY closely related to wxwindows ConfigControls */
40
41 /*****************************************************************************
42  * Preamble
43  *****************************************************************************/
44 #include <stdlib.h>                                      /* malloc(), free() */
45 #include <sys/param.h>                                    /* for MAXPATHLEN */
46 #include <string.h>
47
48 #ifdef HAVE_CONFIG_H
49 # include "config.h"
50 #endif
51
52 #include <vlc_common.h>
53 #include <vlc_config_cat.h>
54
55 #import "intf.h"
56 #import "prefs.h"
57 #import "simple_prefs.h"
58 #import "prefs_widgets.h"
59 #import "vlc_keys.h"
60
61 /* /!\ Warning: Unreadable code :/ */
62
63 /*****************************************************************************
64  * VLCPrefs implementation
65  *****************************************************************************/
66 @implementation VLCPrefs
67
68 static VLCPrefs *_o_sharedMainInstance = nil;
69
70 + (VLCPrefs *)sharedInstance
71 {
72     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
73 }
74
75 - (id)init
76 {
77     if( _o_sharedMainInstance ) {
78         [self dealloc];
79     }
80     else
81     {
82         _o_sharedMainInstance = [super init];
83         p_intf = VLCIntf;
84         o_empty_view = [[NSView alloc] init];
85     }
86
87     return _o_sharedMainInstance;
88 }
89
90 - (void)dealloc
91 {
92     [o_empty_view release];
93     [super dealloc];
94 }
95
96 - (void)awakeFromNib
97 {
98     p_intf = VLCIntf;
99
100     [self initStrings];
101     [o_prefs_view setBorderType: NSGrooveBorder];
102     [o_prefs_view setHasVerticalScroller: YES];
103     [o_prefs_view setDrawsBackground: NO];
104     [o_prefs_view setDocumentView: o_empty_view];
105     [o_tree selectRow:0 byExtendingSelection:NO];
106 }
107
108 - (void)setTitle: (NSString *) o_title_name
109 {
110     [o_title setStringValue: o_title_name];
111 }
112
113 - (void)showPrefs
114 {
115     [[o_basicFull_matrix cellAtRow:0 column:0] setState: NSOffState];
116     [[o_basicFull_matrix cellAtRow:0 column:1] setState: NSOnState];
117     
118     [o_prefs_window center];
119     [o_prefs_window makeKeyAndOrderFront:self];
120 }
121
122 - (void)initStrings
123 {
124     [o_prefs_window setTitle: _NS("Preferences")];
125     [o_save_btn setTitle: _NS("Save")];
126     [o_cancel_btn setTitle: _NS("Cancel")];
127     [o_reset_btn setTitle: _NS("Reset All")];
128     [[o_basicFull_matrix cellAtRow: 0 column: 0] setStringValue: _NS("Basic")];
129     [[o_basicFull_matrix cellAtRow: 0 column: 1] setStringValue: _NS("All")];
130 }
131
132 - (IBAction)savePrefs: (id)sender
133 {
134     /* TODO: call savePrefs on Root item */
135     [[VLCTreeItem rootItem] applyChanges];
136     config_SaveConfigFile( p_intf, NULL );
137     [o_prefs_window orderOut:self];
138 }
139
140 - (IBAction)closePrefs: (id)sender
141 {
142     [o_prefs_window orderOut:self];
143 }
144
145 - (IBAction)resetAll: (id)sender
146 {
147     NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"),
148         _NS("Continue"), nil, o_prefs_window, self,
149         @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil,
150         _NS("Beware this will reset the VLC media player preferences.\n"
151             "Are you sure you want to continue?") );
152 }
153
154 - (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return
155     contextInfo:(void *)o_context
156 {
157     if( i_return == NSAlertAlternateReturn )
158     {
159         [o_prefs_view setDocumentView: o_empty_view];
160         config_ResetAll( p_intf );
161         [[VLCTreeItem rootItem] resetView];
162         [[o_tree itemAtRow:[o_tree selectedRow]]
163             showView:o_prefs_view];
164     }
165 }
166
167 - (IBAction)buttonAction: (id)sender
168 {
169     [o_prefs_window orderOut: self];
170     [[o_basicFull_matrix cellAtRow:0 column:0] setState: NSOnState];
171     [[o_basicFull_matrix cellAtRow:0 column:1] setState: NSOffState];
172     [[[VLCMain sharedInstance] getSimplePreferences] showSimplePrefs];
173 }
174
175 - (void)loadConfigTree
176 {
177 }
178
179 - (void)outlineViewSelectionIsChanging:(NSNotification *)o_notification
180 {
181 }
182
183 /* update the document view to the view of the selected tree item */
184 - (void)outlineViewSelectionDidChange:(NSNotification *)o_notification
185 {
186     [[o_tree itemAtRow:[o_tree selectedRow]] showView: o_prefs_view];
187 }
188
189 @end
190
191 @implementation VLCPrefs (NSTableDataSource)
192
193 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
194     return (item == nil) ? [[VLCTreeItem rootItem] numberOfChildren] :
195                             [item numberOfChildren];
196 }
197
198 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
199 {
200     return (item == nil) ? YES : ( ([item numberOfChildren] != -1) &&
201                                    ([item numberOfChildren] != 0));
202 }
203
204 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
205     return (item == nil) ? [[VLCTreeItem rootItem] childAtIndex:index] :
206                             (id)[item childAtIndex:index];
207 }
208
209 - (id)outlineView:(NSOutlineView *)outlineView
210     objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
211 {
212     return (item == nil) ? @"" : (id)[item getName];
213 }
214
215 @end
216
217 @implementation VLCTreeItem
218
219 static VLCTreeItem *o_root_item = nil;
220
221 #define IsALeafNode ((id)-1)
222
223 - (id)initWithName: (NSString *)o_item_name
224     withTitle: (NSString *)o_item_title
225     withHelp: (NSString *)o_item_help
226     ID: (int)i_id
227     parent:(VLCTreeItem *)o_parent_item
228     children:(NSMutableArray *)o_children_array
229     whithCategory: (int) i_category
230 {
231     self = [super init];
232
233     if( self != nil )
234     {
235         o_name = [o_item_name copy];
236         o_title= [o_item_title copy];
237         o_help= [o_item_help copy];
238         i_object_id = i_id;
239         o_parent = o_parent_item;
240         o_children = o_children_array;
241         i_object_category = i_category;
242         o_subviews = nil;
243     }
244     return( self );
245 }
246
247 + (VLCTreeItem *)rootItem
248 {
249    if (o_root_item == nil)
250         o_root_item = [[VLCTreeItem alloc] initWithName:@"main" withTitle:@"main" withHelp:@"" ID:0
251             parent:nil children:[[NSMutableArray alloc] initWithCapacity:10]
252             whithCategory: -1];
253    return o_root_item;
254 }
255
256 - (void)dealloc
257 {
258     if (o_children != IsALeafNode) [o_children release];
259     [o_name release];
260     [o_title release];
261     [o_help release];
262     [super dealloc];
263 }
264
265 /* Creates and returns the array of children
266  * Loads children incrementally */
267 - (NSArray *)children
268 {
269     if( o_children == IsALeafNode )
270         return o_children;
271     if( [ o_children count] == 0 )
272     {
273         intf_thread_t   *p_intf = VLCIntf;
274         vlc_list_t      *p_list;
275         module_t        *p_module = NULL;
276         module_t        *p_main_module;
277         module_config_t *p_items;
278         int             i = 0;
279         if( [[self getName] isEqualToString: @"main"] )
280         {
281             p_main_module = module_GetMainModule( p_intf );
282             assert( p_main_module );
283
284             /* We found the main module */
285             /* Enumerate config categories and store a reference so we can
286              * generate their config panel them when it is asked by the user. */
287             VLCTreeItem *p_last_category = NULL;
288             unsigned int i_confsize;
289             p_items = module_GetConfig( p_main_module, &i_confsize );
290             o_children = [[NSMutableArray alloc] initWithCapacity:10];
291             for( int i = 0; i < i_confsize; i++ )
292             {
293                 NSString *o_child_name;
294                 NSString *o_child_title;
295                 NSString *o_child_help;
296                 switch( p_items[i].i_type )
297                 {
298                     case CONFIG_CATEGORY:
299                         if( p_items[i].value.i == -1 ) break;
300
301                         o_child_name = [[VLCMain sharedInstance]
302                             localizedString: config_CategoryNameGet( p_items[i].value.i )];
303                         o_child_title = o_child_name;
304                         o_child_help = [[VLCMain sharedInstance]
305                             localizedString: config_CategoryHelpGet( p_items[i].value.i )];
306                         p_last_category = [VLCTreeItem alloc];
307                         [o_children addObject:[p_last_category
308                             initWithName: o_child_name
309                             withTitle: o_child_title
310                             withHelp: o_child_help
311                             ID: ((vlc_object_t*)p_main_module)->i_object_id
312                             parent:self
313                             children:[[NSMutableArray alloc]
314                                 initWithCapacity:10]
315                             whithCategory: p_items[i].value.i]];
316                         break;
317                     case CONFIG_SUBCATEGORY:
318                         if( p_items[i].value.i == -1 ) break;
319
320                         if( p_items[i].value.i != SUBCAT_PLAYLIST_GENERAL &&
321                             p_items[i].value.i != SUBCAT_VIDEO_GENERAL &&
322                             p_items[i].value.i != SUBCAT_INPUT_GENERAL &&
323                             p_items[i].value.i != SUBCAT_INTERFACE_GENERAL &&
324                             p_items[i].value.i != SUBCAT_SOUT_GENERAL &&
325                             p_items[i].value.i != SUBCAT_ADVANCED_MISC &&
326                             p_items[i].value.i != SUBCAT_AUDIO_GENERAL )
327                         {
328                             o_child_name = [[VLCMain sharedInstance]
329                                 localizedString: config_CategoryNameGet( p_items[i].value.i ) ];
330                             o_child_title = o_child_name;
331                             o_child_help = [[VLCMain sharedInstance]
332                                 localizedString: config_CategoryHelpGet( p_items[i].value.i ) ];
333
334                             [p_last_category->o_children
335                                 addObject:[[VLCTreeItem alloc]
336                                 initWithName: o_child_name
337                                 withTitle: o_child_title
338                                 withHelp: o_child_help
339                                 ID: ((vlc_object_t*)p_main_module)->i_object_id
340                                 parent:p_last_category
341                                 children:[[NSMutableArray alloc]
342                                     initWithCapacity:10]
343                                 whithCategory: p_items[i].value.i]];
344                         }
345
346                         break;
347                     default:
348                         break;
349                 }
350             }
351
352             vlc_object_release( (vlc_object_t *)p_main_module );
353
354             /* List the modules */
355             p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
356             if( !p_list ) return nil;
357
358             /* Build a tree of the plugins */
359             /* Add the capabilities */
360             for( i = 0; i < p_list->i_count; i++ )
361             {
362                 unsigned int confsize;
363                 p_module = (module_t *)p_list->p_values[i].p_object;
364
365                 /* Exclude the main module */
366                 if( module_IsMainModule( p_module ) )
367                     continue;
368
369                 /* Exclude empty plugins (submodules don't have config */
370                 /* options, they are stored in the parent module) */
371                 p_items = module_GetConfig( p_module, &confsize );
372
373                 unsigned int j;
374
375                 int i_category = -1;
376                 int i_subcategory = -1;
377                 bool b_item = false;
378
379                 for( j = 0; j < confsize; j++ )
380                 {
381                     if( p_items[j].i_type == CONFIG_CATEGORY )
382                         i_category = p_items[j].value.i;
383                     else if( p_items[j].i_type == CONFIG_SUBCATEGORY )
384                         i_subcategory = p_items[j].value.i;
385
386                     if( p_items[j].i_type & CONFIG_ITEM )
387                         b_item = true;
388             
389                     if( b_item && i_category >= 0 && i_subcategory >= 0 )
390                         break;
391                 }
392     
393                 if( !b_item ) continue;
394
395                 /* Find the right category item */
396
397                 long cookie;
398                 bool b_found = false;
399
400                 VLCTreeItem* p_category_item, * p_subcategory_item;
401                 for (j = 0 ; j < [o_children count] ; j++)
402                 {
403                     p_category_item = [o_children objectAtIndex: j];
404                     if( p_category_item->i_object_category == i_category )
405                     {
406                         b_found = true;
407                         break;
408                     }
409                 }
410                 if( !b_found ) continue;
411
412                 /* Find subcategory item */
413                 b_found = false;
414                 cookie = -1;
415                 for (j = 0 ; j < [p_category_item->o_children count] ; j++)
416                 {
417                     p_subcategory_item = [p_category_item->o_children
418                                             objectAtIndex: j];
419                     if( p_subcategory_item->i_object_category == i_subcategory )
420                     {
421                         b_found = true;
422                         break;
423                     }
424                 }
425                 if( !b_found )
426                     p_subcategory_item = p_category_item;
427
428                 [p_subcategory_item->o_children addObject:[[VLCTreeItem alloc]
429                     initWithName:[[VLCMain sharedInstance]
430                         localizedString: module_GetName( p_module, false ) ]
431                     withTitle:[[VLCMain sharedInstance]
432                         localizedString:  module_GetLongName( p_module ) ]
433                     withHelp: @""
434                     ID: ((vlc_object_t*)p_module)->i_object_id
435                     parent:p_subcategory_item
436                     children:IsALeafNode
437                     whithCategory: -1]];
438                 }
439             vlc_list_release( p_list );
440         }
441     }
442     return o_children;
443 }
444
445 - (int)getObjectID
446 {
447     return i_object_id;
448 }
449
450 - (NSString *)getName
451 {
452     return o_name;
453 }
454
455 - (NSString *)getTitle
456 {
457     return o_title;
458 }
459
460 - (NSString *)getHelp
461 {
462     return o_help;
463 }
464
465 - (VLCTreeItem *)childAtIndex:(int)i_index
466 {
467     return [[self children] objectAtIndex:i_index];
468 }
469
470 - (int)numberOfChildren {
471     id i_tmp = [self children];
472     return (i_tmp == IsALeafNode) ? (-1) : (int)[i_tmp count];
473 }
474
475 - (BOOL)hasPrefs:(NSString *)o_module_name
476 {
477     intf_thread_t *p_intf = VLCIntf;
478     module_t *p_parser;
479     vlc_list_t *p_list;
480     char *psz_module_name;
481     int i_index;
482
483     psz_module_name = (char *)[o_module_name UTF8String];
484
485     /* look for module */
486     p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
487
488     for( i_index = 0; i_index < p_list->i_count; i_index++ )
489     {
490         p_parser = (module_t *)p_list->p_values[i_index].p_object ;
491
492         if( !strcmp( module_GetObjName( p_parser ), psz_module_name ) )
493         {
494             unsigned int confsize;
495             module_GetConfig( p_parser, &confsize );
496             BOOL b_has_prefs = confsize != 0;
497             vlc_list_release( p_list );
498             return( b_has_prefs );
499         }
500     }
501
502     vlc_list_release( p_list );
503
504     return( NO );
505 }
506
507 - (NSView *)showView:(NSScrollView *)o_prefs_view
508 {
509     NSRect          s_vrc;
510     NSView          *o_view;
511
512     [[VLCPrefs sharedInstance] setTitle: [self getTitle]];
513     /* NSLog( [self getHelp] ); */
514     s_vrc = [[o_prefs_view contentView] bounds]; s_vrc.size.height -= 4;
515     o_view = [[VLCFlippedView alloc] initWithFrame: s_vrc];
516     [o_view setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin |
517                                     NSViewMaxYMargin];
518
519 /* Create all subviews if it isn't already done because we cannot use */
520 /* setHiden for MacOS < 10.3*/
521     if( o_subviews == nil )
522     {
523         intf_thread_t   *p_intf = VLCIntf;
524         vlc_list_t      *p_list;
525         module_t        *p_module = NULL;
526         module_t        *p_main_module;
527         module_config_t *p_items;
528         unsigned int confsize;
529
530         o_subviews = [[NSMutableArray alloc] initWithCapacity:10];
531         /* Get a pointer to the module */
532         if( i_object_category == -1 )
533         {
534             p_module = (module_t *) vlc_object_get( i_object_id );
535             assert( p_module );
536
537             p_items = module_GetConfig( p_module, &confsize );
538
539             for( unsigned int i = 0; i < confsize; i++ )
540             {
541                 switch( p_items[i].i_type )
542                 {
543                     case CONFIG_SUBCATEGORY:
544                     case CONFIG_CATEGORY:
545                     case CONFIG_SECTION:
546                     case CONFIG_HINT_USAGE:
547                         break;
548                     default:
549                     {
550                         VLCConfigControl *o_control = nil;
551                         o_control = [VLCConfigControl newControl:&p_items[i]
552                                                       withView:o_view];
553                         if( o_control )
554                         {
555                             [o_control setAutoresizingMask: NSViewMaxYMargin |
556                                 NSViewWidthSizable];
557                             [o_subviews addObject: o_control];
558                         }
559                     }
560                     break;
561                 }
562             }
563             vlc_object_release( (vlc_object_t*)p_module );
564         }
565         else
566         {
567             p_main_module = module_GetMainModule( p_intf );
568             assert( p_main_module );
569             module_config_t *p_items;
570
571             unsigned int i, confsize;
572             p_items = module_GetConfig( p_main_module, &confsize );
573
574             /* We need to first, find the right (sub)category,
575              * and then abort when we find a new (sub)category. Part of the Ugliness. */
576             bool in_right_category = false;
577             bool in_subcategory = false;
578             bool done = false;
579             for( i = 0; i < confsize; i++ )
580             {
581                 if( !p_items[i].i_type )
582                 {
583                     msg_Err( p_intf, "invalid preference item found" );
584                     break;
585                 }
586
587                 switch( p_items[i].i_type )
588                 {
589                     case CONFIG_CATEGORY:
590                         if(!in_right_category && p_items[i].value.i == i_object_category)
591                             in_right_category = true;
592                         else if(in_right_category)
593                             done = true;
594                         break;
595                     case CONFIG_SUBCATEGORY:
596                         if(!in_right_category && p_items[i].value.i == i_object_category)
597                         {
598                             in_right_category = true;
599                             in_subcategory = true;
600                         }
601                         else if(in_right_category && in_subcategory)
602                             done = true;
603                         break;
604                     case CONFIG_SECTION:
605                     case CONFIG_HINT_USAGE:
606                         break;
607                     default:
608                     {
609                         if(!in_right_category) break;
610
611                         VLCConfigControl *o_control = nil;
612                         o_control = [VLCConfigControl newControl:&p_items[i]
613                                                       withView:o_view];
614                         if( o_control != nil )
615                         {
616                             [o_control setAutoresizingMask: NSViewMaxYMargin |
617                                                             NSViewWidthSizable];
618                             [o_subviews addObject: o_control];
619                         }
620                         break;
621                     }
622                 }
623                 if( done ) break;
624             }
625             vlc_object_release( (vlc_object_t*)p_main_module );
626         }
627     }
628
629     if( o_view != nil )
630     {
631         int i_lastItem = 0;
632         int i_yPos = -2;
633         int i_max_label = 0;
634
635         NSEnumerator *enumerator = [o_subviews objectEnumerator];
636         VLCConfigControl *o_widget;
637         NSRect o_frame;
638  
639         while( ( o_widget = [enumerator nextObject] ) )
640             if( i_max_label < [o_widget getLabelSize] )
641                 i_max_label = [o_widget getLabelSize];
642
643         enumerator = [o_subviews objectEnumerator];
644         while( ( o_widget = [enumerator nextObject] ) )
645         {
646             int i_widget;
647
648             i_widget = [o_widget getViewType];
649             i_yPos += [VLCConfigControl calcVerticalMargin:i_widget
650                 lastItem:i_lastItem];
651             [o_widget setYPos:i_yPos];
652             o_frame = [o_widget frame];
653             o_frame.size.width = [o_view frame].size.width -
654                                     LEFTMARGIN - RIGHTMARGIN;
655             [o_widget setFrame:o_frame];
656             [o_widget alignWithXPosition: i_max_label];
657             i_yPos += [o_widget frame].size.height;
658             i_lastItem = i_widget;
659             [o_view addSubview:o_widget];
660          }
661
662         o_frame = [o_view frame];
663         o_frame.size.height = i_yPos;
664         [o_view setFrame:o_frame];
665         [o_prefs_view setDocumentView:o_view];
666
667     }
668     return o_view;
669 }
670
671 - (void)applyChanges
672 {
673     unsigned int i;
674     if( o_subviews != nil )
675         //Item has been shown
676         for( i = 0 ; i < [o_subviews count] ; i++ )
677             [[o_subviews objectAtIndex:i] applyChanges];
678
679     if( o_children != IsALeafNode )
680         for( i = 0 ; i < [o_children count] ; i++ )
681             [[o_children objectAtIndex:i] applyChanges];
682 }
683
684 - (void)resetView
685 {
686     unsigned int i;
687     if( o_subviews != nil )
688     {
689         //Item has been shown
690         [o_subviews release];
691         o_subviews = nil;
692     }
693
694     if( o_children != IsALeafNode )
695         for( i = 0 ; i < [o_children count] ; i++ )
696             [[o_children objectAtIndex:i] resetView];
697 }
698
699 @end
700
701
702 @implementation VLCFlippedView
703
704 - (BOOL)isFlipped
705 {
706     return( YES );
707 }
708
709 @end