From d74fb3d05559522256115d9ba22f05a2922e5adb Mon Sep 17 00:00:00 2001 From: Simon Howard Date: Sat, 26 Dec 2009 02:10:31 +0000 Subject: [PATCH] Save and restore configuration using NSUserDefaults interface. Subversion-branch: /launcher Subversion-revision: 1761 --- IWADController.h | 15 ++- IWADController.m | 180 ++++++++++++++++++++++----- IWADLocation.h | 9 ++ IWADLocation.m | 3 + LauncherManager.h | 13 +- LauncherManager.m | 43 ++++++- Resources/launcher.gorm/data.classes | 3 +- Resources/launcher.gorm/objects.gorm | Bin 83381 -> 84298 bytes 8 files changed, 226 insertions(+), 40 deletions(-) diff --git a/IWADController.h b/IWADController.h index ce7ae68..9eee584 100644 --- a/IWADController.h +++ b/IWADController.h @@ -20,15 +20,20 @@ // //----------------------------------------------------------------------------- +#ifndef LAUNCHER_IWADCONTROLLER_H +#define LAUNCHER_IWADCONTROLLER_H + #include +#include @interface IWADController : NSObject { + id iwadSelector; + id configWindow; + id chex; id doom1; id doom2; - id iwadSelector; - id configWindow; id plutonia; id tnt; } @@ -36,6 +41,12 @@ - (void) closeConfigWindow: (id)sender; - (void) openConfigWindow: (id)sender; - (NSString *) getIWADLocation; +- (void) awakeFromNib; +- (BOOL) setDropdownList; +- (void) setDropdownSelection; +- (void) saveConfig; @end +#endif /* #ifndef LAUNCHER_IWADCONTROLLER_H */ + diff --git a/IWADController.m b/IWADController.m index 415b421..88632c1 100644 --- a/IWADController.m +++ b/IWADController.m @@ -43,6 +43,16 @@ static NSString *IWADLabels[NUM_IWAD_TYPES] = @"Chex Quest" }; +static NSString *IWADFilenames[NUM_IWAD_TYPES + 1] = +{ + @"doom.wad", + @"doom2.wad", + @"tnt.wad", + @"plutonia.wad", + @"chex.wad", + @"undefined" +}; + @implementation IWADController - (void) getIWADList: (IWADLocation **) iwadList @@ -54,39 +64,6 @@ static NSString *IWADLabels[NUM_IWAD_TYPES] = iwadList[IWAD_CHEX] = self->chex; } -// Set the dropdown list to include an entry for each IWAD that has -// been configured. Returns true if at least one IWAD is configured. - -- (BOOL) setDropdownList -{ - IWADLocation *iwadList[NUM_IWAD_TYPES]; - BOOL have_wads; - id location; - unsigned int i; - unsigned int enabled_wads; - - [self getIWADList: iwadList]; - [self->iwadSelector removeAllItems]; - - enabled_wads = 0; - - for (i=0; i 0) - { - [self->iwadSelector addItemWithTitle: IWADLabels[i]]; - ++enabled_wads; - } - } - - have_wads = enabled_wads > 0; - [self->iwadSelector setEnabled: have_wads]; - - return have_wads; -} - - (IWAD) getSelectedIWAD { unsigned int i; @@ -123,6 +100,132 @@ static NSString *IWADLabels[NUM_IWAD_TYPES] = } } +- (void) setIWADConfig +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + NSUserDefaults *defaults; + NSString *key; + NSString *value; + unsigned int i; + + [self getIWADList: iwadList]; + + // Load IWAD filename paths + + defaults = [NSUserDefaults standardUserDefaults]; + + for (i=0; iiwadSelector selectItemWithTitle:IWADLabels[i]]; + break; + } + } +} + +// Set the dropdown list to include an entry for each IWAD that has +// been configured. Returns true if at least one IWAD is configured. + +- (BOOL) setDropdownList +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + BOOL have_wads; + id location; + unsigned int i; + unsigned int enabled_wads; + + // Build the new list. + + [self getIWADList: iwadList]; + [self->iwadSelector removeAllItems]; + + enabled_wads = 0; + + for (i=0; i 0) + { + [self->iwadSelector addItemWithTitle: IWADLabels[i]]; + ++enabled_wads; + } + } + + // Enable/disable the dropdown depending on whether there + // were any configured IWADs. + + have_wads = enabled_wads > 0; + [self->iwadSelector setEnabled: have_wads]; + + // Restore the old selection. + + [self setDropdownSelection]; + + return have_wads; +} + +- (void) saveConfig +{ + IWADLocation *iwadList[NUM_IWAD_TYPES]; + IWAD selectedIWAD; + NSUserDefaults *defaults; + NSString *key; + NSString *value; + unsigned int i; + + [self getIWADList: iwadList]; + + // Store all IWAD locations to user defaults. + + defaults = [NSUserDefaults standardUserDefaults]; + + for (i=0; iconfigWindow orderOut: sender]; + [self saveConfig]; [self setDropdownList]; } - (void) awakeFromNib { + // Set configuration for all IWADs from configuration file. + + [self setIWADConfig]; + // Populate the dropdown IWAD list, and open the configuration // dialog if not yet configured. - if (![self setDropdownList]) + if ([self setDropdownList]) + { + [self setDropdownSelection]; + } + else { [self openConfigWindow: nil]; } diff --git a/IWADLocation.h b/IWADLocation.h index 2bb4239..4ddfebc 100644 --- a/IWADLocation.h +++ b/IWADLocation.h @@ -20,10 +20,17 @@ // //----------------------------------------------------------------------------- +#ifndef LAUNCHER_IWADLOCATION_H +#define LAUNCHER_IWADLOCATION_H + #include +#include "IWADController.h" + @interface IWADLocation : NSObject { + IWADController *iwadController; + id locationConfigBox; } @@ -33,3 +40,5 @@ @end +#endif /* #ifndef LAUNCHER_IWADLOCATION_H */ + diff --git a/IWADLocation.m b/IWADLocation.m index 0cef72f..3f2ac18 100644 --- a/IWADLocation.m +++ b/IWADLocation.m @@ -54,6 +54,9 @@ static id WAD_TYPES[] = { filenames = [openPanel filenames]; [self setLocation: [filenames lastObject]]; + + [self->iwadController saveConfig]; + [self->iwadController setDropdownList]; } } diff --git a/LauncherManager.h b/LauncherManager.h index 161354d..11852dc 100644 --- a/LauncherManager.h +++ b/LauncherManager.h @@ -20,19 +20,28 @@ // //----------------------------------------------------------------------------- +#ifndef LAUNCHER_LAUNCHERMANAGER_H +#define LAUNCHER_LAUNCHERMANAGER_H + #include +#include #include "IWADController.h" @interface LauncherManager : NSObject { + IWADController *iwadController; + + id launcherWindow; + id commandLineArguments; id packageLabel; - id launcherWindow; - IWADController *iwadController; } - (void) launch: (id)sender; - (void) runSetup: (id)sender; +- (void) awakeFromNib; @end +#endif /* #ifndef LAUNCHER_LAUNCHERMANAGER_H */ + diff --git a/LauncherManager.m b/LauncherManager.m index c806963..3f112f1 100644 --- a/LauncherManager.m +++ b/LauncherManager.m @@ -26,25 +26,66 @@ @implementation LauncherManager +// Save configuration. Invoked when we launch the game or quit. + +- (void) saveConfig +{ + NSUserDefaults *defaults; + + // Save IWAD configuration and selected IWAD. + + [self->iwadController saveConfig]; + + // Save command line arguments. + + defaults = [NSUserDefaults standardUserDefaults]; + [defaults setObject:[self->commandLineArguments stringValue] + forKey:@"command_line_args"]; +} + +// Load configuration, invoked on startup. + +- (void) setConfig +{ + NSUserDefaults *defaults; + NSString *args; + + defaults = [NSUserDefaults standardUserDefaults]; + + args = [defaults stringForKey:@"command_line_args"]; + + if (args != nil) + { + [self->commandLineArguments setStringValue:args]; + } +} + - (void) launch: (id)sender { NSString *iwad; NSString *args; + [self saveConfig]; + iwad = [self->iwadController getIWADLocation]; args = [self->commandLineArguments stringValue]; - printf("Command line: %s %s\n", [iwad UTF8String], [args UTF8String]); + if (iwad != nil) + { + printf("Command line: %s %s\n", [iwad UTF8String], [args UTF8String]); + } } - (void) runSetup: (id)sender { + [self saveConfig]; } - (void) awakeFromNib { [self->packageLabel setStringValue: @PACKAGE_STRING]; [self->launcherWindow setTitle: @PACKAGE_NAME " Launcher"]; + [self setConfig]; } @end diff --git a/Resources/launcher.gorm/data.classes b/Resources/launcher.gorm/data.classes index 957a5c6..c4b1bef 100644 --- a/Resources/launcher.gorm/data.classes +++ b/Resources/launcher.gorm/data.classes @@ -43,7 +43,8 @@ "setButtonClicked:" ); Outlets = ( - locationConfigBox + locationConfigBox, + iwadController ); Super = NSObject; }; diff --git a/Resources/launcher.gorm/objects.gorm b/Resources/launcher.gorm/objects.gorm index f90463513a9216a48337e2458fec22b7192f36af..78aec924cdf6b6420c4764ea9fd5bed59385ea19 100644 GIT binary patch delta 7605 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 H{@3xp@Gl@& delta 6826 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