// // ZoomPreferences.m // ZoomCocoa // // Created by Andrew Hunter on Sun Dec 21 2003. // Copyright (c) 2003 Andrew Hunter. All rights reserved. // #import "ZoomPreferences.h" @implementation ZoomPreferences // == Preference keys == NSString* ZoomPreferencesHaveChangedNotification = @"ZoomPreferencesHaveChangedNotification"; static NSString* displayWarnings = @"DisplayWarnings"; static NSString* fatalWarnings = @"FatalWarnings"; static NSString* speakGameText = @"SpeakGameText"; static NSString* scrollbackLength = @"ScrollbackLength"; static NSString* confirmGameClose = @"ConfirmGameClose"; static NSString* keepGamesOrganised = @"KeepGamesOrganised"; static NSString* autosaveGames = @"autosaveGames"; static NSString* gameTitle = @"GameTitle"; static NSString* interpreter = @"Interpreter"; static NSString* revision = @"Revision"; static NSString* fonts = @"Fonts"; static NSString* colours = @"Colours"; static NSString* textMargin = @"TextMargin"; static NSString* useScreenFonts = @"UseScreenFonts"; static NSString* useHyphenation = @"UseHyphenation"; static NSString* useKerning = @"UseKerning"; static NSString* useLigatures = @"UseLigatures"; static NSString* organiserDirectory = @"organiserDirectory"; static NSString* foregroundColour = @"ForegroundColour"; static NSString* backgroundColour = @"BackgroundColour"; static NSString* showBorders = @"ShowBorders"; static NSString* showGlkBorders = @"ShowGlkBorders"; static NSString* showCoverPicture = @"ShowCoverPicture"; // == Global preferences == static ZoomPreferences* globalPreferences = nil; static NSLock* globalLock = nil; + (NSString*) defaultOrganiserDirectory { NSArray* docDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); NSString* res = [[docDir objectAtIndex: 0] stringByAppendingPathComponent: @"Interactive Fiction"]; return res; } + (void)initialize { NSAutoreleasePool* apool = [[NSAutoreleasePool alloc] init]; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; ZoomPreferences* defaultPrefs = [[[self class] alloc] initWithDefaultPreferences]; NSDictionary *appDefaults = [NSDictionary dictionaryWithObject: [defaultPrefs dictionary] forKey: @"ZoomGlobalPreferences"]; [defaultPrefs release]; [defaults registerDefaults: appDefaults]; globalLock = [[NSLock alloc] init]; [apool release]; } + (ZoomPreferences*) globalPreferences { [globalLock lock]; if (globalPreferences == nil) { NSDictionary* globalDict = [[NSUserDefaults standardUserDefaults] objectForKey: @"ZoomGlobalPreferences"]; if (globalDict== nil) globalPreferences = [[ZoomPreferences alloc] initWithDefaultPreferences]; else globalPreferences = [[ZoomPreferences alloc] initWithDictionary: globalDict]; // Must contain valid fonts and colours if ([globalPreferences fonts] == nil || [globalPreferences colours] == nil) { NSLog(@"Missing element in global preferences: replacing"); [globalPreferences release]; globalPreferences = [[ZoomPreferences alloc] initWithDefaultPreferences]; } } [globalLock unlock]; return globalPreferences; } // == Initialisation == - (id) init { self = [super init]; if (self) { prefLock = [[NSLock alloc] init]; prefs = [[NSMutableDictionary allocWithZone: [self zone]] init]; } return self; } static NSArray* DefaultFonts(void) { NSString* defaultFontName = @"Gill Sans"; NSString* fixedFontName = @"Courier"; NSFontManager* mgr = [NSFontManager sharedFontManager]; NSMutableArray* defaultFonts = [[NSMutableArray alloc] init]; NSFont* variableFont = [mgr fontWithFamily: defaultFontName traits: NSUnboldFontMask weight: 5 size: 12]; NSFont* fixedFont = [mgr fontWithFamily: fixedFontName traits: NSUnboldFontMask weight: 5 size: 12]; if (variableFont == nil) variableFont = [NSFont systemFontOfSize: 12]; if (fixedFont == nil) fixedFont = [NSFont userFixedPitchFontOfSize: 12]; int x; for (x=0; x<16; x++) { NSFont* thisFont = variableFont; if ((x&4)) thisFont = fixedFont; if ((x&1)) thisFont = [mgr convertFont: thisFont toHaveTrait: NSBoldFontMask]; if ((x&2)) thisFont = [mgr convertFont: thisFont toHaveTrait: NSItalicFontMask]; if ((x&4)) thisFont = [mgr convertFont: thisFont toHaveTrait: NSFixedPitchFontMask]; [defaultFonts addObject: thisFont]; } return [defaultFonts autorelease]; } static NSArray* DefaultColours(void) { NSMutableArray* defaultColours = [[NSArray arrayWithObjects: [NSColor colorWithDeviceRed: 0 green: 0 blue: 0 alpha: 1], [NSColor colorWithDeviceRed: 1 green: 0 blue: 0 alpha: 1], [NSColor colorWithDeviceRed: 0 green: 1 blue: 0 alpha: 1], [NSColor colorWithDeviceRed: 1 green: 1 blue: 0 alpha: 1], [NSColor colorWithDeviceRed: 0 green: 0 blue: 1 alpha: 1], [NSColor colorWithDeviceRed: 1 green: 0 blue: 1 alpha: 1], [NSColor colorWithDeviceRed: 0 green: 1 blue: 1 alpha: 1], [NSColor colorWithDeviceRed: 1 green: 1 blue: .8 alpha: 1], [NSColor colorWithDeviceRed: .73 green: .73 blue: .73 alpha: 1], [NSColor colorWithDeviceRed: .53 green: .53 blue: .53 alpha: 1], [NSColor colorWithDeviceRed: .26 green: .26 blue: .26 alpha: 1], nil] retain]; return [defaultColours autorelease]; } - (id) initWithDefaultPreferences { self = [self init]; if (self) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; // Defaults [prefs setObject: [NSNumber numberWithBool: NO] forKey: displayWarnings]; [prefs setObject: [NSNumber numberWithBool: NO] forKey: fatalWarnings]; [prefs setObject: [NSNumber numberWithBool: NO] forKey: speakGameText]; [prefs setObject: @"%s (%i.%.6s.%04x)" forKey: gameTitle]; [prefs setObject: [NSNumber numberWithInt: 3] forKey: interpreter]; [prefs setObject: [NSNumber numberWithInt: 'Z'] forKey: revision]; [prefs setObject: DefaultFonts() forKey: fonts]; [prefs setObject: DefaultColours() forKey: colours]; [prefs setObject: [NSNumber numberWithInt: 0] forKey: foregroundColour]; [prefs setObject: [NSNumber numberWithInt: 7] forKey: backgroundColour]; [prefs setObject: [NSNumber numberWithBool: YES] forKey: showBorders]; [prefs setObject: [NSNumber numberWithBool: YES] forKey: showGlkBorders]; [pool release]; } return self; } - (id) initWithDictionary: (NSDictionary*) dict { self = [super init]; if (self) { prefLock = [[NSLock alloc] init]; prefs = [dict mutableCopy]; // Fonts and colours will be archived if they exist NSData* fts = [prefs objectForKey: fonts]; NSData* cols = [prefs objectForKey: colours]; if ([fts isKindOfClass: [NSData class]]) { [prefs setObject: [NSUnarchiver unarchiveObjectWithData: fts] forKey: fonts]; } if ([cols isKindOfClass: [NSData class]]) { [prefs setObject: [NSUnarchiver unarchiveObjectWithData: cols] forKey: colours]; } // Verify that things are intact NSArray* newFonts = [prefs objectForKey: fonts]; NSArray* newColours = [prefs objectForKey: colours]; if (newFonts && [newFonts count] != 16) { NSLog(@"Unable to decode font block completely: using defaults"); [prefs setObject: DefaultFonts() forKey: fonts]; } if (newColours && [newColours count] != 11) { NSLog(@"Unable to decode colour block completely: using defaults"); [prefs setObject: DefaultColours() forKey: colours]; } } return self; } - (NSDictionary*) dictionary { // Fonts and colours need encoding NSMutableDictionary* newDict = [prefs mutableCopy]; NSArray* fts = [newDict objectForKey: fonts]; NSArray* cols = [newDict objectForKey: colours]; if (fts != nil) { [newDict setObject: [NSArchiver archivedDataWithRootObject: fts] forKey: fonts]; } if (cols != nil) { [newDict setObject: [NSArchiver archivedDataWithRootObject: cols] forKey: colours]; } return [newDict autorelease]; } - (void) dealloc { [prefs release]; [prefLock release]; [super dealloc]; } // Getting preferences - (BOOL) displayWarnings { [prefLock lock]; BOOL result = [[prefs objectForKey: displayWarnings] boolValue]; [prefLock unlock]; return result; } - (BOOL) fatalWarnings { [prefLock lock]; BOOL result = [[prefs objectForKey: fatalWarnings] boolValue]; [prefLock unlock]; return result; } - (BOOL) speakGameText { [prefLock lock]; BOOL result = [[prefs objectForKey: speakGameText] boolValue]; [prefLock unlock]; return result; } - (float) scrollbackLength { [prefLock lock]; float result; if ([prefs objectForKey: scrollbackLength] == nil) result = 100.0; else result = [[prefs objectForKey: scrollbackLength] floatValue]; [prefLock unlock]; return result; } - (BOOL) confirmGameClose { BOOL result = YES; [prefLock lock]; NSNumber* confirmValue = (NSNumber*)[prefs objectForKey: confirmGameClose]; if (confirmValue) result = [confirmValue boolValue]; [prefLock unlock]; return result; } - (NSString*) gameTitle { [prefLock lock]; NSString* result = [prefs objectForKey: gameTitle]; [prefLock unlock]; return result; } - (int) interpreter { [prefLock lock]; BOOL result = [[prefs objectForKey: interpreter] intValue]; [prefLock unlock]; return result; } - (unsigned char) revision { [prefLock lock]; unsigned char result = [[prefs objectForKey: revision] intValue]; [prefLock unlock]; return result; } - (NSArray*) fonts { [prefLock lock]; NSArray* result = [prefs objectForKey: fonts]; [prefLock unlock]; return result; } - (NSArray*) colours { [prefLock lock]; NSArray* result = [prefs objectForKey: colours]; [prefLock unlock]; return result; } - (NSString*) proportionalFontFamily { // Font 0 forms the prototype for this NSFont* prototypeFont = [[self fonts] objectAtIndex: 0]; return [prototypeFont familyName]; } - (NSString*) fixedFontFamily { // Font 4 forms the prototype for this NSFont* prototypeFont = [[self fonts] objectAtIndex: 4]; return [prototypeFont familyName]; } - (NSString*) symbolicFontFamily { // Font 8 forms the prototype for this NSFont* prototypeFont = [[self fonts] objectAtIndex: 8]; return [prototypeFont familyName]; } - (float) fontSize { // Font 0 forms the prototype for this NSFont* prototypeFont = [[self fonts] objectAtIndex: 0]; return [prototypeFont pointSize]; } - (NSString*) organiserDirectory { [prefLock lock]; NSString* res = [prefs objectForKey: organiserDirectory]; if (res == nil) { NSArray* docDir = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); res = [[docDir objectAtIndex: 0] stringByAppendingPathComponent: @"Interactive Fiction"]; } [prefLock unlock]; return res; } - (float) textMargin { [prefLock lock]; NSNumber* result = (NSNumber*)[prefs objectForKey: textMargin]; if (result == nil) result = [NSNumber numberWithFloat: 10.0]; [prefLock unlock]; return [result floatValue]; } - (BOOL) useScreenFonts { [prefLock lock]; NSNumber* num = [prefs objectForKey: useScreenFonts]; BOOL result; if (num) result = [num boolValue]; else result = YES; [prefLock unlock]; return result; } - (BOOL) useHyphenation { [prefLock lock]; NSNumber* num = [prefs objectForKey: useHyphenation]; BOOL result; if (num) result = [num boolValue]; else result = YES; [prefLock unlock]; return result; } - (BOOL) useKerning { [prefLock lock]; NSNumber* num = [prefs objectForKey: useKerning]; BOOL result; if (num) result = [num boolValue]; else result = YES; [prefLock unlock]; return result; } - (BOOL) useLigatures { [prefLock lock]; NSNumber* num = [prefs objectForKey: useLigatures]; BOOL result; if (num) result = [num boolValue]; else result = YES; [prefLock unlock]; return result; } - (BOOL) keepGamesOrganised { [prefLock lock]; BOOL result = [[prefs objectForKey: keepGamesOrganised] boolValue]; [prefLock unlock]; return result; } - (BOOL) autosaveGames { [prefLock lock]; NSNumber* autosave = [prefs objectForKey: autosaveGames]; BOOL result = NO; // Changed from 1.0.2beta1 as this was annoying if (autosave) result = [autosave boolValue]; [prefLock unlock]; return result; } // Setting preferences - (void) setDisplayWarnings: (BOOL) flag { [prefs setObject: [NSNumber numberWithBool: flag] forKey: displayWarnings]; [self preferencesHaveChanged]; } - (void) setFatalWarnings: (BOOL) flag { [prefs setObject: [NSNumber numberWithBool: flag] forKey: fatalWarnings]; [self preferencesHaveChanged]; } - (void) setSpeakGameText: (BOOL) flag { [prefs setObject: [NSNumber numberWithBool: flag] forKey: speakGameText]; [self preferencesHaveChanged]; } - (void) setScrollbackLength: (float) length { [prefs setObject: [NSNumber numberWithFloat: length] forKey: scrollbackLength]; [self preferencesHaveChanged]; } - (void) setConfirmGameClose: (BOOL) flag { [prefs setObject: [NSNumber numberWithBool: flag] forKey: confirmGameClose]; [self preferencesHaveChanged]; } - (void) setGameTitle: (NSString*) title { [prefs setObject: [[title copy] autorelease] forKey: gameTitle]; [self preferencesHaveChanged]; } - (void) setInterpreter: (int) inter { [prefs setObject: [NSNumber numberWithInt: inter] forKey: interpreter]; [self preferencesHaveChanged]; } - (void) setRevision: (int) rev { [prefs setObject: [NSNumber numberWithInt: rev] forKey: revision]; [self preferencesHaveChanged]; } - (void) setFonts: (NSArray*) fts { [prefs setObject: [NSArray arrayWithArray: fts] forKey: fonts]; [self preferencesHaveChanged]; } - (void) setColours: (NSArray*) cols { [prefs setObject: [NSArray arrayWithArray: cols] forKey: colours]; [self preferencesHaveChanged]; } - (void) setOrganiserDirectory: (NSString*) directory { if (directory != nil) { [prefs setObject: directory forKey: organiserDirectory]; } else { [prefs removeObjectForKey: organiserDirectory]; } [self preferencesHaveChanged]; } - (void) setKeepGamesOrganised: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: keepGamesOrganised]; [self preferencesHaveChanged]; } - (void) setAutosaveGames: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: autosaveGames]; [self preferencesHaveChanged]; } - (void) setTextMargin: (float) value { [prefs setObject: [NSNumber numberWithFloat: value] forKey: textMargin]; [self preferencesHaveChanged]; } - (void) setUseScreenFonts: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: useScreenFonts]; [self preferencesHaveChanged]; } - (void) setUseHyphenation: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: useHyphenation]; [self preferencesHaveChanged]; } - (void) setUseKerning: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: useKerning]; [self preferencesHaveChanged]; } - (void) setUseLigatures: (BOOL) value { [prefs setObject: [NSNumber numberWithBool: value] forKey: useLigatures]; [self preferencesHaveChanged]; } - (void) setFontRange: (NSRange) fontRange toFamily: (NSString*) newFontFamily { // Sets a given range of fonts to the given family float size = [self fontSize]; NSMutableArray* newFonts = [[[self fonts] mutableCopy] autorelease]; NSFontManager* mgr = [NSFontManager sharedFontManager]; int x; for (x=fontRange.location; x