cog/Preferences/SS_PrefsController.m

580 lines
16 KiB
Objective-C

#import "SS_PrefsController.h"
#import "SS_PreferencePaneProtocol.h"
@implementation SS_PrefsController
#define Last_Pane_Defaults_Key [[[NSBundle mainBundle] bundleIdentifier] stringByAppendingString:@"_Preferences_Last_Pane_Defaults_Key"]
// ************************************************
// version/init/dealloc/constructors
// ************************************************
+ (int)version
{
return 1; // 28th June 2003
}
+ (id)preferencesWithPanesSearchPath:(NSString*)path bundleExtension:(NSString *)ext
{
return [[[SS_PrefsController alloc] initWithPanesSearchPath:path bundleExtension:ext] autorelease];
}
+ (id)preferencesWithBundleExtension:(NSString *)ext
{
return [[[SS_PrefsController alloc] initWithBundleExtension:ext] autorelease];
}
+ (id)preferencesWithPanesSearchPath:(NSString*)path
{
return [[[SS_PrefsController alloc] initWithPanesSearchPath:path] autorelease];
}
+ (id)preferences
{
return [[[SS_PrefsController alloc] init] autorelease];
}
- (id)init
{
return [self initWithPanesSearchPath:nil bundleExtension:nil];
}
- (id)initWithPanesSearchPath:(NSString*)path
{
return [self initWithPanesSearchPath:path bundleExtension:nil];
}
- (id)initWithBundleExtension:(NSString *)ext
{
return [self initWithPanesSearchPath:nil bundleExtension:ext];
}
// Designated initializer
- (id)initWithPanesSearchPath:(NSString*)path bundleExtension:(NSString *)ext
{
if (self = [super init]) {
[self setDebug:NO];
preferencePanes = [[NSMutableDictionary alloc] init];
panesOrder = [[NSMutableArray alloc] init];
[self setToolbarDisplayMode:NSToolbarDisplayModeIconAndLabel];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
[self setToolbarSizeMode:NSToolbarSizeModeDefault];
#endif
[self setUsesTexturedWindow:NO];
[self setAlwaysShowsToolbar:NO];
[self setAlwaysOpensCentered:YES];
if (!ext || [ext isEqualToString:@""]) {
bundleExtension = [[NSString alloc] initWithString:@"preferencePane"];
} else {
bundleExtension = [ext retain];
}
if (!path || [path isEqualToString:@""]) {
searchPath = [[NSString alloc] initWithString:[[NSBundle mainBundle] resourcePath]];
} else {
searchPath = [path retain];
}
// Read PreferencePanes
if (searchPath) {
NSEnumerator* enumerator = [[NSBundle pathsForResourcesOfType:bundleExtension inDirectory:searchPath] objectEnumerator];
NSString* panePath;
while ((panePath = [enumerator nextObject])) {
[self activatePane:panePath];
}
}
return self;
}
return nil;
}
- (void)dealloc
{
if (prefsWindow) {
[prefsWindow release];
}
if (prefsToolbar) {
[prefsToolbar release];
}
if (prefsToolbarItems) {
[prefsToolbarItems release];
}
if (preferencePanes) {
[preferencePanes release];
}
if (panesOrder) {
[panesOrder release];
}
if (bundleExtension) {
[bundleExtension release];
}
if (searchPath) {
[searchPath release];
}
[super dealloc];
}
// ************************************************
// Preferences methods
// ************************************************
- (void)showPreferencesWindow
{
[self createPreferencesWindowAndDisplay:YES];
}
- (void)createPreferencesWindow
{
[self createPreferencesWindowAndDisplay:YES];
}
- (void)createPreferencesWindowAndDisplay:(BOOL)shouldDisplay
{
if (prefsWindow) {
if (alwaysOpensCentered && ![prefsWindow isVisible]) {
[prefsWindow center];
}
[prefsWindow makeKeyAndOrderFront:nil];
return;
}
// Create prefs window
unsigned int styleMask = (NSClosableWindowMask | NSResizableWindowMask);
if (usesTexturedWindow) {
styleMask = (styleMask | NSTexturedBackgroundWindowMask);
}
prefsWindow = [[NSWindow alloc] initWithContentRect:NSMakeRect(0, 0, 350, 200)
styleMask:styleMask
backing:NSBackingStoreBuffered
defer:NO];
[prefsWindow setReleasedWhenClosed:NO];
[prefsWindow setTitle:@"Preferences"]; // initial default title
[prefsWindow center];
[self createPrefsToolbar];
// Register defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if (panesOrder && ([panesOrder count] > 0)) {
NSMutableDictionary *defaultValues = [NSMutableDictionary dictionary];
[defaultValues setObject:[panesOrder objectAtIndex:0] forKey:Last_Pane_Defaults_Key];
[defaults registerDefaults:defaultValues];
}
// Load last view
NSString *lastViewName = [defaults objectForKey:Last_Pane_Defaults_Key];
if ([panesOrder containsObject:lastViewName] && [self loadPrefsPaneNamed:lastViewName display:NO]) {
if (shouldDisplay) {
[prefsWindow makeKeyAndOrderFront:nil];
}
return;
}
[self debugLog:[NSString stringWithFormat:@"Could not load last-used preference pane \"%@\". Trying to load another pane instead.", lastViewName]];
// Try to load each prefpane in turn if loading the last-viewed one fails.
NSEnumerator* panes = [panesOrder objectEnumerator];
NSString *pane;
while (pane = [panes nextObject]) {
if (![pane isEqualToString:lastViewName]) {
if ([self loadPrefsPaneNamed:pane display:NO]) {
if (shouldDisplay) {
[prefsWindow makeKeyAndOrderFront:nil];
}
return;
}
}
}
[self debugLog:[NSString stringWithFormat:@"Could not load any valid preference panes. The preference pane bundle extension was \"%@\" and the search path was: %@", bundleExtension, searchPath]];
// Show alert dialog.
NSString *appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleName"];
NSRunAlertPanel(@"Preferences",
[NSString stringWithFormat:@"Preferences are not available for %@.", appName],
@"OK",
nil,
nil);
[prefsWindow release];
prefsWindow = nil;
}
- (void)destroyPreferencesWindow
{
if (prefsWindow) {
[prefsWindow release];
}
prefsWindow = nil;
}
- (void)activatePane:(NSString*)path {
NSBundle* paneBundle = [NSBundle bundleWithPath:path];
if (paneBundle) {
NSDictionary* paneDict = [paneBundle infoDictionary];
NSString* paneName = [paneDict objectForKey:@"NSPrincipalClass"];
if (paneName) {
Class paneClass = NSClassFromString(paneName);
if (!paneClass) {
paneClass = [paneBundle principalClass];
if ([paneClass conformsToProtocol:@protocol(SS_PreferencePaneProtocol)] && [paneClass isKindOfClass:[NSObject class]]) {
NSArray *panes = [paneClass preferencePanes];
NSEnumerator *enumerator = [panes objectEnumerator];
id <SS_PreferencePaneProtocol> aPane;
while (aPane = [enumerator nextObject]) {
[panesOrder addObject:[aPane paneName]];
[preferencePanes setObject:aPane forKey:[aPane paneName]];
}
} else {
[self debugLog:[NSString stringWithFormat:@"Did not load bundle: %@ because its Principal Class is either not an NSObject subclass, or does not conform to the PreferencePane Protocol.", paneBundle]];
}
} else {
[self debugLog:[NSString stringWithFormat:@"Did not load bundle: %@ because its Principal Class was already used in another Preference pane.", paneBundle]];
}
} else {
[self debugLog:[NSString stringWithFormat:@"Could not obtain name of Principal Class for bundle: %@", paneBundle]];
}
} else {
[self debugLog:[NSString stringWithFormat:@"Could not initialize bundle: %@", paneBundle]];
}
}
- (BOOL)loadPreferencePaneNamed:(NSString *)name
{
return [self loadPrefsPaneNamed:(NSString *)name display:YES];
}
- (NSArray *)loadedPanes
{
if (preferencePanes) {
return [preferencePanes allKeys];
}
return nil;
}
- (BOOL)loadPrefsPaneNamed:(NSString *)name display:(BOOL)disp
{
if (!prefsWindow) {
NSBeep();
[self debugLog:[NSString stringWithFormat:@"Could not load \"%@\" preference pane because the Preferences window seems to no longer exist.", name]];
return NO;
}
id tempPane = nil;
tempPane = [preferencePanes objectForKey:name];
if (!tempPane) {
[self debugLog:[NSString stringWithFormat:@"Could not load preference pane \"%@\", because that pane does not exist.", name]];
return NO;
}
NSView *prefsView = nil;
prefsView = [tempPane paneView];
if (!prefsView) {
[self debugLog:[NSString stringWithFormat:@"Could not load \"%@\" preference pane because its view could not be loaded from the bundle.", name]];
return NO;
}
// Get rid of old view before resizing, for display purposes.
if (disp) {
NSView *tempView = [[NSView alloc] initWithFrame:[[prefsWindow contentView] frame]];
[prefsWindow setContentView:tempView];
[tempView release];
}
// Preserve upper left point of window during resize.
NSRect newFrame = [prefsWindow frame];
newFrame.size.height = [prefsView frame].size.height + ([prefsWindow frame].size.height - [[prefsWindow contentView] frame].size.height);
newFrame.size.width = [prefsView frame].size.width;
newFrame.origin.y += ([[prefsWindow contentView] frame].size.height - [prefsView frame].size.height);
id <SS_PreferencePaneProtocol> pane = [preferencePanes objectForKey:name];
[prefsWindow setShowsResizeIndicator:([pane allowsHorizontalResizing] || [pane allowsHorizontalResizing])];
[prefsWindow setFrame:newFrame display:disp animate:disp];
[prefsWindow setContentView:prefsView];
// Set appropriate resizing on window.
NSSize theSize = [prefsWindow frame].size;
theSize.height -= ToolbarHeightForWindow(prefsWindow);
[prefsWindow setMinSize:theSize];
BOOL canResize = NO;
if ([pane allowsHorizontalResizing]) {
theSize.width = FLT_MAX;
canResize = YES;
}
if ([pane allowsVerticalResizing]) {
theSize.height = FLT_MAX;
canResize = YES;
}
[prefsWindow setMaxSize:theSize];
[prefsWindow setShowsResizeIndicator:canResize];
if ((prefsToolbarItems && ([prefsToolbarItems count] > 1)) || alwaysShowsToolbar) {
[prefsWindow setTitle:[@"Preferences: " stringByAppendingString:name]];
}
// Update defaults
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:name forKey:Last_Pane_Defaults_Key];
return YES;
}
- (void)debugLog:(NSString*)msg
{
if (debug) {
NSLog(@"[--- PREFERENCES DEBUG MESSAGE ---]\r%@\r\r", msg);
}
}
// ************************************************
// Prefs Toolbar methods
// ************************************************
float ToolbarHeightForWindow(NSWindow *window)
{
NSToolbar *toolbar;
float toolbarHeight = 0.0;
NSRect windowFrame;
toolbar = [window toolbar];
if(toolbar && [toolbar isVisible])
{
windowFrame = [NSWindow contentRectForFrameRect:[window frame]
styleMask:[window styleMask]];
toolbarHeight = NSHeight(windowFrame)
- NSHeight([[window contentView] frame]);
}
return toolbarHeight;
}
- (void)createPrefsToolbar
{
// Create toolbar items
prefsToolbarItems = [[NSMutableDictionary alloc] init];
NSEnumerator *itemEnumerator = [panesOrder objectEnumerator];
NSString *name;
NSImage *itemImage;
while (name = [itemEnumerator nextObject]) {
if ([preferencePanes objectForKey:name] != nil) {
NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:name];
[item setPaletteLabel:name]; // item's label in the "Customize Toolbar" sheet (not relevant here, but we set it anyway)
[item setLabel:name]; // item's label in the toolbar
NSString *tempTip = [[preferencePanes objectForKey:name] paneToolTip];
if (!tempTip || [tempTip isEqualToString:@""]) {
[item setToolTip:nil];
} else {
[item setToolTip:tempTip];
}
itemImage = [[preferencePanes objectForKey:name] paneIcon];
[item setImage:itemImage];
[item setTarget:self];
[item setAction:@selector(prefsToolbarItemClicked:)]; // action called when item is clicked
[prefsToolbarItems setObject:item forKey:name]; // add to items
[item release];
} else {
[self debugLog:[NSString stringWithFormat:@"Could not create toolbar item for preference pane \"%@\", because that pane does not exist.", name]];
}
}
NSString *bundleIdentifier = [[NSBundle mainBundle] bundleIdentifier];
prefsToolbar = [[NSToolbar alloc] initWithIdentifier:[bundleIdentifier stringByAppendingString:@"_Preferences_Toolbar_Identifier"]];
[prefsToolbar setDelegate:self];
[prefsToolbar setAllowsUserCustomization:NO];
[prefsToolbar setAutosavesConfiguration:NO];
[prefsToolbar setDisplayMode:toolbarDisplayMode];
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
[prefsToolbar setSizeMode:toolbarSizeMode];
#endif
if ((prefsToolbarItems && ([prefsToolbarItems count] > 1)) || alwaysShowsToolbar) {
[prefsWindow setToolbar:prefsToolbar];
} else if (!alwaysShowsToolbar && prefsToolbarItems && ([prefsToolbarItems count] == 1)) {
[self debugLog:@"Not showing toolbar in Preferences window because there is only one preference pane loaded. You can override this behaviour using -[setAlwaysShowsToolbar:YES]."];
}
}
- (NSToolbarDisplayMode)toolbarDisplayMode
{
return toolbarDisplayMode;
}
- (void)setToolbarDisplayMode:(NSToolbarDisplayMode)displayMode
{
toolbarDisplayMode = displayMode;
}
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_2
- (NSToolbarSizeMode)toolbarSizeMode
{
return toolbarSizeMode;
}
- (void)setToolbarSizeMode:(NSToolbarSizeMode)sizeMode
{
toolbarSizeMode = sizeMode;
}
#endif
- (void)prefsToolbarItemClicked:(NSToolbarItem*)item
{
if (![[item itemIdentifier] isEqualToString:[prefsWindow title]]) {
[self loadPrefsPaneNamed:[item itemIdentifier] display:YES];
}
}
- (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
{
return panesOrder;
}
- (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
{
return panesOrder;
}
- (NSToolbarItem *)toolbar:(NSToolbar *)toolbar itemForItemIdentifier:(NSString *)itemIdentifier willBeInsertedIntoToolbar:(BOOL)flag
{
return [prefsToolbarItems objectForKey:itemIdentifier];
}
// ************************************************
// Accessors
// ************************************************
- (NSWindow *)preferencesWindow
{
return prefsWindow;
}
- (NSString *)bundleExtension
{
return bundleExtension;
}
- (NSString *)searchPath
{
return searchPath;
}
- (NSArray *)panesOrder
{
return panesOrder;
}
- (void)setPanesOrder:(NSArray *)newPanesOrder
{
[panesOrder removeAllObjects];
NSEnumerator *enumerator = [newPanesOrder objectEnumerator];
NSString *name;
while (name = [enumerator nextObject]) {
if ([preferencePanes objectForKey:name] != nil) {
[panesOrder addObject:name];
} else {
[self debugLog:[NSString stringWithFormat:@"Did not add preference pane \"%@\" to the toolbar ordering array, because that pane does not exist.", name]];
}
}
}
- (BOOL)debug
{
return debug;
}
- (void)setDebug:(BOOL)newDebug
{
debug = newDebug;
}
- (BOOL)usesTexturedWindow
{
return usesTexturedWindow;
}
- (void)setUsesTexturedWindow:(BOOL)newUsesTexturedWindow
{
usesTexturedWindow = newUsesTexturedWindow;
}
- (BOOL)alwaysShowsToolbar
{
return alwaysShowsToolbar;
}
- (void)setAlwaysShowsToolbar:(BOOL)newAlwaysShowsToolbar
{
alwaysShowsToolbar = newAlwaysShowsToolbar;
}
- (BOOL)alwaysOpensCentered
{
return alwaysOpensCentered;
}
- (void)setAlwaysOpensCentered:(BOOL)newAlwaysOpensCentered
{
alwaysOpensCentered = newAlwaysOpensCentered;
}
@end