]> git.sesse.net Git - vlc/blob - modules/gui/macosx/prefs.m
macosx: added an option to disable the native fullscreen mode on Lion
[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 "CompatibilityFixes.h"
56 #import "intf.h"
57 #import "prefs.h"
58 #import "simple_prefs.h"
59 #import "prefs_widgets.h"
60 #import <vlc_keys.h>
61 #import <vlc_modules.h>
62
63 /* /!\ Warning: Unreadable code :/ */
64
65 @interface VLCTreeItem : NSObject
66 {
67     NSString *_name;
68     NSMutableArray *_children;
69     NSMutableArray *_options;
70     NSMutableArray *_subviews;
71 }
72
73 - (id)initWithName:(NSString*)name;
74
75 - (int)numberOfChildren;
76 - (VLCTreeItem *)childAtIndex:(NSInteger)i_index;
77
78 - (NSString *)name;
79 - (NSMutableArray *)children;
80 - (NSMutableArray *)options;
81 - (void)showView:(NSScrollView *)o_prefs_view;
82 - (void)applyChanges;
83 - (void)resetView;
84
85 @end
86
87 /* CONFIG_SUBCAT */
88 @interface VLCTreeSubCategoryItem : VLCTreeItem
89 {
90     int _subCategory;
91 }
92 + (VLCTreeSubCategoryItem *)subCategoryTreeItemWithSubCategory:(int)subCategory;
93 - (id)initWithSubCategory:(int)subCategory;
94 - (int)subCategory;
95 @end
96
97 /* Plugin daughters */
98 @interface VLCTreePluginItem : VLCTreeItem
99 {
100     module_config_t * _configItems;
101     unsigned int _configSize;
102 }
103 + (VLCTreePluginItem *)pluginTreeItemWithPlugin:(module_t *)plugin;
104 - (id)initWithPlugin:(module_t *)plugin;
105
106 - (module_config_t *)configItems;
107 - (unsigned int)configSize;
108 @end
109
110 /* CONFIG_CAT */
111 @interface VLCTreeCategoryItem : VLCTreeItem
112 {
113     int _category;
114 }
115 + (VLCTreeCategoryItem *)categoryTreeItemWithCategory:(int)category;
116 - (id)initWithCategory:(int)category;
117
118 - (int)category;
119 - (VLCTreeSubCategoryItem *)itemRepresentingSubCategory:(int)category;
120 @end
121
122 /* individual options. */
123 @interface VLCTreeLeafItem : VLCTreeItem
124 {
125     module_config_t * _configItem;
126 }
127 - (id)initWithConfigItem:(module_config_t *)configItem;
128
129 - (module_config_t *)configItem;
130 @end
131
132 @interface VLCTreeMainItem : VLCTreePluginItem
133 {
134 }
135 - (VLCTreeCategoryItem *)itemRepresentingCategory:(int)category;
136 @end
137
138 #pragma mark -
139
140 /*****************************************************************************
141  * VLCPrefs implementation
142  *****************************************************************************/
143 @implementation VLCPrefs
144
145 static VLCPrefs *_o_sharedMainInstance = nil;
146
147 + (VLCPrefs *)sharedInstance
148 {
149     return _o_sharedMainInstance ? _o_sharedMainInstance : [[self alloc] init];
150 }
151
152 - (id)init
153 {
154     if( _o_sharedMainInstance ) {
155         [self dealloc];
156     }
157     else
158     {
159         _o_sharedMainInstance = [super init];
160         p_intf = VLCIntf;
161         o_empty_view = [[NSView alloc] init];
162         _rootTreeItem = [[VLCTreeMainItem alloc] init];
163     }
164
165     return _o_sharedMainInstance;
166 }
167
168 - (void)dealloc
169 {
170     [o_empty_view release];
171     [_rootTreeItem release];
172     [super dealloc];
173 }
174
175 - (void)awakeFromNib
176 {
177     p_intf = VLCIntf;
178
179     if (OSX_LION)
180         [o_prefs_window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenAuxiliary];
181
182     [self initStrings];
183     [o_prefs_view setBorderType: NSGrooveBorder];
184     [o_prefs_view setHasVerticalScroller: YES];
185     [o_prefs_view setDrawsBackground: NO];
186     [o_prefs_view setDocumentView: o_empty_view];
187         [o_tree selectRowIndexes: [NSIndexSet indexSetWithIndex: 0] byExtendingSelection: NO];
188 }
189
190 - (void)setTitle: (NSString *) o_title_name
191 {
192     [o_title setStringValue: o_title_name];
193 }
194
195 - (void)showPrefs
196 {
197     [[o_basicFull_matrix cellAtRow:0 column:0] setState: NSOffState];
198     [[o_basicFull_matrix cellAtRow:0 column:1] setState: NSOnState];
199     
200     [o_prefs_window center];
201     [o_prefs_window makeKeyAndOrderFront:self];
202     [_rootTreeItem resetView];
203 }
204
205 - (void)initStrings
206 {
207     [o_prefs_window setTitle: _NS("Preferences")];
208     [o_save_btn setTitle: _NS("Save")];
209     [o_cancel_btn setTitle: _NS("Cancel")];
210     [o_reset_btn setTitle: _NS("Reset All")];
211     [[o_basicFull_matrix cellAtRow: 0 column: 0] setStringValue: _NS("Basic")];
212     [[o_basicFull_matrix cellAtRow: 0 column: 1] setStringValue: _NS("All")];
213 }
214
215 - (IBAction)savePrefs: (id)sender
216 {
217     /* TODO: call savePrefs on Root item */
218     [_rootTreeItem applyChanges];
219     config_SaveConfigFile( p_intf );
220     [o_prefs_window orderOut:self];
221 }
222
223 - (IBAction)closePrefs: (id)sender
224 {
225     [o_prefs_window orderOut:self];
226 }
227
228 - (IBAction)resetAll: (id)sender
229 {
230     NSBeginInformationalAlertSheet(_NS("Reset Preferences"), _NS("Cancel"),
231         _NS("Continue"), nil, o_prefs_window, self,
232         @selector(sheetDidEnd: returnCode: contextInfo:), NULL, nil,
233         _NS("Beware this will reset the VLC media player preferences.\n"
234             "Are you sure you want to continue?") );
235 }
236
237 - (void)sheetDidEnd:(NSWindow *)o_sheet returnCode:(int)i_return
238     contextInfo:(void *)o_context
239 {
240     if( i_return == NSAlertAlternateReturn )
241     {
242         config_ResetAll( p_intf );
243         [_rootTreeItem resetView];
244     }
245 }
246
247 - (IBAction)buttonAction: (id)sender
248 {
249     [o_prefs_window orderOut: self];
250     [[o_basicFull_matrix cellAtRow:0 column:0] setState: NSOnState];
251     [[o_basicFull_matrix cellAtRow:0 column:1] setState: NSOffState];
252     [[[VLCMain sharedInstance] simplePreferences] showSimplePrefs];
253 }
254
255 - (void)loadConfigTree
256 {
257 }
258
259 - (void)outlineViewSelectionIsChanging:(NSNotification *)o_notification
260 {
261 }
262
263 /* update the document view to the view of the selected tree item */
264 - (void)outlineViewSelectionDidChange:(NSNotification *)o_notification
265 {
266     [[o_tree itemAtRow:[o_tree selectedRow]] showView: o_prefs_view];
267     [o_tree expandItem:[o_tree itemAtRow:[o_tree selectedRow]]];
268 }
269
270 @end
271
272 @implementation VLCPrefs (NSTableDataSource)
273
274 - (NSInteger)outlineView:(NSOutlineView *)outlineView numberOfChildrenOfItem:(id)item
275 {
276     return (item == nil) ? [_rootTreeItem numberOfChildren] : [item numberOfChildren];
277 }
278
279 - (BOOL)outlineView:(NSOutlineView *)outlineView isItemExpandable:(id)item
280 {
281     return (item == nil) ? [_rootTreeItem numberOfChildren] : [item numberOfChildren];
282 }
283
284 - (id)outlineView:(NSOutlineView *)outlineView child:(NSInteger)index ofItem:(id)item
285 {
286     return (item == nil) ? (id)[_rootTreeItem childAtIndex:index]: (id)[item childAtIndex:index];
287 }
288
289 - (id)outlineView:(NSOutlineView *)outlineView
290     objectValueForTableColumn:(NSTableColumn *)tableColumn byItem:(id)item
291 {
292     return (item == nil) ? @"" : [item name];
293 }
294
295 @end
296
297 #pragma mark -
298 @implementation VLCTreeMainItem
299
300 - (VLCTreeCategoryItem *)itemRepresentingCategory:(int)category
301 {
302     NSUInteger childrenCount = [[self children] count];
303     for( int i = 0; i < childrenCount; i++ )
304     {
305         VLCTreeCategoryItem * categoryItem = [[self children] objectAtIndex:i];
306         if( [categoryItem category] == category )
307             return categoryItem;
308     }
309     return nil;
310 }
311
312 - (bool)isSubCategoryGeneral:(int)category
313 {
314     if(category == SUBCAT_VIDEO_GENERAL ||
315           category == SUBCAT_ADVANCED_MISC ||
316           category == SUBCAT_INPUT_GENERAL ||
317           category == SUBCAT_INTERFACE_GENERAL ||
318           category == SUBCAT_SOUT_GENERAL||
319           category == SUBCAT_PLAYLIST_GENERAL||
320           category == SUBCAT_AUDIO_GENERAL )
321     {
322         return true;
323     }
324     return false;
325 }
326
327 /* Creates and returns the array of children
328  * Loads children incrementally */
329 - (NSMutableArray *)children
330 {
331     if( _children ) return _children;
332     _children = [[NSMutableArray alloc] init];
333
334     intf_thread_t   *p_intf = VLCIntf;
335
336     /* List the modules */
337     size_t count, i;
338     module_t ** modules = module_list_get( &count );
339     if( !modules ) return nil;
340
341     /* Build a tree of the plugins */
342     /* Add the capabilities */
343     for( i = 0; i < count; i++ )
344     {
345         VLCTreeCategoryItem * categoryItem = nil;
346         VLCTreeSubCategoryItem * subCategoryItem = nil;
347         VLCTreePluginItem * pluginItem = nil;
348         module_config_t *p_configs = NULL;
349         int lastsubcat = 0;
350         unsigned int confsize;
351
352         module_t * p_module = modules[i];
353
354         /* Exclude empty plugins (submodules don't have config */
355         /* options, they are stored in the parent module) */
356         if( module_is_main( p_module ) ) {
357             pluginItem = self;
358             _configItems = module_config_get( p_module, &confsize );
359             _configSize = confsize;
360         } else {
361             pluginItem = [VLCTreePluginItem pluginTreeItemWithPlugin: p_module];
362             confsize = [pluginItem configSize];
363         }
364         p_configs = [pluginItem configItems];
365
366         unsigned int j;
367         for( j = 0; j < confsize; j++ )
368         {
369             int configType = p_configs[j].i_type;
370             if( configType == CONFIG_CATEGORY )
371             {
372                 categoryItem = [self itemRepresentingCategory:p_configs[j].value.i];
373                 if(!categoryItem)
374                 {
375                     categoryItem = [VLCTreeCategoryItem categoryTreeItemWithCategory:p_configs[j].value.i];
376                     if(categoryItem) [[self children] addObject:categoryItem];
377                 }
378             }
379             else if( configType == CONFIG_SUBCATEGORY )
380             {
381                 lastsubcat = p_configs[j].value.i;
382                 if( categoryItem && ![self isSubCategoryGeneral:lastsubcat] )
383                 {
384                     subCategoryItem = [categoryItem itemRepresentingSubCategory:lastsubcat];
385                     if(!subCategoryItem)
386                     {
387                         subCategoryItem = [VLCTreeSubCategoryItem subCategoryTreeItemWithSubCategory:lastsubcat];
388                         if(subCategoryItem) [[categoryItem children] addObject:subCategoryItem];
389                     }
390                 }
391             }
392             
393             if( module_is_main( p_module) && CONFIG_ITEM(configType) )
394             {
395                 if( categoryItem && [self isSubCategoryGeneral:lastsubcat] )
396                 {
397                     [[categoryItem options] addObject:[[VLCTreeLeafItem alloc] initWithConfigItem:&p_configs[j]]];
398                 }
399                 else if( subCategoryItem )
400                 {
401                     [[subCategoryItem options] addObject:[[VLCTreeLeafItem alloc] initWithConfigItem:&p_configs[j]]];
402                 }
403             }
404             else if( !module_is_main( p_module) && CONFIG_ITEM(configType))
405             {
406                 if( ![[subCategoryItem children] containsObject: pluginItem] )
407                 {
408                     [[subCategoryItem children] addObject:pluginItem];
409                 }
410                 if( pluginItem )
411                     [[pluginItem options] addObject:[[VLCTreeLeafItem alloc] initWithConfigItem:&p_configs[j]]];
412             }
413         }
414     }
415     module_list_free( modules );
416     return _children;
417 }
418 @end
419
420 #pragma mark -
421 @implementation VLCTreeCategoryItem
422 + (VLCTreeCategoryItem *)categoryTreeItemWithCategory:(int)category
423 {
424     if(!config_CategoryNameGet( category )) return nil;
425     return [[[[self class] alloc] initWithCategory:category] autorelease];
426 }
427 - (id)initWithCategory:(int)category
428 {
429     NSString * name = [[VLCMain sharedInstance] localizedString: config_CategoryNameGet( category )];
430     if(self = [super initWithName:name])
431     {
432         _category = category;
433         //_help = [[[VLCMain sharedInstance] localizedString: config_CategoryHelpGet( category )] retain];
434     }
435     return self;
436 }
437
438 - (VLCTreeSubCategoryItem *)itemRepresentingSubCategory:(int)subCategory
439 {
440     assert( [self isKindOfClass:[VLCTreeCategoryItem class]] );
441     NSUInteger childrenCount = [[self children] count];
442     for( NSUInteger i = 0; i < childrenCount; i++ )
443     {
444         VLCTreeSubCategoryItem * subCategoryItem = [[self children] objectAtIndex:i];
445         if( [subCategoryItem subCategory] == subCategory )
446             return subCategoryItem;
447     }
448     return nil;
449 }
450
451 - (int)category
452 {
453     return _category;
454 }
455 @end
456
457 #pragma mark -
458 @implementation VLCTreeSubCategoryItem
459 - (id)initWithSubCategory:(int)subCategory
460 {
461     NSString * name = [[VLCMain sharedInstance] localizedString: config_CategoryNameGet( subCategory )];
462     if(self = [super initWithName:name])
463     {
464         _subCategory = subCategory;
465         //_help = [[[VLCMain sharedInstance] localizedString: config_CategoryHelpGet( subCategory )] retain];
466     }
467     return self;
468 }
469
470 + (VLCTreeSubCategoryItem *)subCategoryTreeItemWithSubCategory:(int)subCategory
471 {
472     if(!config_CategoryNameGet( subCategory )) return nil;
473     return [[[[self class] alloc] initWithSubCategory:subCategory] autorelease];
474 }
475
476 - (int)subCategory
477 {
478     return _subCategory;
479 }
480
481 @end
482
483 #pragma mark -
484 @implementation VLCTreePluginItem
485 - (id)initWithPlugin:(module_t *)plugin
486 {
487     NSString * name = [[VLCMain sharedInstance] localizedString: module_get_name( plugin, false )?:""];
488     if(self = [super initWithName:name])
489     {
490         _configItems = module_config_get( plugin, &_configSize );
491         //_plugin = plugin;
492         //_help = [[[VLCMain sharedInstance] localizedString: config_CategoryHelpGet( subCategory )] retain];
493     }
494     return self;
495 }
496
497 + (VLCTreePluginItem *)pluginTreeItemWithPlugin:(module_t *)plugin
498 {
499     return [[[[self class] alloc] initWithPlugin:plugin] autorelease];
500 }
501
502 - (void)dealloc
503 {
504     module_config_free( _configItems );
505     [super dealloc];
506 }
507
508 - (module_config_t *)configItems
509 {
510     return _configItems;
511 }
512
513 - (unsigned int)configSize
514 {
515     return _configSize;
516 }
517
518 @end
519
520 #pragma mark -
521 @implementation VLCTreeLeafItem
522
523 - (id)initWithConfigItem: (module_config_t *) configItem
524 {
525     NSString * name = [[VLCMain sharedInstance] localizedString:configItem->psz_name];
526     self = [super initWithName:name];
527     [name release];
528     if( self != nil )
529     {
530         _configItem = configItem;
531     }
532     return self;
533 }
534
535 - (module_config_t *)configItem
536 {
537     return _configItem;
538 }
539 @end
540
541 #pragma mark -
542 #pragma mark (Root class for all TreeItems)
543 @implementation VLCTreeItem
544
545 - (id)initWithName:(NSString*)name
546 {
547     self = [super init];
548     if( self != nil )
549     {
550         _name = [name retain];
551     }
552     return self;
553 }
554
555 - (void)dealloc
556 {
557     [_children release];
558     [_options release];
559     [_name release];
560     [_subviews release];
561     [super dealloc];
562 }
563
564 - (VLCTreeItem *)childAtIndex:(NSInteger)i_index
565 {
566     return [[self children] objectAtIndex:i_index];
567 }
568
569 - (int)numberOfChildren
570 {
571     return [[self children] count];
572 }
573
574 - (NSString *)name
575 {
576     return [[_name retain] autorelease];
577 }
578
579 - (void)showView:(NSScrollView *)prefsView
580 {
581     NSRect          s_vrc;
582     NSView          *view;
583
584     [[VLCPrefs sharedInstance] setTitle: [self name]];
585     s_vrc = [[prefsView contentView] bounds]; s_vrc.size.height -= 4;
586     view = [[VLCFlippedView alloc] initWithFrame: s_vrc];
587     [view setAutoresizingMask: NSViewWidthSizable | NSViewMinYMargin | NSViewMaxYMargin];
588
589     if(!_subviews)
590     {
591         _subviews = [[NSMutableArray alloc] initWithCapacity:10];
592
593         NSUInteger count = [[self options] count];
594         for( NSUInteger i = 0; i < count; i++)
595         {
596             VLCTreeLeafItem * item = [[self options] objectAtIndex:i];
597
598             VLCConfigControl *control;
599             control = [VLCConfigControl newControl:[item configItem] withView:view];
600             if( control )
601             {
602                 [control setAutoresizingMask: NSViewMaxYMargin | NSViewWidthSizable];
603                 [_subviews addObject: control];
604             }
605         }
606     }
607
608     assert(view);
609     
610     int i_lastItem = 0;
611     int i_yPos = -2;
612     int i_max_label = 0;
613
614     NSEnumerator *enumerator = [_subviews objectEnumerator];
615     VLCConfigControl *widget;
616     NSRect frame;
617
618     while( ( widget = [enumerator nextObject] ) )
619         if( i_max_label < [widget labelSize] )
620             i_max_label = [widget labelSize];
621
622     enumerator = [_subviews objectEnumerator];
623     while( ( widget = [enumerator nextObject] ) )
624     {
625         int i_widget;
626
627         i_widget = [widget viewType];
628         i_yPos += [VLCConfigControl calcVerticalMargin:i_widget lastItem:i_lastItem];
629         [widget setYPos:i_yPos];
630         frame = [widget frame];
631         frame.size.width = [view frame].size.width - LEFTMARGIN - RIGHTMARGIN;
632         [widget setFrame:frame];
633         [widget alignWithXPosition: i_max_label];
634         i_yPos += [widget frame].size.height;
635         i_lastItem = i_widget;
636         [view addSubview:widget];
637     }
638
639     frame = [view frame];
640     frame.size.height = i_yPos;
641     [view setFrame:frame];
642     [prefsView setDocumentView:view];
643 }
644
645 - (void)applyChanges
646 {
647     NSUInteger i;
648     NSUInteger count = [_subviews count];
649     for( i = 0 ; i < count ; i++ )
650         [[_subviews objectAtIndex:i] applyChanges];
651
652     count = [_children count];
653     for( i = 0 ; i < count ; i++ )
654         [[_children objectAtIndex:i] applyChanges];
655 }
656
657 - (void)resetView
658 {
659     NSUInteger i;
660     NSUInteger count = [_subviews count];
661     for( i = 0 ; i < count ; i++ )
662         [[_subviews objectAtIndex:i] resetValues];
663
664     count = [_options count];
665     for( i = 0 ; i < count ; i++ )
666         [[_options objectAtIndex:i] resetView];
667
668     count = [_children count];
669     for( i = 0 ; i < count ; i++ )
670         [[_children objectAtIndex:i] resetView];
671
672 }
673
674 - (NSMutableArray *)children
675 {
676     if(!_children) _children = [[NSMutableArray alloc] init];
677     return _children;
678 }
679
680 - (NSMutableArray *)options
681 {
682     if(!_options) _options = [[NSMutableArray alloc] init];
683     return _options;
684 }
685 @end
686
687 #pragma mark -
688 @implementation VLCFlippedView
689
690 - (BOOL)isFlipped
691 {
692     return( YES );
693 }
694
695 @end