From 97b3b5972cf8d60be90b1418edf3460e875e6bd5 Mon Sep 17 00:00:00 2001 From: Hank Ramsey Date: Sat, 18 Dec 2004 20:55:58 +0000 Subject: [PATCH] added lua scripting, cel urls, new settings manager, refactoring --- macosx/CelestiaAppCore.h | 14 +- macosx/CelestiaAppCore.mm | 318 +--------------- macosx/CelestiaController.h | 16 +- macosx/CelestiaController.m | 726 ++++++++++++++++++------------------ 4 files changed, 388 insertions(+), 686 deletions(-) diff --git a/macosx/CelestiaAppCore.h b/macosx/CelestiaAppCore.h index 433b14e06..e9bc6f1df 100644 --- a/macosx/CelestiaAppCore.h +++ b/macosx/CelestiaAppCore.h @@ -16,14 +16,16 @@ #import "CelestiaSimulation.h" #import "CelestiaRenderer.h" + @interface CelestiaAppCore : NSObject { CelestiaDestinations* _destinations; } +-(void*) appCore; -(int)toCelestiaKey:(NSEvent*)theEvent; -(int)toCelestiaModifiers:(unsigned int)modifiers buttons:(unsigned int)buttons; -(void)archive; +(CelestiaAppCore *)sharedAppCore; --(BOOL)initSimulation; + -(BOOL)initSimulation; -(BOOL)initRenderer; -(void)start:(NSDate *)date withTimeZone:(NSTimeZone *)timeZone; -(void)charEntered:(char)c; @@ -53,20 +55,12 @@ -(void)setContextMenuCallback:(id)cObj; -(void)back; -(void)forward; -- (NSURL *) currentURL; +- (NSString *) currentURL; -(void)goToUrl:(NSString *)url; -(unsigned int) getLocationFilter; -(void) setLocationFilter: (unsigned int) filter; -(void)runScript:(NSString *)fileName; -//- (BOOL) testSetting: (int) tag; -//- (void) handleSetting: (int) tag; --(void)validateItems; --(BOOL)validateItem:(id)item; -(void)showInfoURL; --(void)actionForItem:(id)item; --(void)validateItemForTag:(int)tag; --(void)defineKeyForItem:(id)item; --(void)addSurfaceMenu:(NSMenu*)contextMenu; -(void)keyDown:(int)c withModifiers:(int)m; -(void)keyUp:(int)c withModifiers:(int)m; diff --git a/macosx/CelestiaAppCore.mm b/macosx/CelestiaAppCore.mm index aafcf1faf..07453184a 100644 --- a/macosx/CelestiaAppCore.mm +++ b/macosx/CelestiaAppCore.mm @@ -50,6 +50,7 @@ public: } }; +/* class MacOSXWatcher : public CelestiaWatcher { private: @@ -66,6 +67,7 @@ class MacOSXWatcher : public CelestiaWatcher [celAppCore validateItems]; }; }; +*/ CelestiaAppCore *_sharedCelestiaAppCore; CelestiaCore *appCore; @@ -83,6 +85,9 @@ void ContextMenuCallback(float x,float y, Selection selection) { @implementation CelestiaAppCore +-(CelestiaCore*) appCore + {return appCore; }; + -(int)toCelestiaKey:(NSEvent*)theEvent { int celestiaKey = 0; @@ -461,10 +466,11 @@ static NSMutableDictionary* tagDict; appCore->forward(); } -- (NSURL *) currentURL +- (NSString *) currentURL { - Url currentUrl = Url(appCore, Url::Absolute); - NSURL *url = [NSURL URLWithString: [ NSString stringWithStdString: currentUrl.getAsString() ]]; + Url currentUrl = Url(appCore, Url::Absolute); +// NSURL *url = [NSURL URLWithString: [ NSString stringWithStdString: currentUrl.getAsString() ]]; + NSString *url = [ NSString stringWithStdString: currentUrl.getAsString() ]; return url; } @@ -485,6 +491,8 @@ static NSMutableDictionary* tagDict; -(void)runScript:(NSString *)fileName { + appCore->runScript([fileName cString]); +/* ifstream scriptfile([fileName cString]); CommandParser parser(scriptfile); CommandSequence* script = parser.parse(); @@ -494,8 +502,12 @@ static NSMutableDictionary* tagDict; return; } appCore->runScript(script); +*/ } +// Alternate Surface Menu + +/* moved to CelstiaSettings - (void) disableSurfaceMenu: (NSMenu*) contextMenu { NSMenuItem* surfaceItem = [ contextMenu itemWithTitle: @"Show Alternate Surface"]; @@ -604,6 +616,7 @@ static NSMutableDictionary* tagDict; } } } +*/ - (void) showInfoURL { @@ -657,304 +670,5 @@ static NSMutableDictionary* tagDict; } } - -- (int) tagForKey: (int) key -{ - int tag; - switch (key) - { - case 112: tag = 501; break; // LabelPlanets - case 109: tag = 502; break; // LabelMoons - case 119: tag = 505; break; // LabelAsteroids - case 98: tag = 500; break; // LabelStars - case 101: tag = 504; break; // LabelGalaxies - case 110: tag = 506; break; // LabelSpacecraft - case 61: tag = 503; break; // LabelConstellations - case 105: tag = 404; break; // CloudMaps - case 1: tag = 408; break; // Atmospheres - case 12: tag = 407; break; // NightMaps - case 5: tag = 410; break; // EclipseShadows - case 111: tag = 405; break; // Orbits - case 117: tag = 402; break; // Galaxies - case 47: tag = 403; break; // Diagrams - case 2: tag = 413; break; // Boundaries - case 59: tag = 406; break; // CelestialSphere - case 25: tag = 414; break; // AutoMag - case 20: tag = 415; break; // CometTails - case 11: tag = 416; break; // Markers - case 19: tag = 411; break; // StarsAsPoints - case 24: tag = 409; break; // SmoothLines - default : tag = key; break; // Special or not a setting - } - return tag; -} - -- (BOOL) validateButton: (id) item atIndex: (int) index withValue: (int) value -{ - if ( (index==0) && [item isKindOfClass: [NSPopUpButton class] ] ) - { - NSEnumerator* items = [ [item itemArray] objectEnumerator ]; - id menuItem; - while (menuItem = [items nextObject]) - { - if ( [menuItem tag] == ([item tag]+value) ) - { - [item selectItem: menuItem]; - break; - } - } - } - else - [item setState: (value == index) ? NSOnState: NSOffState ]; - return YES; -} - -- (BOOL) validateItem: (id) item -{ - int tag = [item tag]; - if ( tag <= 128 ) tag = [self tagForKey: tag ]; - if ( tag == 32 ) - { - [item setState: (fabs(appCore->getSimulation()->getTimeScale()) == 0.0 ) ? NSOnState : NSOffState ]; - } - else if ( tag <= 128 ) - { - [item setState: NSOffState ]; - } - else if ( tag >= 400 && tag < 500 ) - { - int flag = tag-400; - int renderFlags = appCore->getRenderer()->getRenderFlags(); - [item setState: ( (renderFlags & ( 1 << flag )) != 0 ) ? NSOnState : NSOffState ]; - } - else if ( tag >= 500 && tag < 600 ) - { - int flag = tag-500; - int labelMode = appCore->getRenderer()->getLabelMode(); - [item setState: ( (labelMode & ( 1 << flag )) != 0 ) ? NSOnState : NSOffState ]; - } - else if ( tag >= 600 && tag < 610 ) - { - int index = tag-600; - [item setState: [self validateAltSurface: index ] ? NSOnState: NSOffState ]; - } - else if ( tag >= 610 && tag < 620 ) - { - int index = tag-610; - [self validateButton: item atIndex: index withValue: appCore->getHudDetail() ]; - } - else if ( tag >= 620 && tag < 630 ) - { - int index = tag-620; - [self validateButton: item atIndex: index withValue: appCore->getRenderer()->getStarStyle() ]; - } - else if ( tag >= 630 && tag < 640 ) - { - int index = tag-630; - [self validateButton: item atIndex: index - withValue: appCore->getRenderer()->getGLContext()->getRenderPath() ]; - } - else if ( tag >= 640 && tag < 700 ) - { - int index = tag-640; - [self validateButton: item atIndex: index - withValue: appCore->getRenderer()->getResolution() ]; - } - else if ( tag >= 700 && tag < 800 ) - { - int flag = tag-700; - int orbitMask = appCore->getRenderer()->getOrbitMask(); - [item setState: ( (orbitMask & ( 1 << flag )) != 0 ) ? NSOnState: NSOffState ]; - } - else if ( tag >= 800 && tag < 900 ) - { - int flag = tag-800; - unsigned int locationFilter = [self getLocationFilter]; - [item setState: ( (locationFilter & ( 1 << flag )) != 0 ) ? NSOnState: NSOffState ]; - } - else if ( tag >= 900 && tag < 1000 ) - { - int index = tag-900; - switch(index) - { - case 0: - [item setFloatValue: appCore->getRenderer()->getAmbientLightLevel() ]; - break; - case 1: - [item setFloatValue: appCore->getRenderer()->getBrightnessBias()]; - break; - case 2: - if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) - { - [item setFloatValue: appCore->getSimulation()->getFaintestVisible()]; - } - else - { - [item setFloatValue: appCore->getRenderer()->getFaintestAM45deg()]; - } - break; - case 3: - [item setFloatValue: appCore->getRenderer()->getMinimumFeatureSize()]; - break; - default: break; - } - } - return YES; -} - - -- (void) selectPopUpButtonItem: (id) item withIndex: (int) index -{ - id item2 = [ tagDict objectForKey: [NSNumber numberWithInt: ([item tag]-index) ]]; - if ([item2 isKindOfClass: [NSPopUpButton class]]) - { - [item2 selectItem: item]; - }; -} - -- (void) actionForItem: (id) item -{ - int tag = [item tag]; -// NSLog(@"item action for key: %d\n",tag); - if ( tag <= 128 ) tag = [self tagForKey: tag ]; - if ( tag <= 128 ) { [ self charEntered: tag ]; return; }; - if ( tag >= 400 && tag < 500 ) - { - int flag = tag-400; - int renderFlags = appCore->getRenderer()->getRenderFlags(); - appCore->getRenderer()->setRenderFlags( renderFlags ^ ( 1 << flag ) ); - return; - } - if ( tag >= 500 && tag < 600 ) - { - int flag = tag-500; - int labelMode = appCore->getRenderer()->getLabelMode(); - appCore->getRenderer()->setLabelMode( labelMode ^ ( 1 << flag ) ); - return; - } - if ( tag >= 600 && tag < 610 ) - { - int index = tag-600; - [self setAltSurface: index ]; - return; - } - if ( tag >= 610 && tag < 620 ) - { - int index = tag-610; - appCore->setHudDetail(index); - [self selectPopUpButtonItem: item withIndex: index]; -/* - id item2 = [ tagDict objectForKey: [NSNumber numberWithInt: 610 ]]; - if ([item2 isKindOfClass: [NSPopUpButton class]]) - { - [item2 selectItem: item]; - }; -*/ - return; - } - if ( tag >= 620 && tag < 630 ) - { - int index = tag-620; - appCore->getRenderer()->setStarStyle((Renderer::StarStyle)index); - [self selectPopUpButtonItem: item withIndex: index]; - return; - } - if ( tag >= 630 && tag < 640 ) - { - int index = tag-630; - appCore->getRenderer()->getGLContext()->setRenderPath((GLContext::GLRenderPath)index); - [self selectPopUpButtonItem: item withIndex: index]; - return; - } - if ( tag >= 640 && tag < 700 ) - { - int index = tag-640; - appCore->getRenderer()->setResolution(index); - [self selectPopUpButtonItem: item withIndex: index]; - return; - } - if ( tag >= 700 && tag < 800 ) - { - int flag = tag-700; - int orbitMask = appCore->getRenderer()->getOrbitMask(); - appCore->getRenderer()->setOrbitMask( orbitMask ^ ( 1 << flag ) ); - return; - } - if ( tag >= 800 && tag < 900 ) - { - int flag = tag-800; - unsigned int locationFilter = [self getLocationFilter]; - [self setLocationFilter: locationFilter ^ ( 1 << flag ) ]; - return; - } - if ( tag >= 900 && tag < 1000 ) - { - int index = tag-900; - switch(index) - { - case 0: - appCore->getRenderer()->setAmbientLightLevel([item floatValue]); - break; - case 1: - appCore->getRenderer()->setBrightnessBias([item floatValue]); - break; - case 2: - if ((appCore->getRenderer()->getRenderFlags() & Renderer::ShowAutoMag) == 0) - { - appCore->setFaintest([item floatValue]); - } - else - { - appCore->getRenderer()->setFaintestAM45deg([item floatValue]); - appCore->setFaintestAutoMag(); - } - break; - case 3: - appCore->getRenderer()->setMinimumFeatureSize([item floatValue]); - break; - default: break; - } - return; - } -} - -- (void) defineKeyForItem: (id) item -{ - int tag = [item tag]; - if ( tag != 0 ) - { - NSNumber* itemKey = [NSNumber numberWithInt: tag ]; - if ( [ tagDict objectForKey: itemKey ] == 0 ) - { - [tagDict setObject: item forKey: itemKey]; -// NSLog(@"defining item for key: %d\n",tag); - } - } -} - - - -- (void) validateItemForTag: (int) tag -{ -//NSLog(@"validating item for key: %d\n",tag); - if ( tag <= 128 ) tag = [self tagForKey: tag ]; - id item = [ tagDict objectForKey: [NSNumber numberWithInt: tag ]]; - if ( [item isKindOfClass: [NSMenuItem class]] ) return; // auto-validated - if ( item != 0 ) - { -//NSLog(@"validating item for key: %d value: %d\n",tag,[self testSetting: [item tag] ]); - [ self validateItem: item ]; - } -} - -- (void) validateItems -{ - id obj; - NSEnumerator* enumerator = [[tagDict allKeys] objectEnumerator]; - while ((obj = [enumerator nextObject]) != nil) { - [self validateItemForTag: [obj intValue]]; - } -} - @end diff --git a/macosx/CelestiaController.h b/macosx/CelestiaController.h index 08b19fc82..f073fe8b4 100644 --- a/macosx/CelestiaController.h +++ b/macosx/CelestiaController.h @@ -5,14 +5,17 @@ // Created by Bob Ippolito on Tue May 28 2002. // Copyright (c) 2002 Chris Laurel. All rights reserved. // +#define __AIFF__ #import #import "CelestiaAppCore.h" +#import "CelestiaSettings.h" #import "FavoritesDrawerController.h" #import "RenderPanelController.h" @interface CelestiaController : NSWindowController { + CelestiaSettings* settings; CelestiaAppCore* appCore; BOOL ready; BOOL isDirty; @@ -24,32 +27,33 @@ IBOutlet FavoritesDrawerController *favoritesDrawerController; IBOutlet RenderPanelController *renderPanelController; NSTimer* timer; + volatile NSThread *computeThread; + volatile BOOL computeThreadShouldTerminate; + NSConditionLock* startupCondition; int keyCode, keyTime; + NSString *pendingScript; + NSString *pendingUrl; } -(BOOL)applicationShouldTerminate:(id)sender; -(BOOL)windowShouldClose:(id)sender; --(IBAction)showGotoObject:(id)sender; --(IBAction)gotoObject:(id)sender; -(IBAction)back:(id)sender; -(IBAction)forward:(id)sender; -(IBAction)showInfoURL:(id)sender; -(IBAction)openScript:(id)sender; -(void)setDirty; +-(void)forceDisplay; -(void)resize; -(void)startInitialization; -(void)finishInitialization; -(void)display; --(void)idle; -(void)awakeFromNib; -(void)keyPress:(int)code hold:(int)time; -(void)setupResourceDirectory; --(void)scanForKeys:(id)item; +(CelestiaController*) shared; - (void) fatalError: (NSString *) msg; - +-(void)addSurfaceMenu:(NSMenu*)contextMenu; -(BOOL)validateMenuItem:(id)item; -(IBAction)activateMenuItem:(id)item; --(IBAction)activateSwitchButton:(id)item; @end diff --git a/macosx/CelestiaController.m b/macosx/CelestiaController.m index 9ae087328..3ca9ffa92 100644 --- a/macosx/CelestiaController.m +++ b/macosx/CelestiaController.m @@ -12,104 +12,101 @@ #import "FavoritesDrawerController.h" #import "CGLInfo.h" +#include + @implementation CelestiaController --(BOOL)applicationShouldTerminate:(id)sender +static CelestiaController* firstInstance; + ++(CelestiaController*) shared { - if (timer != nil) { - [timer invalidate]; - [timer release]; - } - timer = nil; - [[CelestiaAppCore sharedAppCore] archive]; - return YES; + // class method to get single shared instance + return firstInstance; } --(BOOL)windowShouldClose:(id)sender -{ - [NSApp terminate:nil]; - return YES; -} - -- (void)resize -{ - //NSLog(@"[CelestiaController resize]"); - [appCore resize:[glView frame]]; - isDirty = NO; -} - -- (void)setDirty -{ - isDirty = YES; -} - -- (void)display -{ - if ( [startupCondition condition] != 1 ) - return; - if (!ready) - [self finishInitialization]; -// else - { - if (isDirty) [self resize]; - [appCore tick]; - [appCore draw]; - } -//else { - // [self finishInitialization]; -// } -} - --(void) keyPress:(int) code hold: (int) time -{ - keyCode = code; - keyTime = time; - [appCore keyDown: keyCode ]; -} - --(void) startGLView -{ - static bool needsStart = YES; - if (!needsStart) return; - needsStart = NO; - [[glView window] setAutodisplay:YES]; - [glView setNeedsDisplay:YES]; - [[glView window] setFrameUsingName: @"Celestia"]; - [loadingPanel orderOut: nil ]; - [[glView window] setAlphaValue: 1.0f]; - [[glView window] setFrameAutosaveName: @"Celestia"]; - [[glView window] makeFirstResponder: glView ]; - [glView registerForDraggedTypes: - [NSArray arrayWithObject: NSStringPboardType]]; -// [self scanForKeys: [renderPanelController window]]; -} - -/* -static NSMenu* savedMainMenu; - -- (void) beginLoading -{ - NSMenu* tempMenu = [[NSMenu alloc] initWithTitle: @""]; - NSMenu* tempSubmenu = [[NSMenu alloc] initWithTitle: @""]; - [tempMenu addItem: [[NSMenuItem alloc] initWithTitle: @"" action: NULL keyEquivalent: @""]]; - [tempMenu setSubmenu: tempSubmenu forItem: [tempMenu itemAtIndex: 0] ]; - [[glView window] setAlphaValue: 0.0f]; - [loadingIndicator startAnimation: nil]; - savedMainMenu = [NSApp mainMenu]; - [ NSApp setMainMenu: tempMenu ]; -// [ NSApp setAppleMenu: tempMenu ]; -} - -- (void) endLoading -{ -// [ NSApp setMainMenu: savedMainMenu ]; -} -*/ +// Startup Methods ---------------------------------------------------------- NSString* fatalErrorMessage; +- (void)awakeFromNib +{ + if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) + { + [super awakeFromNib]; + } + + if (firstInstance == nil ) firstInstance = self; + ready = NO; + isDirty = YES; + appCore = nil; + fatalErrorMessage = nil; + + [self setupResourceDirectory]; + + // hide main window until ready + [[glView window] setAlphaValue: 0.0f]; // not [[glView window] orderOut: nil]; + + // create appCore + appCore = [CelestiaAppCore sharedAppCore]; + // check for startup failure + if (appCore == nil) + { + NSLog(@"Could not create CelestiaAppCore!"); + [NSApp terminate:self]; + return; + } + + startupCondition = [[NSConditionLock alloc] initWithCondition: 0]; + + // start initialization thread + [NSThread detachNewThreadSelector: @selector(startInitialization) toTarget: self + withObject: nil]; + + // wait for completion + [self performSelectorOnMainThread: @selector(waitWhileLoading:) withObject: nil waitUntilDone: NO ]; +} + + +- (void) setupResourceDirectory +{ + NSBundle* mainBundle = [NSBundle mainBundle]; + // Change directory to resource dir so Celestia can find cfg files and textures + NSFileManager *fileManager = [NSFileManager defaultManager]; + NSString* path; + if ( [ fileManager fileExistsAtPath: path = [[[ mainBundle bundlePath ] stringByDeletingLastPathComponent] stringByAppendingPathComponent: @"CelestiaResources" ]] ) + chdir([path cString]); + else if ( [ fileManager fileExistsAtPath: path = [ @"~/Library/Application Support/CelestiaResources" stringByExpandingTildeInPath] ] ) + chdir([path cString]); + else if ( [ fileManager fileExistsAtPath: path = @"/Library/Application Support/CelestiaResources" ] ) + chdir([path cString]); + else if ( [ fileManager fileExistsAtPath: path = [[ mainBundle resourcePath ] stringByAppendingPathComponent: @"CelestiaResources" ]] ) + chdir([path cString]); + else { + NSRunAlertPanel(@"Missing Resource Directory",@"It appears that the \"CelestiaResources\" directory has not been properly installed in the correct location as indicated in the installation instructions. \n\nPlease correct this and try again.",nil,nil,nil); + chdir([[mainBundle resourcePath] cString]); + } +} + +- (void)startInitialization +{ + // initialize simulator in separate thread to allow loading indicator window while waiting + if (![appCore initSimulation]) + { + NSLog(@"Could not initSimulation!"); + [startupCondition lock]; + [startupCondition unlockWithCondition: 99]; + [NSThread exit]; + return; + } + + [startupCondition lock]; + [startupCondition unlockWithCondition: 1]; +} + + - (void) fatalError: (NSString *) msg { + // handle fatal error message from either main or loading threads if ( msg == nil ) { if (fatalErrorMessage == nil) return; @@ -121,11 +118,184 @@ NSString* fatalErrorMessage; fatalErrorMessage = [msg retain]; } -- (void)idle +- (void) waitWhileLoading: (id) obj { + // display loading indicator window while loading + static NSModalSession session = nil; - if (ready) + + if ( [startupCondition condition] == 0 ) { + if ( session != nil ) + return; + [loadingIndicator startAnimation: nil]; + // begin modal session for loading panel + session = [NSApp beginModalSessionForWindow:loadingPanel]; + // modal session runloop + for (;;) + { + if ( fatalErrorMessage != nil ) + break; + if ([NSApp runModalSession:session] != NSRunContinuesResponse) + break; + if ( [startupCondition condition] != 0 ) + break; + } + // terminate modal session for loading panel + [NSApp endModalSession:session]; + } + // check for fatal error in loading thread + [self fatalError: nil]; + // complete startup + [loadingPanel orderOut: nil ]; + [self finishInitialization]; +} + +-(void) setupFavorites +{ + NSInvocation *menuCallback; + + menuCallback = [NSInvocation invocationWithMethodSignature:[FavoritesDrawerController instanceMethodSignatureForSelector:@selector(synchronizeFavoritesMenu)]]; + [menuCallback setSelector:@selector(synchronizeFavoritesMenu)]; + [menuCallback setTarget:favoritesDrawerController]; + [[CelestiaFavorites sharedFavorites] setSynchronize:menuCallback]; + [[CelestiaFavorites sharedFavorites] synchronize]; +} + +-(void) setupRenderPanel +{ +// [[renderPanelController window] setAlphaValue: 0.8f]; +// [[renderPanelController window] setLevel: NSFloatingWindowLevel]; +// [[glView window] addChildWindow: [renderPanelController window] ordered: NSWindowAbove]; + [[renderPanelController window] setMovableByWindowBackground: YES]; + [[renderPanelController window] setHidesOnDeactivate: YES]; + [[renderPanelController window] setReleasedWhenClosed: NO]; + [[renderPanelController window] setOneShot: NO]; +// [renderPanelController finishSetup]; +} + +-(void) startGLView +{ + [[glView window] setAutodisplay:YES]; + [[glView window] setHidesOnDeactivate: NO]; + [[glView window] setFrameUsingName: @"Celestia"]; + [[glView window] setAlphaValue: 1.0f]; + [[glView window] setFrameAutosaveName: @"Celestia"]; + [[glView window] makeMainWindow ]; + [[glView window] makeFirstResponder: glView ]; + [[glView window] makeKeyAndOrderFront: glView ]; + [glView registerForDraggedTypes: + [NSArray arrayWithObjects: NSStringPboardType, NSFilenamesPboardType, NSURLPboardType, nil]]; + [glView setNeedsDisplay:YES]; +} + +- (void)finishInitialization +{ + [appCore initRenderer]; + + [self setupFavorites]; + + settings = [CelestiaSettings shared]; + [settings setControl: self]; + [settings scanForKeys: [renderPanelController window]]; + [self setupRenderPanel]; + [settings validateItems]; + + // load settings + [settings loadUserDefaults]; + + // set the simulation starting time to the current system time + [appCore start:[NSDate date] withTimeZone:[NSTimeZone defaultTimeZone]]; + + // run script if pending + if (pendingScript != nil ) + { + [appCore runScript: pendingScript ]; + } + + // paste URL if pending + if (pendingUrl != nil ) + { + [ appCore goToUrl: pendingUrl ]; + } + + ready = YES; + [self startGLView]; + + // start animation timer + timer = [[NSTimer scheduledTimerWithTimeInterval: 0.01 target: self selector:@selector(timeDisplay) userInfo:nil repeats:true] retain]; + +} + +// Application Event Handler Methods ---------------------------------------------------------- + +- (void) applicationWillFinishLaunching:(NSNotification *) notification { + [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector( handleURLEvent:withReplyEvent: ) forEventClass:kInternetEventClass andEventID:kAEGetURL]; +} + +- (BOOL) application:(NSApplication *)theApplication openFile:(NSString *)filename +{ + if ( ready ) + [appCore runScript: filename ]; + else + pendingScript = [filename retain]; + return YES; +} + +- (void) handleURLEvent:(NSAppleEventDescriptor *) event withReplyEvent:(NSAppleEventDescriptor *) replyEvent +{ + if ( ready ) + [ appCore goToUrl: [[event descriptorAtIndex:1] stringValue] ]; + else + pendingUrl = [[[event descriptorAtIndex:1] stringValue] retain]; +} + +-(BOOL)applicationShouldTerminate:(id)sender +{ + if ( NSRunAlertPanel(@"Quit Celestia?",@"Are you sure you want to quit Celestia?",@"Quit",@"Cancel",nil) != NSAlertDefaultReturn ) + { + return NO; + } + if (timer != nil) { + [timer invalidate]; + [timer release]; + } + timer = nil; + [[CelestiaAppCore sharedAppCore] archive]; + return YES; +} + +- (void) applicationWillTerminate:(NSNotification *) notification { + [settings storeUserDefaults]; +} + + +// Window Event Handler Methods ---------------------------------------------------------- + +-(BOOL)windowShouldClose:(id)sender +{ + [NSApp terminate:nil]; + return NO; +} + +- (void)resize +{ + [appCore resize:[glView frame]]; + isDirty = NO; +} + +// Held Key Simulation Methods ---------------------------------------------------------- + +-(void) keyPress:(int) code hold: (int) time +{ + // start simulated key hold + keyCode = code; + keyTime = time; + [appCore keyDown: keyCode ]; +} + +- (void) keyTick +{ if ( keyCode != 0 ) { if ( keyTime <= 0 ) @@ -135,165 +305,77 @@ NSString* fatalErrorMessage; } else keyTime --; - } - + } +} + +// Display Update Management Methods ---------------------------------------------------------- + +- (void)setDirty +{ + isDirty = YES; +} + +- (void) forceDisplay +{ + if (![glView needsDisplay]) [glView setNeedsDisplay:YES]; +} + +- (void) display +{ + // update display when required by glView (invoked from drawRect:) + if (ready) + { + if (isDirty) + [self resize]; + // render to glView + [appCore draw]; + // update scene [appCore tick]; - [glView setNeedsDisplay:YES]; + } +} + +- (void) timeDisplay +{ + if (!ready) return; + + // check for time to release simulated key held down + [self keyTick]; + + // adjust timer if necessary to avoid saturating the runloop + // otherwise appkit events may be delayed + static NSEvent* lastEvent = nil; + NSEvent* nextEvent = nil; + + nextEvent = nil; + // make sure we get some events (so we don't block checking the event queue) + [NSEvent startPeriodicEventsAfterDelay: 0.0 withPeriod: 0.001 ]; + // check for waiting events + nextEvent = [NSApp nextEventMatchingMask: ( NSPeriodicMask|NSAppKitDefined ) untilDate: nil inMode: NSDefaultRunLoopMode dequeue: NO]; + // stop generating periodic events + [NSEvent stopPeriodicEvents]; + if ( [nextEvent type] == NSPeriodic ) + { + // ignore periodic events + [NSApp discardEventsMatchingMask: NSPeriodicMask beforeEvent: nil ]; } else { - if ( [startupCondition condition] == 0 ) + if ( nextEvent == lastEvent ) + { + // event is still waiting, so delay firing timer to allow event to process + [timer setFireDate: [[NSDate date] addTimeInterval: 0.01 ] ]; + } + else { - if ( session != nil ) - return; - [loadingIndicator startAnimation: nil]; - session = [NSApp beginModalSessionForWindow:loadingPanel]; - for (;;) { - if ( fatalErrorMessage != nil ) - break; - if ([NSApp runModalSession:session] != NSRunContinuesResponse) - break; - if ( [startupCondition condition] != 0 ) - break; - } - [NSApp endModalSession:session]; + lastEvent = nextEvent; + return; } -// [glView setNeedsDisplay:YES]; -// if ( fatalErrorMessage != nil ) -// { - [self fatalError: nil]; -// return; -// } - [self startGLView]; } + // force display update + [self forceDisplay]; + } -} - - -static CelestiaController* firstInstance; - -+(CelestiaController*) shared -{ - return firstInstance; -} - -- (void)awakeFromNib -{ - if ([[self superclass] instancesRespondToSelector:@selector(awakeFromNib)]) { - [super awakeFromNib]; - } - //NSLog(@"[CelestiaController awakeFromNib]"); -// [self startInitialization]; - - if (firstInstance == nil ) firstInstance = self; - ready = NO; - isDirty = YES; - appCore = nil; - fatalErrorMessage = nil; - - [ self setupResourceDirectory ]; - NSLog(@"about to createAppCore\n"); - appCore = [CelestiaAppCore sharedAppCore]; - if (appCore == nil) - { - NSLog(@"Could not create CelestiaAppCore!"); - [NSApp terminate:self]; - return; - } - [self scanForKeys: [renderPanelController window]]; -// hide main window until ready - [[glView window] setAlphaValue: 0.0f]; -// [[glView window] orderOut: nil]; - startupCondition = [[NSConditionLock alloc] initWithCondition: 0]; - [NSThread detachNewThreadSelector: @selector(startInitialization) toTarget: self - withObject: nil]; - - timer = [[NSTimer scheduledTimerWithTimeInterval: 0.001 target: self selector:@selector(idle) userInfo:nil repeats:true] retain]; - - -} - -- (void) setupResourceDirectory -{ - // Change directory to resource dir so Celestia can find cfg files and textures - NSFileManager *fileManager = [NSFileManager defaultManager]; - NSString* path; // = [ @"~/Library/Application Support/CelestiaResources" stringByExpandingTildeInPath]; - if ( [ fileManager fileExistsAtPath: path = [ @"~/Library/Application Support/CelestiaResources" stringByExpandingTildeInPath] ] ) - chdir([path cString]); - else if ( [ fileManager fileExistsAtPath: path = [ @"/Library/Application Support/CelestiaResources" stringByExpandingTildeInPath] ] ) - chdir([path cString]); - else { - NSRunAlertPanel(@"Missing Resource Directory",@"It appears that the \"CelestiaResources\" directory was not properly installed in the correct location as indicated in the installation instructions. \n\nPlease try again and see if you can get it right this time!",nil,nil,nil); - chdir([[[NSBundle mainBundle] resourcePath] cString]); - } -} - -- (void)startInitialization -{ - //NSLog(@"[CelestiaController startInitialization]"); - if (![appCore initSimulation]) - { - NSLog(@"Could not initSimulation!"); - [startupCondition lock]; - [startupCondition unlockWithCondition: 99]; - [NSThread exit]; - return; -// [NSApp terminate:self]; -// return; - } -NSLog(@"done with initSimulation\n"); - - [startupCondition lock]; - [startupCondition unlockWithCondition: 1]; -} - -- (void)finishInitialization -{ - NSInvocation *menuCallback; - //NSLog(@"finishInitialization"); - // GL should be all set up, now initialize the renderer. - [appCore initRenderer]; - // Set the simulation starting time to the current system time - [appCore start:[NSDate date] withTimeZone:[NSTimeZone defaultTimeZone]]; - ready = YES; - menuCallback = [NSInvocation invocationWithMethodSignature:[FavoritesDrawerController instanceMethodSignatureForSelector:@selector(synchronizeFavoritesMenu)]]; - [menuCallback setSelector:@selector(synchronizeFavoritesMenu)]; - [menuCallback setTarget:favoritesDrawerController]; - [[CelestiaFavorites sharedFavorites] setSynchronize:menuCallback]; - [[CelestiaFavorites sharedFavorites] synchronize]; - // DEBUG - //NSLog(@"%@",[CGLInfo displayDescriptions]); - - [renderPanelController finishSetup]; - [appCore validateItems]; -} - -- (void)dealloc -{ - NSLog(@"[CelestiaController dealloc]"); - if (appCore != nil) { - [appCore release]; - } - appCore = nil; - if (timer != nil) { - [timer invalidate]; - [timer release]; - } - timer = nil; - [super dealloc]; -} - - -- (IBAction)gotoObject:(id)sender -{ - NSLog(@"[CelestiaController gotoObject:%@]",sender); -} - -- (IBAction)showGotoObject:(id)sender -{ - NSLog(@"[CelestiaController showGotoObject:%@]",sender); - [gotoWindow makeKeyAndOrderFront:self]; -} +// Application Action Methods ---------------------------------------------------------- - (void) openPanelDidEnd:(NSOpenPanel*)openPanel returnCode: (int) rc contextInfo: (void *) ci { @@ -301,42 +383,40 @@ NSLog(@"done with initSimulation\n"); { NSString *path; path = [openPanel filename]; - [openPanel close]; - [appCore runScript: path]; - -// NSRunAlertPanel(@"Not yet implemented",@"Stay tuned.",nil,nil,nil); + [appCore runScript: path]; } } - (IBAction) openScript: (id) sender { NSOpenPanel* panel = [NSOpenPanel openPanel]; - [ panel beginSheetForDirectory: nil + [ panel beginSheetForDirectory: nil file: nil - types: nil // @".cel" - modalForWindow: [glView window] - modalDelegate: self - didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) - contextInfo: nil + types: [NSArray arrayWithObjects: @"cel", @"celx", nil ] + modalForWindow: [glView window] + modalDelegate: self + didEndSelector: @selector(openPanelDidEnd:returnCode:contextInfo:) + contextInfo: nil ]; } - -- (IBAction)back:(id)sender +- (IBAction) back:(id)sender { [appCore back]; } -- (IBAction)forward:(id)sender +- (IBAction) forward:(id)sender { [appCore forward]; } -- (IBAction)showInfoURL:(id)sender +- (IBAction) showInfoURL:(id)sender { [appCore showInfoURL]; } +// GUI Tag Methods ---------------------------------------------------------- + - (BOOL) validateMenuItem: (id) item { if ( [startupCondition condition] == 0 ) return NO; @@ -346,7 +426,7 @@ NSLog(@"done with initSimulation\n"); } else { - return [appCore validateItem: item ]; + return [settings validateItem: item ]; } } @@ -355,128 +435,38 @@ NSLog(@"done with initSimulation\n"); int tag = [item tag]; if ( tag != 0 ) { - if ( tag < 0 ) { + if ( tag < 0 ) // simulate key press and hold + { [self keyPress: -tag hold: 2]; - } else { - [appCore actionForItem: item ]; - } - [appCore validateItemForTag: tag]; + } + else + { + [settings actionForItem: item ]; + } + [settings validateItemForTag: tag]; } } -- (IBAction) activateSwitchButton: (id) item +- (void) addSurfaceMenu: (NSMenu*) contextMenu { - int tag = [item tag]; - if ( tag != 0 ) - { - [appCore actionForItem: item]; - [appCore tick]; - [glView setNeedsDisplay:YES]; - } + [settings addSurfaceMenu: contextMenu ]; } -- (IBAction) activateMatrixButton: (id) item +// Dealloc Method ---------------------------------------------------------- + +- (void)dealloc { - item = [item selectedCell]; - int tag = [item tag]; - if ( tag != 0 ) - { - [appCore actionForItem: item]; + if (appCore != nil) { + [appCore release]; } -} - -- (void) scanForKeys: (id) item -{ -// NSLog(@"scanning item %@\n", [item description]); - - if ( [item isKindOfClass: [NSTabViewItem class]] ) - { -// NSLog(@"scanning viewitem\n"); - item = [item view]; - [ self scanForKeys: item ]; - return; + appCore = nil; + if (timer != nil) { + [timer invalidate]; + [timer release]; } - - - if ([item isKindOfClass: [NSMenuItem class]] && [item tag] != 0) - { -// NSLog(@"scanning menuItem\n"); - [appCore defineKeyForItem: item]; - [item setTarget: self]; - [item setAction: @selector(activateMenuItem:)]; - return; - } - else if ([item isKindOfClass: [NSSlider class]] && [item tag] != 0) - { -// NSLog(@"scanning cell\n"); - [appCore defineKeyForItem: item]; - [item setTarget: self]; - [item setAction: @selector(activateSwitchButton:)]; - return; - } - - if ( [item isKindOfClass: [NSTabView class]] ) - { -// NSLog(@"scanning tabview\n"); - item = [ [item tabViewItems] objectEnumerator ]; - } - else if ( [item isKindOfClass: [NSPopUpButton class]] ) - { - [appCore defineKeyForItem: item]; -// NSLog(@"scanning popupbutton\n"); - item = [ [item itemArray] objectEnumerator ]; - } - else if ([item isKindOfClass: [NSControl class]] && [item tag] != 0) - { -// NSLog(@"scanning control\n"); - [appCore defineKeyForItem: item]; - [item setTarget: self]; - [item setAction: @selector(activateSwitchButton:)]; - return; - } - else if ( [item isKindOfClass: [NSMatrix class]] ) - { -// NSLog(@"scanning matrix\n"); - item = [ [item cells] objectEnumerator ]; - } - else if ( [item isKindOfClass: [NSView class]] ) - { -// NSLog(@"scanning view\n"); -// NSLog(@"subviews items = %@\n", [item subviews]); - item = [ [item subviews] objectEnumerator ]; -// NSLog(@"view items = %@\n", item); - }; - - if ( [item isKindOfClass: [NSEnumerator class] ] ) - { -// NSLog(@"scanning array\n"); - id subitem; - while(subitem = [item nextObject]) - { -// NSLog(@"scanning array item\n"); - [ self scanForKeys: subitem ]; - } - return; - } - - if ([item isKindOfClass: [NSCell class]] && [item tag] != 0) - { -// NSLog(@"scanning cell\n"); - [appCore defineKeyForItem: item]; - [item setTarget: self]; - [item setAction: @selector(activateMatrixButton:)]; - return; - } - - if ( [item isKindOfClass: [NSWindow class]] ) - { -// NSLog(@"scanning window\n"); - item = [item contentView ]; - [ self scanForKeys: item ]; - return; - } - + timer = nil; + [super dealloc]; +} -} @end \ No newline at end of file