From ef959a623b0d00f3de924ba3def5515e7939b196 Mon Sep 17 00:00:00 2001 From: Kelvin Sherlock Date: Sat, 29 Aug 2020 15:42:06 -0400 Subject: [PATCH] move main window to a separate nib/controller. --- MA2ME.xcodeproj/project.pbxproj | 18 ++ MA2ME/AppDelegate.h | 2 +- MA2ME/AppDelegate.m | 320 +-------------------------- MA2ME/Base.lproj/LaunchWindow.xib | 130 +++++++++++ MA2ME/Base.lproj/MainMenu.xib | 117 +--------- MA2ME/LaunchWindowController.h | 17 ++ MA2ME/LaunchWindowController.m | 351 ++++++++++++++++++++++++++++++ 7 files changed, 522 insertions(+), 433 deletions(-) create mode 100644 MA2ME/Base.lproj/LaunchWindow.xib create mode 100644 MA2ME/LaunchWindowController.h create mode 100644 MA2ME/LaunchWindowController.m diff --git a/MA2ME.xcodeproj/project.pbxproj b/MA2ME.xcodeproj/project.pbxproj index d19dd62..9be6b64 100644 --- a/MA2ME.xcodeproj/project.pbxproj +++ b/MA2ME.xcodeproj/project.pbxproj @@ -65,6 +65,8 @@ B6BA258824E99BEB005FB8FF /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B6BA258724E99BEB005FB8FF /* main.m */; }; B6D6DE3924FAC8B500661A5F /* Preferences.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6D6DE3724FAC8B500661A5F /* Preferences.xib */; }; B6D6DE3B24FACF4F00661A5F /* Defaults.plist in Resources */ = {isa = PBXBuildFile; fileRef = B6D6DE3A24FACF4F00661A5F /* Defaults.plist */; }; + B6D6DE3E24FADF8B00661A5F /* LaunchWindow.xib in Resources */ = {isa = PBXBuildFile; fileRef = B6D6DE3C24FADF8B00661A5F /* LaunchWindow.xib */; }; + B6D6DE4124FADFAC00661A5F /* LaunchWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B6D6DE4024FADFAC00661A5F /* LaunchWindowController.m */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -137,6 +139,9 @@ B6BA258924E99BEB005FB8FF /* MA2ME.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = MA2ME.entitlements; sourceTree = ""; }; B6D6DE3824FAC8B500661A5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/Preferences.xib; sourceTree = ""; }; B6D6DE3A24FACF4F00661A5F /* Defaults.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Defaults.plist; sourceTree = ""; }; + B6D6DE3D24FADF8B00661A5F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchWindow.xib; sourceTree = ""; }; + B6D6DE3F24FADFAC00661A5F /* LaunchWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = LaunchWindowController.h; sourceTree = ""; }; + B6D6DE4024FADFAC00661A5F /* LaunchWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = LaunchWindowController.m; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -250,9 +255,12 @@ B6BA258724E99BEB005FB8FF /* main.m */, B6BA258924E99BEB005FB8FF /* MA2ME.entitlements */, B6BA258324E99BEB005FB8FF /* MainMenu.xib */, + B6D6DE3C24FADF8B00661A5F /* LaunchWindow.xib */, B6D6DE3724FAC8B500661A5F /* Preferences.xib */, B61099E524F5F230005CB652 /* MediaView.xib */, B61099E324F5F230005CB652 /* SlotView.xib */, + B6D6DE3F24FADFAC00661A5F /* LaunchWindowController.h */, + B6D6DE4024FADFAC00661A5F /* LaunchWindowController.m */, ); path = MA2ME; sourceTree = ""; @@ -318,6 +326,7 @@ B6109A2424F5F377005CB652 /* las128ex.plist in Resources */, B6109A1D24F5F377005CB652 /* space84.plist in Resources */, B6109A3F24F5F377005CB652 /* agat9.plist in Resources */, + B6D6DE3E24FADF8B00661A5F /* LaunchWindow.xib in Resources */, B6109A3124F5F377005CB652 /* apple1.plist in Resources */, B6109A3524F5F377005CB652 /* ace100.plist in Resources */, B6109A3424F5F377005CB652 /* am64.plist in Resources */, @@ -377,6 +386,7 @@ files = ( B6BA258824E99BEB005FB8FF /* main.m in Sources */, B64E15AC24EA1FD400E8AD3D /* SlotBrowserDelegate.m in Sources */, + B6D6DE4124FADFAC00661A5F /* LaunchWindowController.m in Sources */, B64E15A924EA1D5300E8AD3D /* ROMBrowserDelegate.m in Sources */, B64979C224EF6703008ABD20 /* MediaViewController.m in Sources */, B60A6E1424EE0AE2004B7EEF /* FlippedView.m in Sources */, @@ -420,6 +430,14 @@ name = Preferences.xib; sourceTree = ""; }; + B6D6DE3C24FADF8B00661A5F /* LaunchWindow.xib */ = { + isa = PBXVariantGroup; + children = ( + B6D6DE3D24FADF8B00661A5F /* Base */, + ); + name = LaunchWindow.xib; + sourceTree = ""; + }; /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ diff --git a/MA2ME/AppDelegate.h b/MA2ME/AppDelegate.h index 6483083..b61fa9f 100644 --- a/MA2ME/AppDelegate.h +++ b/MA2ME/AppDelegate.h @@ -8,7 +8,7 @@ #import -@interface AppDelegate : NSObject +@interface AppDelegate : NSObject - (IBAction)displayPreferences:(id)sender; diff --git a/MA2ME/AppDelegate.m b/MA2ME/AppDelegate.m index 25cc7b8..3597d35 100644 --- a/MA2ME/AppDelegate.m +++ b/MA2ME/AppDelegate.m @@ -9,36 +9,17 @@ #import "AppDelegate.h" #import "SlotViewController.h" #import "MediaViewController.h" +#import "LaunchWindowController.h" @interface AppDelegate () -@property (weak) IBOutlet NSWindow *window; -@property (strong) IBOutlet SlotViewController *slotController; -@property (strong) IBOutlet MediaViewController *mediaController; - - -@property (weak) IBOutlet NSView *modelView; -@property (weak) IBOutlet NSView *slotView; -@property (weak) IBOutlet NSView *mediaView; - -/* kvo */ -@property NSString *commandLine; -@property NSArray *args; - -@property NSString *mameROM; -@property BOOL mameWindow; -@property BOOL mameNoThrottle; -@property BOOL mameDebug; -@property BOOL mameSquarePixels; - -@property NSArray *browserItems; @end @implementation AppDelegate { NSWindowController *_prefs; + NSWindowController *_launcher; } -static NSString *kMyContext = @"kMyContext"; - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { // Insert code here to initialize your application @@ -56,63 +37,8 @@ static NSString *kMyContext = @"kMyContext"; [[NSUserDefaults standardUserDefaults] registerDefaults: dict]; } - - - /* My Copy of XCode/Interface Builder barfs on NSBrowser. */ - - - path = [bundle pathForResource: @"models" ofType: @"plist"]; - _browserItems = [NSArray arrayWithContentsOfFile: path]; - - NSView *view = [_window contentView]; - - NSRect frame; - NSBrowser *browser = nil; - - frame = [_modelView frame]; - browser = [[NSBrowser alloc] initWithFrame: frame]; - - [browser setMaxVisibleColumns: 2]; - //[browser setTakesTitleFromPreviousColumn: YES]; - //[browser setTitled: NO]; - [browser setAllowsEmptySelection: NO]; - [browser setDelegate: self]; - [browser setAction: @selector(modelClick:)]; - - [view addSubview: browser]; - //[browser setTitled: YES]; // NSBrowser title bug. - -#if 0 - frame = [_slotView frame]; - browser = [[NSBrowser alloc] initWithFrame: frame]; - - [browser setMaxVisibleColumns: 2]; - [browser setTakesTitleFromPreviousColumn: YES]; - [browser setTitled: NO]; - [browser setDelegate: _slotDelegate]; - //[browser setAction: @selector(modelClick:)]; - - [view addSubview: browser]; - [browser setTitled: YES]; // NSBrowser title bug. - [_slotDelegate setBrowser: browser]; -#endif - - [_slotView addSubview: [_slotController view]]; - [_mediaView addSubview: [_mediaController view]]; - - - [self addObserver: self forKeyPath: @"mameROM" options:0 context: (__bridge void * _Nullable)(kMyContext)]; - [self addObserver: self forKeyPath: @"mameWindow" options:0 context: (__bridge void * _Nullable)(kMyContext)]; - [self addObserver: self forKeyPath: @"mameSquarePixels" options:0 context: (__bridge void * _Nullable)(kMyContext)]; - [self addObserver: self forKeyPath: @"mameDebug" options:0 context: (__bridge void * _Nullable)(kMyContext)]; - [self addObserver: self forKeyPath: @"mameNoThrottle" options:0 context: (__bridge void * _Nullable)(kMyContext)]; - - [_slotController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)]; - [_mediaController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)]; - - [_mediaController bind: @"media" toObject: _slotController withKeyPath: @"media" options: 0]; - - [self buildCommandLine]; + _launcher = [LaunchWindowController new]; + [_launcher showWindow: nil]; } @@ -125,248 +51,10 @@ static NSString *kMyContext = @"kMyContext"; } --(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { - if (context == (__bridge void *)kMyContext) { - [self buildCommandLine]; - } else { - [super observeValueForKeyPath: keyPath ofObject: object change: change context: context]; - } -} - -static NSString * JoinArguments(NSArray *argv) { - - static NSCharacterSet *safe = nil; - static NSCharacterSet *unsafe = nil; - - if (!safe) { - NSString *str = - @"%+-./:=_" - @"0123456789" - @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - ; - safe = [NSCharacterSet characterSetWithCharactersInString: str]; - unsafe = [safe invertedSet]; - } - - NSMutableString *rv = [NSMutableString new]; - - - //unsigned ix = 0; - [rv appendString: @"mame"]; - for (NSString *s in argv) { - [rv appendString: @" "]; - NSUInteger l = [s length]; - - if (!l) { - [rv appendString: @"''"]; - continue; - } - - if (!CFStringFindCharacterFromSet((CFStringRef)s, (CFCharacterSetRef)unsafe, CFRangeMake(0, l), 0, NULL)) { - [rv appendString: s]; - continue; - } - - unichar *buffer = malloc(sizeof(unichar) * l); - [s getCharacters: buffer range: NSMakeRange(0, l)]; - - [rv appendString: @"'"]; - for (NSUInteger i = 0; i < l; ++i) { - unichar c = buffer[i]; - switch (c) { - case '\'': - [rv appendString: @"\\'"]; - break; - case '\\': - [rv appendString: @"\\\\"]; - break; - case 0x7f: - [rv appendString: @"\\177"]; - break; - default: { - NSString *cc; - if (c < 0x20) { - cc = [NSString stringWithFormat: @"\\%o", c]; - } else { - cc = [NSString stringWithCharacters: &c length: 1]; - } - [rv appendString: cc]; - break; - } - } - } - [rv appendString: @"'"]; - free(buffer); - } - return rv; -} - --(void)buildCommandLine { - - - if (!_mameROM) { - [self setCommandLine: @""]; - return; - } - - NSMutableArray *argv = [NSMutableArray new]; - - //[argv addObject: @"mame"]; - [argv addObject: _mameROM]; - - if (_mameDebug) [argv addObject: @"-debug"]; - if (_mameWindow) [argv addObject: @"-window"]; - - // -nounevenstretch -video soft - [argv addObject: @"-skip_gameinfo"]; - - if (_mameWindow && _mameSquarePixels) { - NSSize screen = [_slotController resolution]; - - NSString *res = [NSString stringWithFormat: @"%ux%u", (unsigned)screen.width, (unsigned)screen.height]; - NSString *aspect = [NSString stringWithFormat: @"%u:%u", (unsigned)screen.width, (unsigned)screen.height]; - - [argv addObject: @"-nomax"]; - [argv addObject: @"-nounevenstretch"]; - - if ([_mameROM hasPrefix: @"apple2gs"]) { - [argv addObject: @"-resolution"]; - [argv addObject: res]; // @"704x462"]; - [argv addObject: @"-video"]; - [argv addObject: @"soft"]; - [argv addObject: @"-aspect"]; - [argv addObject: aspect]; - } else { - [argv addObject: @"-resolution"]; - [argv addObject: res]; // @"560x384"]; - - } - } - // -speed n - // -scale n - - NSArray *tmp; - tmp = [_slotController args]; - if ([tmp count]) { - [argv addObjectsFromArray: tmp]; - } - - tmp = [_mediaController args]; - if ([tmp count]) { - [argv addObjectsFromArray: tmp]; - } - - if (_mameNoThrottle) [argv addObject: @"-nothrottle"]; - - - [self setCommandLine: JoinArguments(argv)]; //[argv componentsJoinedByString:@" "]]; - [self setArgs: argv]; -} - --(IBAction)modelClick:(id)sender { - - NSDictionary *item = [self itemForBrowser: sender]; - NSString *model = [item objectForKey: @"value"]; - - [self setMameROM: model]; - -// [self buildCommandLine]; - - [_slotController setModel: model]; -} - -#pragma mark NSBrowser - --(NSDictionary *)itemForBrowser: (NSBrowser *)browser { - - NSIndexPath *path = [browser selectionIndexPath]; - - NSArray *a = _browserItems; - NSDictionary *item = nil; - - NSUInteger l = [path length]; - for (NSUInteger i = 0; i < l; ++i) { - NSUInteger ix = [path indexAtPosition: i]; - if (ix > [a count]) return nil; - item = [a objectAtIndex: ix]; - a = [item objectForKey: @"children"]; - } - - return item; -} --(NSArray *)itemsForBrowser: (NSBrowser *)browser column: (NSInteger) column { - - NSArray *a = _browserItems; - for (unsigned i = 0; i < column; ++i) { - NSInteger ix = [browser selectedRowInColumn: i]; - if (ix < 0) return 0; - - NSDictionary *item = [a objectAtIndex: ix]; - a = [item objectForKey: @"children"]; - if (!a) return 0; - } - return a; - -} - -- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(NSInteger)row column:(NSInteger)column { - NSArray *a = [self itemsForBrowser: sender column: column]; - if (!a || row >= [a count]) return; - - NSDictionary *item = [a objectAtIndex: row]; - - NSBrowserCell *bc = (NSBrowserCell *)cell; - - [bc setStringValue: [item objectForKey: @"description"]]; - [bc setLeaf: ![item objectForKey: @"children"]]; - -} - - -- (NSString *)browser:(NSBrowser *)sender titleOfColumn:(NSInteger)column { - return column == 0 ? @"Model" : @"Submodel"; -} - -#if 0 -- (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item { - return nil; -} --(id)rootItemForBrowser:(NSBrowser *)browser { - return _browserItems; -} -#endif - -- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column { - - NSArray *a = [self itemsForBrowser: sender column: column]; - return [a count]; -} #pragma mark - IBActions -- (IBAction)launchAction:(id)sender { - if (![_args count]) return; - - NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; - - NSString *path = [defaults stringForKey: @"MamePath"]; - if (![path length]) path = @"/usr/local/bin/mame"; - - NSError *error = nil; - NSURL *url = [NSURL fileURLWithPath: path]; - - NSTask *task = [NSTask launchedTaskWithExecutableURL: url - arguments: _args - error: &error - terminationHandler: ^(NSTask *t){ - - - - - }]; - if (error) NSLog(@"launchAction: %@", error); -} - (IBAction)displayPreferences:(id)sender { if (!_prefs) { diff --git a/MA2ME/Base.lproj/LaunchWindow.xib b/MA2ME/Base.lproj/LaunchWindow.xib new file mode 100644 index 0000000..833c0f7 --- /dev/null +++ b/MA2ME/Base.lproj/LaunchWindow.xib @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MA2ME/Base.lproj/MainMenu.xib b/MA2ME/Base.lproj/MainMenu.xib index 8e9c763..a7192d8 100644 --- a/MA2ME/Base.lproj/MainMenu.xib +++ b/MA2ME/Base.lproj/MainMenu.xib @@ -3,7 +3,6 @@ - @@ -13,16 +12,7 @@ - - - - - - - - - - + @@ -690,110 +680,5 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/MA2ME/LaunchWindowController.h b/MA2ME/LaunchWindowController.h new file mode 100644 index 0000000..cf76836 --- /dev/null +++ b/MA2ME/LaunchWindowController.h @@ -0,0 +1,17 @@ +// +// LaunchWindowController.h +// MA2ME +// +// Created by Kelvin Sherlock on 8/29/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import + +NS_ASSUME_NONNULL_BEGIN + +@interface LaunchWindowController : NSWindowController + +@end + +NS_ASSUME_NONNULL_END diff --git a/MA2ME/LaunchWindowController.m b/MA2ME/LaunchWindowController.m new file mode 100644 index 0000000..b971c16 --- /dev/null +++ b/MA2ME/LaunchWindowController.m @@ -0,0 +1,351 @@ +// +// LaunchWindowController.m +// MA2ME +// +// Created by Kelvin Sherlock on 8/29/2020. +// Copyright © 2020 Kelvin Sherlock. All rights reserved. +// + +#import "LaunchWindowController.h" +#import "MediaViewController.h" +#import "SlotViewController.h" + +static NSString *kMyContext = @"kMyContext"; + + +@interface LaunchWindowController () +@property (strong) IBOutlet MediaViewController *mediaController; +@property (strong) IBOutlet SlotViewController *slotController; + +@property (weak) IBOutlet NSView *modelView; +@property (weak) IBOutlet NSView *slotView; +@property (weak) IBOutlet NSView *mediaView; + +/* kvo */ +@property NSString *commandLine; +@property NSArray *args; + +@property NSString *mameMachine; +@property BOOL mameWindow; +@property BOOL mameNoThrottle; +@property BOOL mameDebug; +@property BOOL mameSquarePixels; +@property BOOL mameNoBlur; + + +@property NSArray *browserItems; + +@end + + +@implementation LaunchWindowController + +-(NSString *)windowNibName { + return @"LaunchWindow"; +} + +- (void)windowDidLoad { + [super windowDidLoad]; + + // Implement this method to handle any initialization after your window controller's window has been loaded from its nib file. + + NSBundle *bundle = [NSBundle mainBundle]; + NSString *path; + NSWindow *window = [self window]; + + + /* My Copy of XCode/Interface Builder barfs on NSBrowser. */ + + + path = [bundle pathForResource: @"models" ofType: @"plist"]; + _browserItems = [NSArray arrayWithContentsOfFile: path]; + + NSView *view = [window contentView]; + + NSRect frame; + NSBrowser *browser = nil; + + frame = [_modelView frame]; + browser = [[NSBrowser alloc] initWithFrame: frame]; + + [browser setMaxVisibleColumns: 2]; + //[browser setTakesTitleFromPreviousColumn: YES]; + //[browser setTitled: NO]; + [browser setAllowsEmptySelection: NO]; + [browser setDelegate: self]; + [browser setAction: @selector(modelClick:)]; + + [view addSubview: browser]; + //[browser setTitled: YES]; // NSBrowser title bug. + + + [_slotView addSubview: [_slotController view]]; + [_mediaView addSubview: [_mediaController view]]; + + + [self addObserver: self forKeyPath: @"mameMachine" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + [self addObserver: self forKeyPath: @"mameWindow" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + [self addObserver: self forKeyPath: @"mameSquarePixels" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + [self addObserver: self forKeyPath: @"mameDebug" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + [self addObserver: self forKeyPath: @"mameNoThrottle" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + + [self addObserver: self forKeyPath: @"mameNoBlur" options:0 context: (__bridge void * _Nullable)(kMyContext)]; + + [_slotController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)]; + [_mediaController addObserver: self forKeyPath: @"args" options: 0 context: (__bridge void * _Nullable)(kMyContext)]; + + [_mediaController bind: @"media" toObject: _slotController withKeyPath: @"media" options: 0]; + + [self buildCommandLine]; + + +} + + +-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { + + if (context == (__bridge void *)kMyContext) { + [self buildCommandLine]; + } else { + [super observeValueForKeyPath: keyPath ofObject: object change: change context: context]; + } +} + +static NSString * JoinArguments(NSArray *argv) { + + static NSCharacterSet *safe = nil; + static NSCharacterSet *unsafe = nil; + + if (!safe) { + NSString *str = + @"%+-./:=_" + @"0123456789" + @"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + ; + safe = [NSCharacterSet characterSetWithCharactersInString: str]; + unsafe = [safe invertedSet]; + } + + NSMutableString *rv = [NSMutableString new]; + + + //unsigned ix = 0; + [rv appendString: @"mame"]; + for (NSString *s in argv) { + [rv appendString: @" "]; + NSUInteger l = [s length]; + + if (!l) { + [rv appendString: @"''"]; + continue; + } + + if (!CFStringFindCharacterFromSet((CFStringRef)s, (CFCharacterSetRef)unsafe, CFRangeMake(0, l), 0, NULL)) { + [rv appendString: s]; + continue; + } + + unichar *buffer = malloc(sizeof(unichar) * l); + [s getCharacters: buffer range: NSMakeRange(0, l)]; + + [rv appendString: @"'"]; + for (NSUInteger i = 0; i < l; ++i) { + unichar c = buffer[i]; + switch (c) { + case '\'': + [rv appendString: @"\\'"]; + break; + case '\\': + [rv appendString: @"\\\\"]; + break; + case 0x7f: + [rv appendString: @"\\177"]; + break; + default: { + NSString *cc; + if (c < 0x20) { + cc = [NSString stringWithFormat: @"\\%o", c]; + } else { + cc = [NSString stringWithCharacters: &c length: 1]; + } + [rv appendString: cc]; + break; + } + } + } + [rv appendString: @"'"]; + free(buffer); + } + return rv; +} + +-(void)buildCommandLine { + + + if (!_mameMachine) { + [self setCommandLine: @""]; + return; + } + + NSMutableArray *argv = [NSMutableArray new]; + + //[argv addObject: @"mame"]; + [argv addObject: _mameMachine]; + + if (_mameDebug) [argv addObject: @"-debug"]; + if (_mameWindow) [argv addObject: @"-window"]; + + // -nounevenstretch -video soft + [argv addObject: @"-skip_gameinfo"]; + + if (_mameWindow && _mameSquarePixels) { + NSSize screen = [_slotController resolution]; + + NSString *res = [NSString stringWithFormat: @"%ux%u", (unsigned)screen.width, (unsigned)screen.height]; + NSString *aspect = [NSString stringWithFormat: @"%u:%u", (unsigned)screen.width, (unsigned)screen.height]; + + [argv addObject: @"-nomax"]; + [argv addObject: @"-nounevenstretch"]; + + [argv addObject: @"-resolution"]; + [argv addObject: res]; + + [argv addObject: @"-aspect"]; + [argv addObject: aspect]; + + if (_mameNoBlur) { + [argv addObject: @"-video"]; + [argv addObject: @"soft"]; + } + } + // -speed n + // -scale n + + NSArray *tmp; + tmp = [_slotController args]; + if ([tmp count]) { + [argv addObjectsFromArray: tmp]; + } + + tmp = [_mediaController args]; + if ([tmp count]) { + [argv addObjectsFromArray: tmp]; + } + + if (_mameNoThrottle) [argv addObject: @"-nothrottle"]; + + + [self setCommandLine: JoinArguments(argv)]; //[argv componentsJoinedByString:@" "]]; + [self setArgs: argv]; +} + +# pragma mark - IBActions + +-(IBAction)modelClick:(id)sender { + + NSDictionary *item = [self itemForBrowser: sender]; + NSString *model = [item objectForKey: @"value"]; + + [self setMameMachine: model]; + +// [self buildCommandLine]; + + [_slotController setModel: model]; +} + + +- (IBAction)launchAction:(id)sender { + + if (![_args count]) return; + + NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; + + NSString *path = [defaults stringForKey: @"MamePath"]; + if (![path length]) path = @"/usr/local/bin/mame"; + + NSError *error = nil; + NSURL *url = [NSURL fileURLWithPath: path]; + + NSTask *task = [NSTask new]; + [task setExecutableURL: url]; + [task setArguments: _args]; + [task setTerminationHandler: ^(NSTask *t){ + + }]; + // set stderr/stdout... + [task launchAndReturnError: &error]; + + + if (error) NSLog(@"launchAction: %@", error); + +} + +#pragma mark - NSBrowser + +-(NSDictionary *)itemForBrowser: (NSBrowser *)browser { + + NSIndexPath *path = [browser selectionIndexPath]; + + NSArray *a = _browserItems; + NSDictionary *item = nil; + + NSUInteger l = [path length]; + for (NSUInteger i = 0; i < l; ++i) { + NSUInteger ix = [path indexAtPosition: i]; + if (ix > [a count]) return nil; + item = [a objectAtIndex: ix]; + a = [item objectForKey: @"children"]; + } + + return item; +} +-(NSArray *)itemsForBrowser: (NSBrowser *)browser column: (NSInteger) column { + + NSArray *a = _browserItems; + for (unsigned i = 0; i < column; ++i) { + NSInteger ix = [browser selectedRowInColumn: i]; + if (ix < 0) return 0; + + NSDictionary *item = [a objectAtIndex: ix]; + a = [item objectForKey: @"children"]; + if (!a) return 0; + } + return a; + +} + +- (void)browser:(NSBrowser *)sender willDisplayCell:(id)cell atRow:(NSInteger)row column:(NSInteger)column { + NSArray *a = [self itemsForBrowser: sender column: column]; + if (!a || row >= [a count]) return; + + NSDictionary *item = [a objectAtIndex: row]; + + NSBrowserCell *bc = (NSBrowserCell *)cell; + + [bc setStringValue: [item objectForKey: @"description"]]; + [bc setLeaf: ![item objectForKey: @"children"]]; + +} + + +- (NSString *)browser:(NSBrowser *)sender titleOfColumn:(NSInteger)column { + return column == 0 ? @"Model" : @"Submodel"; +} + +#if 0 +- (id)browser:(NSBrowser *)browser child:(NSInteger)index ofItem:(id)item { + return nil; +} +-(id)rootItemForBrowser:(NSBrowser *)browser { + return _browserItems; +} +#endif + +- (NSInteger)browser:(NSBrowser *)sender numberOfRowsInColumn:(NSInteger)column { + + NSArray *a = [self itemsForBrowser: sender column: column]; + return [a count]; +} + + +@end