1 /*****************************************************************************
2 * prefs.m: MacOS X module for vlc
3 *****************************************************************************
4 * Copyright (C) 2002-2006 the VideoLAN team
7 * Authors: Jon Lech Johansen <jon-vl@nanocrew.net>
8 * Derk-Jan Hartman <hartman at videolan dot org>
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.
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.
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 *****************************************************************************/
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)
36 The class is sort of a mix of wxwindows intfs, PrefsTreeCtrl and ConfigTreeData
38 /* VLCConfigControl are subclassed NSView's containing and managing individual config items
39 the classes are VERY closely related to wxwindows ConfigControls */
41 /*****************************************************************************
43 *****************************************************************************/
44 #include <stdlib.h> /* malloc(), free() */
45 #include <sys/param.h> /* for MAXPATHLEN */
52 #include <vlc_common.h>
53 #include <vlc_config_cat.h>
57 #import "simple_prefs.h"
58 #import "prefs_widgets.h"
61 /* /!\ Warning: Unreadable code :/ */
63 /*****************************************************************************
64 * VLCPrefs implementation
65 *****************************************************************************/
66 @implementation VLCPrefs
68 static VLCPrefs *_o_sharedMainInstance = nil;
70 + (VLCPrefs *)sharedInstance
72 return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
77 if( _o_sharedMainInstance ) {
82 _o_sharedMainInstance = [super init];
84 o_empty_view = [[NSView alloc] init];
87 return _o_sharedMainInstance;
92 [o_empty_view release];
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];
108 - (void)setTitle: (NSString *) o_title_name
110 [o_title setStringValue: o_title_name];
115 [[o_basicFull_matrix cellAtRow:0 column:0] setState: NSOffState];
116 [[o_basicFull_matrix cellAtRow:0 column:1] setState: NSOnState];
118 [o_prefs_window center];
119 [o_prefs_window makeKeyAndOrderFront:self];
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")];
132 - (IBAction)savePrefs: (id)sender
134 /* TODO: call savePrefs on Root item */
135 [[VLCTreeItem rootItem] applyChanges];
136 config_SaveConfigFile( p_intf, NULL );
137 [o_prefs_window orderOut:self];
140 - (IBAction)closePrefs: (id)sender
142 [o_prefs_window orderOut:self];
145 - (IBAction)resetAll: (id)sender
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?") );
154 - (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return
155 contextInfo:(void *)o_context
157 if( i_return == NSAlertAlternateReturn )
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];
167 - (IBAction)buttonAction: (id)sender
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];
175 - (void)loadConfigTree
179 - (void)outlineViewSelectionIsChanging:(NSNotification *)o_notification
183 /* update the document view to the view of the selected tree item */
184 - (void)outlineViewSelectionDidChange:(NSNotification *)o_notification
186 [[o_tree itemAtRow:[o_tree selectedRow]] showView: o_prefs_view];
191 @implementation VLCPrefs (NSTableDataSource)
193 - (int)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item {
194 return (item == nil) ? [[VLCTreeItem rootItem] numberOfChildren] :
195 [item numberOfChildren];
198 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
200 return (item == nil) ? YES : ( ([item numberOfChildren] != -1) &&
201 ([item numberOfChildren] != 0));
204 - (id)outlineView:(NSOutlineView *)outlineView child:(int)index ofItem:(id)item {
205 return (item == nil) ? [[VLCTreeItem rootItem] childAtIndex:index] :
206 (id)[item childAtIndex:index];
209 - (id)outlineView:(NSOutlineView *)outlineView
210 objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
212 return (item == nil) ? @"" : (id)[item getName];
217 @implementation VLCTreeItem
219 static VLCTreeItem *o_root_item = nil;
221 #define IsALeafNode ((id)-1)
223 - (id)initWithName: (NSString *)o_item_name
224 withTitle: (NSString *)o_item_title
225 withHelp: (NSString *)o_item_help
227 parent:(VLCTreeItem *)o_parent_item
228 children:(NSMutableArray *)o_children_array
229 whithCategory: (int) i_category
235 o_name = [o_item_name copy];
236 o_title= [o_item_title copy];
237 o_help= [o_item_help copy];
239 o_parent = o_parent_item;
240 o_children = o_children_array;
241 i_object_category = i_category;
247 + (VLCTreeItem *)rootItem
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]
258 if (o_children != IsALeafNode) [o_children release];
265 /* Creates and returns the array of children
266 * Loads children incrementally */
267 - (NSArray *)children
269 if( o_children == IsALeafNode )
271 if( [ o_children count] == 0 )
273 intf_thread_t *p_intf = VLCIntf;
275 module_t *p_module = NULL;
276 module_t *p_main_module;
277 module_config_t *p_items;
279 if( [[self getName] isEqualToString: @"main"] )
281 p_main_module = module_GetMainModule( p_intf );
282 assert( p_main_module );
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++ )
293 NSString *o_child_name;
294 NSString *o_child_title;
295 NSString *o_child_help;
296 switch( p_items[i].i_type )
298 case CONFIG_CATEGORY:
299 if( p_items[i].value.i == -1 ) break;
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
313 children:[[NSMutableArray alloc]
315 whithCategory: p_items[i].value.i]];
317 case CONFIG_SUBCATEGORY:
318 if( p_items[i].value.i == -1 ) break;
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 )
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 ) ];
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]
343 whithCategory: p_items[i].value.i]];
352 vlc_object_release( (vlc_object_t *)p_main_module );
354 /* List the modules */
355 p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
356 if( !p_list ) return nil;
358 /* Build a tree of the plugins */
359 /* Add the capabilities */
360 for( i = 0; i < p_list->i_count; i++ )
362 unsigned int confsize;
363 p_module = (module_t *)p_list->p_values[i].p_object;
365 /* Exclude the main module */
366 if( module_IsMainModule( p_module ) )
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 );
376 int i_subcategory = -1;
379 for( j = 0; j < confsize; j++ )
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;
386 if( p_items[j].i_type & CONFIG_ITEM )
389 if( b_item && i_category >= 0 && i_subcategory >= 0 )
393 if( !b_item ) continue;
395 /* Find the right category item */
398 bool b_found = false;
400 VLCTreeItem* p_category_item, * p_subcategory_item;
401 for (j = 0 ; j < [o_children count] ; j++)
403 p_category_item = [o_children objectAtIndex: j];
404 if( p_category_item->i_object_category == i_category )
410 if( !b_found ) continue;
412 /* Find subcategory item */
415 for (j = 0 ; j < [p_category_item->o_children count] ; j++)
417 p_subcategory_item = [p_category_item->o_children
419 if( p_subcategory_item->i_object_category == i_subcategory )
426 p_subcategory_item = p_category_item;
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 ) ]
434 ID: ((vlc_object_t*)p_module)->i_object_id
435 parent:p_subcategory_item
439 vlc_list_release( p_list );
450 - (NSString *)getName
455 - (NSString *)getTitle
460 - (NSString *)getHelp
465 - (VLCTreeItem *)childAtIndex:(int)i_index
467 return [[self children] objectAtIndex:i_index];
470 - (int)numberOfChildren {
471 id i_tmp = [self children];
472 return (i_tmp == IsALeafNode) ? (-1) : (int)[i_tmp count];
475 - (BOOL)hasPrefs:(NSString *)o_module_name
477 intf_thread_t *p_intf = VLCIntf;
480 char *psz_module_name;
483 psz_module_name = (char *)[o_module_name UTF8String];
485 /* look for module */
486 p_list = vlc_list_find( p_intf, VLC_OBJECT_MODULE, FIND_ANYWHERE );
488 for( i_index = 0; i_index < p_list->i_count; i_index++ )
490 p_parser = (module_t *)p_list->p_values[i_index].p_object ;
492 if( !strcmp( module_GetObjName( p_parser ), psz_module_name ) )
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 );
502 vlc_list_release( p_list );
507 - (NSView *)showView:(NSScrollView *)o_prefs_view
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 |
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 )
523 intf_thread_t *p_intf = VLCIntf;
525 module_t *p_module = NULL;
526 module_t *p_main_module;
527 module_config_t *p_items;
528 unsigned int confsize;
530 o_subviews = [[NSMutableArray alloc] initWithCapacity:10];
531 /* Get a pointer to the module */
532 if( i_object_category == -1 )
534 p_module = (module_t *) vlc_object_get( i_object_id );
537 p_items = module_GetConfig( p_module, &confsize );
539 for( unsigned int i = 0; i < confsize; i++ )
541 switch( p_items[i].i_type )
543 case CONFIG_SUBCATEGORY:
544 case CONFIG_CATEGORY:
546 case CONFIG_HINT_USAGE:
550 VLCConfigControl *o_control = nil;
551 o_control = [VLCConfigControl newControl:&p_items[i]
555 [o_control setAutoresizingMask: NSViewMaxYMargin |
557 [o_subviews addObject: o_control];
563 vlc_object_release( (vlc_object_t*)p_module );
567 p_main_module = module_GetMainModule( p_intf );
568 assert( p_main_module );
569 module_config_t *p_items;
571 unsigned int i, confsize;
572 p_items = module_GetConfig( p_main_module, &confsize );
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;
579 for( i = 0; i < confsize; i++ )
581 if( !p_items[i].i_type )
583 msg_Err( p_intf, "invalid preference item found" );
587 switch( p_items[i].i_type )
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)
595 case CONFIG_SUBCATEGORY:
596 if(!in_right_category && p_items[i].value.i == i_object_category)
598 in_right_category = true;
599 in_subcategory = true;
601 else if(in_right_category && in_subcategory)
605 case CONFIG_HINT_USAGE:
609 if(!in_right_category) break;
611 VLCConfigControl *o_control = nil;
612 o_control = [VLCConfigControl newControl:&p_items[i]
614 if( o_control != nil )
616 [o_control setAutoresizingMask: NSViewMaxYMargin |
618 [o_subviews addObject: o_control];
625 vlc_object_release( (vlc_object_t*)p_main_module );
635 NSEnumerator *enumerator = [o_subviews objectEnumerator];
636 VLCConfigControl *o_widget;
639 while( ( o_widget = [enumerator nextObject] ) )
640 if( i_max_label < [o_widget getLabelSize] )
641 i_max_label = [o_widget getLabelSize];
643 enumerator = [o_subviews objectEnumerator];
644 while( ( o_widget = [enumerator nextObject] ) )
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];
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];
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];
679 if( o_children != IsALeafNode )
680 for( i = 0 ; i < [o_children count] ; i++ )
681 [[o_children objectAtIndex:i] applyChanges];
687 if( o_subviews != nil )
689 //Item has been shown
690 [o_subviews release];
694 if( o_children != IsALeafNode )
695 for( i = 0 ; i < [o_children count] ; i++ )
696 [[o_children objectAtIndex:i] resetView];
702 @implementation VLCFlippedView