693 lines
20 KiB
Objective-C
693 lines
20 KiB
Objective-C
//
|
|
// CelestiaController.m
|
|
// celestia
|
|
//
|
|
// Created by Bob Ippolito on Tue May 28 2002.
|
|
// Copyright (C) 2007, Celestia Development Team
|
|
//
|
|
|
|
#include <unistd.h>
|
|
#import "CelestiaController.h"
|
|
#import "FavoritesDrawerController.h"
|
|
#import "CelestiaOpenGLView.h"
|
|
#import "SplashScreen.h"
|
|
#import "SplashWindowController.h"
|
|
#import "EclipseFinderController.h"
|
|
#import "ScriptsController.h"
|
|
#import <OpenGL/gl.h>
|
|
#import "CGLInfo.h"
|
|
#import "ConfigSelectionWindowController.h"
|
|
#import "Migrator.h"
|
|
#import "NSWindowController_Extensions.h"
|
|
#import "Menu_Extensions.h"
|
|
|
|
#include <float.h>
|
|
|
|
@interface CelestiaController () <CelestiaWindowKeyDownNotifier, MenuCallbackNotifier>
|
|
@end
|
|
|
|
@implementation CelestiaController
|
|
|
|
static NSURL *configFilePath = nil;
|
|
static NSURL *dataDirPath = nil;
|
|
static NSURL *extraDataDirPath = nil;
|
|
|
|
static CelestiaController* firstInstance;
|
|
|
|
+(CelestiaController*) shared
|
|
{
|
|
// class method to get single shared instance
|
|
return firstInstance;
|
|
}
|
|
|
|
// Startup Methods ----------------------------------------------------------
|
|
|
|
NSString* fatalErrorMessage;
|
|
|
|
- (void)awakeFromNib
|
|
{
|
|
[super awakeFromNib];
|
|
[Migrator tryToMigrate];
|
|
|
|
if (firstInstance == nil ) firstInstance = self;
|
|
|
|
// read config file/data dir from saved
|
|
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
|
|
NSData *configFileData = [prefs objectForKey:configFilePathPrefKey];
|
|
NSData *dataDirData = [prefs objectForKey:dataDirPathPrefKey];
|
|
|
|
// access saved bookmarks
|
|
if (configFileData != nil)
|
|
{
|
|
NSError *error = nil;
|
|
NSURL *tempPath = [NSURL URLByResolvingBookmarkData:configFileData options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:nil error:&error];
|
|
if ([tempPath startAccessingSecurityScopedResource])
|
|
{
|
|
configFilePath = tempPath;
|
|
}
|
|
}
|
|
|
|
if (dataDirData != nil)
|
|
{
|
|
NSError *error = nil;
|
|
NSURL *tempPath = [NSURL URLByResolvingBookmarkData:dataDirData options:NSURLBookmarkResolutionWithSecurityScope relativeToURL:nil bookmarkDataIsStale:nil error:&error];
|
|
if ([tempPath startAccessingSecurityScopedResource])
|
|
{
|
|
dataDirPath = tempPath;
|
|
}
|
|
}
|
|
|
|
// use the default location for nil ones
|
|
if (configFilePath == nil)
|
|
configFilePath = [ConfigSelectionWindowController applicationConfig];
|
|
|
|
if (dataDirPath == nil)
|
|
dataDirPath = [ConfigSelectionWindowController applicationDataDirectory];
|
|
|
|
// add the edit configuration menu item
|
|
NSMenu *appMenu = [[[[NSApp mainMenu] itemArray] objectAtIndex:0] submenu];
|
|
[appMenu insertItem:[[NSMenuItem alloc] initWithTitle:NSLocalizedString(@"Change Configuration File", "") action:@selector(changeConfigFileLocationFromMenu) keyEquivalent:@""] atIndex:[[appMenu itemArray] count] - 1];
|
|
|
|
ready = NO;
|
|
isDirty = YES;
|
|
needsRelaunch = NO;
|
|
forceQuit = NO;
|
|
appCore = nil;
|
|
fatalErrorMessage = nil;
|
|
lastScript = nil;
|
|
|
|
[self setupResourceDirectory];
|
|
[scriptsController buildScriptMenuWithScriptDir:[extraDataDirPath path]];
|
|
|
|
// hide main window until ready
|
|
[[glView window] setAlphaValue: 0.0f]; // not [[glView window] orderOut: nil];
|
|
[[glView window] setIgnoresMouseEvents:YES];
|
|
|
|
// create appCore
|
|
appCore = [CelestiaAppCore sharedAppCore];
|
|
// check for startup failure
|
|
if (appCore == nil)
|
|
{
|
|
NSLog(@"Could not create CelestiaAppCore!");
|
|
[NSApp terminate:self];
|
|
return;
|
|
}
|
|
|
|
BOOL nosplash = [[NSUserDefaults standardUserDefaults] boolForKey:@"nosplash"];
|
|
startupCondition = [[NSConditionLock alloc] initWithCondition: 0];
|
|
|
|
if (!nosplash)
|
|
{
|
|
[splashWindowController showWindow];
|
|
}
|
|
|
|
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
|
|
// start initialization thread
|
|
BOOL result = [self startInitialization];
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
[self->splashWindowController close];
|
|
if (result)
|
|
[self finishInitialization];
|
|
else
|
|
[self initializationError];
|
|
});
|
|
});
|
|
}
|
|
|
|
- (void)setNeedsRelaunch:(BOOL)newValue
|
|
{
|
|
needsRelaunch = newValue;
|
|
}
|
|
|
|
- (void)changeConfigFileLocationFromMenu
|
|
{
|
|
[self changeConfigFileLocation:YES];
|
|
}
|
|
|
|
- (void)changeConfigFileLocation:(BOOL)cancelAllowed
|
|
{
|
|
if (configSelectionWindowController == nil) {
|
|
configSelectionWindowController = [[ConfigSelectionWindowController alloc] initWithWindowNibName:@"ConfigSelectionWindow"];
|
|
configSelectionWindowController->dataDirPath = dataDirPath;
|
|
configSelectionWindowController->configFilePath = configFilePath;
|
|
}
|
|
[configSelectionWindowController setMandatory:!cancelAllowed];
|
|
[configSelectionWindowController showWindow:self];
|
|
}
|
|
|
|
- (void) setupResourceDirectory
|
|
{
|
|
// Change directory to resource dir so Celestia can find cfg files and textures
|
|
NSFileManager *fileManager = [NSFileManager defaultManager];
|
|
[fileManager changeCurrentDirectoryPath:[dataDirPath path]];
|
|
|
|
// extra/script resources are located in application support folder, not sandboxed
|
|
NSString *supportPath = [NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES) firstObject];
|
|
NSString *extraDataDir = [supportPath stringByAppendingPathComponent:CELESTIA_RESOURCES_FOLDER];
|
|
NSString *extraDir = [extraDataDir stringByAppendingPathComponent:@"extras"];
|
|
NSString *scriptDir = [extraDataDir stringByAppendingPathComponent:CEL_SCRIPTS_FOLDER];
|
|
|
|
NSString *localeDir = [[dataDirPath path] stringByAppendingPathComponent:@"locale"];
|
|
|
|
// setup gettext localization
|
|
setlocale(LC_ALL, "");
|
|
setlocale(LC_NUMERIC, "C");
|
|
bindtextdomain("celestia", [localeDir UTF8String]);
|
|
bind_textdomain_codeset("celestia", "UTF-8");
|
|
bindtextdomain("celestia_constellations", [localeDir UTF8String]);
|
|
bind_textdomain_codeset("celestia_constellations", "UTF-8");
|
|
textdomain("celestia");
|
|
|
|
BOOL isDirectory;
|
|
BOOL exists = [fileManager fileExistsAtPath:extraDataDir isDirectory:&isDirectory];
|
|
|
|
if (exists && !isDirectory) // should be a directory but not
|
|
return;
|
|
|
|
if (!exists)
|
|
{
|
|
// create directory and subdirectories
|
|
if ([fileManager createDirectoryAtPath:extraDataDir withIntermediateDirectories:YES attributes:nil error:nil])
|
|
{
|
|
[fileManager createDirectoryAtPath:extraDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
[fileManager createDirectoryAtPath:scriptDir withIntermediateDirectories:YES attributes:nil error:nil];
|
|
}
|
|
}
|
|
|
|
if ([fileManager fileExistsAtPath:extraDataDir isDirectory:&isDirectory] && isDirectory) {
|
|
extraDataDirPath = [NSURL fileURLWithPath:extraDataDir];
|
|
}
|
|
}
|
|
|
|
- (BOOL)startInitialization
|
|
{
|
|
BOOL result = NO;
|
|
|
|
[[glView openGLContext] makeCurrentContext];
|
|
|
|
@autoreleasepool {
|
|
#ifdef DEBUG
|
|
NSDate *t = [NSDate date];
|
|
#endif
|
|
|
|
result = [appCore initSimulationWithConfigPath:[configFilePath path] extraPath:[extraDataDirPath path]];
|
|
|
|
if (!result)
|
|
{
|
|
[startupCondition lock];
|
|
[startupCondition unlockWithCondition:99];
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
NSLog(@"Init took %lf seconds\n", -[t timeIntervalSinceNow]);
|
|
#endif
|
|
[startupCondition lock];
|
|
[startupCondition unlockWithCondition:1];
|
|
}
|
|
}
|
|
|
|
[NSOpenGLContext clearCurrentContext];
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void) initializationError
|
|
{
|
|
NSAlert *alert = [[NSAlert alloc] init];
|
|
[alert setMessageText:NSLocalizedString(@"Celestia failed to load data files.", @"")];
|
|
[alert addButtonWithTitle:NSLocalizedString(@"Choose Configuration File", @"")];
|
|
[alert addButtonWithTitle:NSLocalizedString(@"Quit", @"")];
|
|
forceQuit = YES;
|
|
if ([alert runModal] == NSAlertFirstButtonReturn)
|
|
{
|
|
// choose new configuration file
|
|
[self changeConfigFileLocation:NO];
|
|
return;
|
|
}
|
|
// quit
|
|
[NSApp terminate:self];
|
|
}
|
|
|
|
- (void) fatalError: (NSString *) msg
|
|
{
|
|
// handle fatal error message from either main or loading threads
|
|
if ( msg == nil )
|
|
{
|
|
if (fatalErrorMessage == nil) return;
|
|
[splashWindowController close];
|
|
NSRunAlertPanel(NSLocalizedString(@"Fatal Error",@""), @"%@", fatalErrorMessage, nil, nil, nil);
|
|
fatalErrorMessage = nil; // user could cancel the terminate
|
|
[NSApp terminate:self];
|
|
return;
|
|
}
|
|
fatalErrorMessage = msg;
|
|
}
|
|
|
|
-(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) startGLView
|
|
{
|
|
[[glView window] setIgnoresMouseEvents:NO];
|
|
[[glView window] setAutodisplay:YES];
|
|
[[glView window] setHidesOnDeactivate: NO];
|
|
[[glView window] setFrameUsingName: @"Celestia"];
|
|
[[glView window] setAlphaValue: 1.0f];
|
|
[[glView window] setFrameAutosaveName: @"Celestia"];
|
|
if ([[glView window] canBecomeMainWindow])
|
|
[[glView window] makeMainWindow ];
|
|
[[glView window] makeFirstResponder: glView ];
|
|
[[glView window] makeKeyAndOrderFront: glView ];
|
|
[glView registerForDraggedTypes:
|
|
[NSArray arrayWithObjects: NSStringPboardType, NSFilenamesPboardType, NSURLPboardType, nil]];
|
|
[glView setNeedsDisplay:YES];
|
|
}
|
|
|
|
- (void)startRender
|
|
{
|
|
[self startGLView];
|
|
|
|
// workaround for fov problem
|
|
if (pendingUrl) [appCore goToUrl: pendingUrl];
|
|
|
|
if ([startupCondition tryLockWhenCondition: 1])
|
|
[startupCondition unlockWithCondition: 2];
|
|
}
|
|
|
|
- (void)finishInitialization
|
|
{
|
|
[[glView openGLContext] makeCurrentContext];
|
|
[glView setAASamples: [appCore aaSamples]];
|
|
[appCore initRenderer];
|
|
|
|
[self setupFavorites];
|
|
|
|
settings = [CelestiaSettings shared];
|
|
[settings setControl: self];
|
|
[settings scanForKeys: [renderPanelController window]];
|
|
[settings validateItems];
|
|
|
|
// paste URL if pending
|
|
if (pendingUrl != nil )
|
|
{
|
|
[ appCore setStartURL: pendingUrl ];
|
|
}
|
|
|
|
// Settings used to be loaded after starting simulation due to
|
|
// timezone setting requiring simulation time, but this dependency
|
|
// has been removed. In fact timezone needs to be set in order to
|
|
// correctly set the simulation time so settings loaded before starting.
|
|
[settings loadUserDefaults];
|
|
|
|
[appCore start:[NSDate date]];
|
|
|
|
ready = YES;
|
|
timer = [NSTimer timerWithTimeInterval: 0.01 target: self selector:@selector(timeDisplay) userInfo:nil repeats:YES];
|
|
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
|
|
|
|
// Threaded startup can allow app to be hidden during startup
|
|
// When this happens delay rendering until first unhide
|
|
// to prevent all sorts of problems
|
|
if (![NSApp isHidden])
|
|
[self startRender];
|
|
|
|
// run script if pending (scripts can run without rendering)
|
|
if (pendingScript != nil )
|
|
{
|
|
[self runScript: pendingScript ];
|
|
}
|
|
}
|
|
|
|
// 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 )
|
|
[self runScript: filename ];
|
|
else
|
|
pendingScript = filename;
|
|
return YES;
|
|
}
|
|
|
|
- (void) handleURLEvent:(NSAppleEventDescriptor *) event withReplyEvent:(NSAppleEventDescriptor *) replyEvent
|
|
{
|
|
if ( ready )
|
|
[ appCore goToUrl: [[event descriptorAtIndex:1] stringValue] ];
|
|
else
|
|
pendingUrl = [[event descriptorAtIndex:1] stringValue];
|
|
}
|
|
|
|
- (void)applicationDidHide:(NSNotification *)aNotification
|
|
{
|
|
ready = NO;
|
|
}
|
|
|
|
- (void)applicationWillUnhide:(NSNotification *)aNotification
|
|
{
|
|
if ( [startupCondition condition] == 0 ) return;
|
|
ready = YES;
|
|
}
|
|
|
|
- (void)applicationDidUnhide:(NSNotification *)aNotification
|
|
{
|
|
if ( [startupCondition tryLockWhenCondition: 1] )
|
|
{
|
|
[startupCondition unlock];
|
|
[self startRender];
|
|
}
|
|
}
|
|
|
|
-(BOOL)applicationShouldTerminate:(id)sender
|
|
{
|
|
if (forceQuit || needsRelaunch)
|
|
return YES;
|
|
|
|
if ( NSRunAlertPanel(NSLocalizedString(@"Quit Celestia?",@""),
|
|
NSLocalizedString(@"Are you sure you want to quit Celestia?",@""),
|
|
NSLocalizedString(@"Quit",@""),
|
|
NSLocalizedString(@"Cancel",@""),
|
|
nil) != NSAlertDefaultReturn )
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
if (timer != nil) {
|
|
[timer invalidate];
|
|
timer = nil;
|
|
}
|
|
[[CelestiaAppCore sharedAppCore] archive];
|
|
return YES;
|
|
}
|
|
|
|
- (void) applicationWillTerminate:(NSNotification *) notification
|
|
{
|
|
[settings storeUserDefaults];
|
|
|
|
[configFilePath stopAccessingSecurityScopedResource];
|
|
[dataDirPath stopAccessingSecurityScopedResource];
|
|
|
|
if (appCore != nil) {
|
|
appCore = nil;
|
|
}
|
|
|
|
// TODO: relaunch app in sandbox
|
|
// if (needsRelaunch)
|
|
// {
|
|
// [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:[[NSBundle mainBundle] bundleIdentifier] options:NSWorkspaceLaunchAsync additionalEventParamDescriptor:nil launchIdentifier:nil];
|
|
// }
|
|
}
|
|
|
|
|
|
// Window Event Handler Methods ----------------------------------------------------------
|
|
|
|
-(BOOL)windowShouldClose:(id)sender
|
|
{
|
|
[NSApp terminate:nil];
|
|
return NO;
|
|
}
|
|
|
|
- (void)resize
|
|
{
|
|
[appCore resize:[glView frame]];
|
|
isDirty = NO;
|
|
}
|
|
|
|
// Catch key events even when gl window is not key
|
|
-(void)delegateKeyDown:(NSEvent *)theEvent
|
|
{
|
|
if (ready)
|
|
[glView keyDown: theEvent];
|
|
}
|
|
|
|
// Held Key Simulation Methods ----------------------------------------------------------
|
|
|
|
-(void) keyPress:(NSInteger) code hold: (NSInteger) time
|
|
{
|
|
// start simulated key hold
|
|
keyCode = code;
|
|
keyTime = time;
|
|
[appCore keyDown: (int)keyCode ];
|
|
}
|
|
|
|
- (void) keyTick
|
|
{
|
|
if ( keyCode != 0 )
|
|
{
|
|
if ( keyTime <= 0 )
|
|
{
|
|
[ appCore keyUp: (int)keyCode];
|
|
keyCode = 0;
|
|
}
|
|
else
|
|
keyTime --;
|
|
}
|
|
}
|
|
|
|
// Display Update Management Methods ----------------------------------------------------------
|
|
|
|
- (void)setDirty
|
|
{
|
|
isDirty = YES;
|
|
}
|
|
|
|
- (void) forceDisplay
|
|
{
|
|
[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];
|
|
}
|
|
}
|
|
|
|
- (void) timeDisplay
|
|
{
|
|
// if (!ready) return;
|
|
|
|
[NSEvent stopPeriodicEvents];
|
|
|
|
// check for time to release simulated key held down
|
|
[self keyTick];
|
|
#ifdef USE_PERIODIC
|
|
// adjust timer if necessary to receive waiting appkit events
|
|
static NSEvent* lastEvent = nil;
|
|
NSEvent *nextEvent;
|
|
|
|
[NSEvent startPeriodicEventsAfterDelay: 0.0 withPeriod: 0.001 ];
|
|
nextEvent = [NSApp nextEventMatchingMask: ( NSPeriodicMask|NSAppKitDefinedMask ) untilDate: nil inMode: NSDefaultRunLoopMode dequeue: NO];
|
|
[NSEvent stopPeriodicEvents];
|
|
|
|
if ( [nextEvent type] == NSPeriodic )
|
|
{
|
|
// ignore periodic events
|
|
[NSApp discardEventsMatchingMask: NSPeriodicMask beforeEvent: nil ];
|
|
}
|
|
else
|
|
{
|
|
if ( nextEvent == lastEvent )
|
|
{
|
|
// event is still waiting, so delay firing timer to allow event to process
|
|
[timer setFireDate: [[NSDate date] addTimeInterval: 0.01 ] ];
|
|
}
|
|
else
|
|
{
|
|
lastEvent = nextEvent;
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
// force display update
|
|
[self forceDisplay];
|
|
}
|
|
|
|
- (void) runScript: (NSString*) path
|
|
{
|
|
lastScript = path;
|
|
[appCore runScript: lastScript];
|
|
}
|
|
|
|
- (IBAction) openScript: (id) sender
|
|
{
|
|
NSOpenPanel* panel = [NSOpenPanel openPanel];
|
|
[panel setAllowedFileTypes:@[@"cel", @"celx"]];
|
|
if ([panel runModal] == NSOKButton)
|
|
{
|
|
NSString *path;
|
|
path = [[panel URL] path];
|
|
[self runScript: path];
|
|
}
|
|
}
|
|
|
|
- (IBAction) rerunScript: (id) sender
|
|
{
|
|
if (lastScript) [appCore runScript: lastScript];
|
|
}
|
|
|
|
- (IBAction) back:(id)sender
|
|
{
|
|
[appCore back];
|
|
}
|
|
|
|
- (IBAction) forward:(id)sender
|
|
{
|
|
[appCore forward];
|
|
}
|
|
|
|
- (IBAction) selectSatellite:(id)sender
|
|
{
|
|
if (sender &&
|
|
[sender respondsToSelector: @selector(representedObject)] &&
|
|
[sender representedObject])
|
|
{
|
|
[[appCore simulation] setSelection: [[CelestiaSelection alloc] initWithCelestiaBody: [sender representedObject]]];
|
|
}
|
|
}
|
|
|
|
- (IBAction) showGLInfo:(id)sender
|
|
{
|
|
if (![glInfoPanel isVisible])
|
|
{
|
|
NSTextStorage *text = [glInfo textStorage];
|
|
NSMutableAttributedString *str = [[NSMutableAttributedString alloc] initWithString: [CGLInfo info]];
|
|
if (@available(macOS 10.10, *)) {
|
|
[str addAttributes:@{NSForegroundColorAttributeName : [NSColor labelColor]} range:NSMakeRange(0, [str length])];
|
|
}
|
|
[text setAttributedString: str];
|
|
}
|
|
|
|
[glInfoPanel makeKeyAndOrderFront: self];
|
|
}
|
|
|
|
- (IBAction) showInfoURL:(id)sender
|
|
{
|
|
[appCore showInfoURL];
|
|
}
|
|
|
|
- (IBAction) captureMovie: (id) sender
|
|
{
|
|
// Not supported now
|
|
NSRunAlertPanel(NSLocalizedString(@"No Movie Capture",@""), NSLocalizedString(@"Movie capture is not available in this version of Celestia.",@""),nil,nil,nil);
|
|
}
|
|
|
|
// GUI Tag Methods ----------------------------------------------------------
|
|
|
|
- (BOOL) validateMenuItem: (id) item
|
|
{
|
|
if ( [startupCondition condition] == 0 ) return NO;
|
|
if ( [item action] == nil ) return NO;
|
|
if ( [item action] != @selector(activateMenuItem:) ) return YES;
|
|
if ( [item tag] == 0 )
|
|
{
|
|
return [item isEnabled];
|
|
}
|
|
else
|
|
{
|
|
return [settings validateItem: item ];
|
|
}
|
|
}
|
|
|
|
- (IBAction) activateMenuItem: (id) item
|
|
{
|
|
NSInteger tag = [item tag];
|
|
if ( tag != 0 )
|
|
{
|
|
if ( tag < 0 ) // simulate key press and hold
|
|
{
|
|
[self keyPress: -tag hold: 2];
|
|
}
|
|
else
|
|
{
|
|
[settings actionForItem: item ];
|
|
}
|
|
[settings validateItemForTag: tag];
|
|
}
|
|
}
|
|
|
|
-(IBAction) showPanel: (id) sender
|
|
{
|
|
switch( [sender tag] )
|
|
{
|
|
case 0:
|
|
if (!browserWindowController) browserWindowController = [[BrowserWindowController alloc] init];
|
|
[browserWindowController showWindow: self];
|
|
break;
|
|
case 1:
|
|
if (!eclipseFinderController) eclipseFinderController = [[EclipseFinderController alloc] init];
|
|
[eclipseFinderController showWindow: self];
|
|
break;
|
|
}
|
|
}
|
|
|
|
- (void) showHelp: (id) sender
|
|
{
|
|
if (helpWindowController == nil)
|
|
helpWindowController = [[NSWindowController alloc] initWithWindowNibName: @"HelpWindow"];
|
|
|
|
[helpWindowController showWindow: self];
|
|
}
|
|
|
|
@end
|
|
|
|
#pragma mark -
|
|
|
|
// Solution for keyDown sent but keyUp not sent for Cmd key combos.
|
|
// Fixes the infamous Cmd+arrow "infinite spin"
|
|
@interface CelestiaApplication : NSApplication
|
|
@end
|
|
|
|
@implementation CelestiaApplication
|
|
- (void)sendEvent: (NSEvent *)aEvent
|
|
{
|
|
if ([aEvent type] == NSKeyUp)
|
|
{
|
|
[[[self mainWindow] firstResponder] tryToPerform: @selector(keyUp:)
|
|
with: aEvent];
|
|
return;
|
|
}
|
|
|
|
[super sendEvent: aEvent];
|
|
}
|
|
@end
|