CelestiaContent/src/celestia/macosx/CelestiaAppCore.mm

731 lines
19 KiB
Plaintext

//
// CelestiaCore.mm
// celestia
//
// Created by Bob Ippolito on Wed Jun 05 2002.
// Copyright (C) 2007, Celestia Development Team
//
#import "CelestiaAppCore.h"
#import "CelestiaAppCore_PrivateAPI.h"
#import "NSString_ObjCPlusPlus.h"
#import "CelestiaDestination_PrivateAPI.h"
#import "CelestiaFavorite_PrivateAPI.h"
#import "CelestiaSelection_PrivateAPI.h"
#import "CelestiaSimulation_PrivateAPI.h"
#import "CelestiaRenderer_PrivateAPI.h"
#import "Astro.h"
#import "CelestiaController.h"
#include "celestiacore.h"
#include "render.h"
#include "qtcapture.h"
#import <Carbon/Carbon.h>
class MacSettingsWatcher : public CelestiaWatcher, public RendererWatcher
{
private:
CelestiaSettings *settings;
CelestiaCore *appCore;
public:
MacSettingsWatcher(CelestiaAppCore *_appCore,
CelestiaSettings* _settings) :
CelestiaWatcher(*[_appCore appCore]), settings(_settings), appCore([_appCore appCore])
{
appCore->getRenderer()->addWatcher(this);
};
virtual ~MacSettingsWatcher()
{
appCore->getRenderer()->removeWatcher(this);
};
void notifyChange(CelestiaCore *, int flags)
{
if ( 0 != (flags & (
// CelestiaCore::LabelFlagsChanged
// | CelestiaCore::RenderFlagsChanged
CelestiaCore::VerbosityLevelChanged
| CelestiaCore::TimeZoneChanged
// | CelestiaCore::AmbientLightChanged
| CelestiaCore::FaintestChanged
)) )
{
[settings validateItems];
}
};
virtual void notifyRenderSettingsChanged(const Renderer *renderer)
{
[settings validateItems];
};
};
class MacOSXAlerter : public CelestiaCore::Alerter
{
public:
MacOSXAlerter() {};
virtual ~MacOSXAlerter() {};
virtual void fatalError(const std::string& msg)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSLog(@"alerter fatalError!");
[[CelestiaController shared] fatalError: [NSString stringWithStdString: msg] ];
NSLog(@"alerter fatalError finis");
[pool release];
}
};
class MacOSXSplashProgressNotifier : public ProgressNotifier
{
public:
MacOSXSplashProgressNotifier() {};
virtual ~MacOSXSplashProgressNotifier() {};
virtual void update(const string& msg)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
[[[CelestiaController shared] valueForKey: @"splashWindowController"] performSelector: @selector(setStatusText:) withObject: [NSString stringWithStdString: msg]];
[pool release];
};
};
class MacOSXCursorHandler : public CelestiaCore::CursorHandler
{
public:
MacOSXCursorHandler() : cursor(kThemeArrowCursor),
shape(CelestiaCore::ArrowCursor) {};
virtual ~MacOSXCursorHandler() {};
virtual void setCursorShape(CelestiaCore::CursorShape aShape)
{
ThemeCursor changedCursor;
switch (aShape)
{
case CelestiaCore::SizeVerCursor:
changedCursor =
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
kThemeResizeUpDownCursor
#else
kThemeClosedHandCursor
#endif
;
break;
case CelestiaCore::SizeHorCursor:
changedCursor =
#if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_3
kThemeResizeLeftRightCursor
#else
kThemeClosedHandCursor
#endif
;
break;
default:
changedCursor = kThemeArrowCursor;
break;
}
if (changedCursor != cursor)
{
SetThemeCursor(changedCursor);
cursor = changedCursor;
shape = aShape;
}
};
virtual CelestiaCore::CursorShape getCursorShape() const
{
return shape;
};
private:
ThemeCursor cursor;
CelestiaCore::CursorShape shape;
};
CelestiaAppCore *_sharedCelestiaAppCore;
CelestiaCore *appCore;
@implementation CelestiaAppCore
-(CelestiaCore*) appCore
{return appCore; };
-(int)toCelestiaKey:(NSEvent*)theEvent {
int celestiaKey = 0;
unichar key = [[theEvent characters] characterAtIndex: 0];
if ( ([theEvent modifierFlags] & NSNumericPadKeyMask) && (key >= '0') && (key <= '9'))
switch(key)
{
case '0':
celestiaKey = CelestiaCore::Key_NumPad0;
break;
case '1':
celestiaKey = CelestiaCore::Key_NumPad1;
break;
case '2':
celestiaKey = CelestiaCore::Key_NumPad2;
break;
case '3':
celestiaKey = CelestiaCore::Key_NumPad3;
break;
case '4':
celestiaKey = CelestiaCore::Key_NumPad4;
break;
case '5':
celestiaKey = CelestiaCore::Key_NumPad5;
break;
case '6':
celestiaKey = CelestiaCore::Key_NumPad6;
break;
case '7':
celestiaKey = CelestiaCore::Key_NumPad7;
break;
case '8':
celestiaKey = CelestiaCore::Key_NumPad8;
break;
case '9':
celestiaKey = CelestiaCore::Key_NumPad9;
break;
default: break;
}
else switch(key)
{
case NSF1FunctionKey:
celestiaKey = CelestiaCore::Key_F1;
break;
case NSF2FunctionKey:
celestiaKey = CelestiaCore::Key_F2;
break;
case NSF3FunctionKey:
celestiaKey = CelestiaCore::Key_F3;
break;
case NSF4FunctionKey:
celestiaKey = CelestiaCore::Key_F4;
break;
case NSF5FunctionKey:
celestiaKey = CelestiaCore::Key_F5;
break;
case NSF6FunctionKey:
celestiaKey = CelestiaCore::Key_F6;
break;
case NSF7FunctionKey:
celestiaKey = CelestiaCore::Key_F7;
break;
case NSF8FunctionKey:
celestiaKey = CelestiaCore::Key_F8;
break;
case NSF9FunctionKey:
celestiaKey = CelestiaCore::Key_F9;
break;
case NSF10FunctionKey:
celestiaKey = CelestiaCore::Key_F10;
break;
case NSF11FunctionKey:
celestiaKey = CelestiaCore::Key_F11;
break;
case NSF12FunctionKey:
celestiaKey = CelestiaCore::Key_F12;
break;
case NSUpArrowFunctionKey:
celestiaKey = CelestiaCore::Key_Up;
break;
case NSDownArrowFunctionKey:
celestiaKey = CelestiaCore::Key_Down;
break;
case NSLeftArrowFunctionKey:
celestiaKey = CelestiaCore::Key_Left;
break;
case NSRightArrowFunctionKey:
celestiaKey = CelestiaCore::Key_Right;
break;
case NSPageUpFunctionKey:
celestiaKey = CelestiaCore::Key_PageUp;
break;
case NSPageDownFunctionKey:
celestiaKey = CelestiaCore::Key_PageDown;
break;
case NSHomeFunctionKey:
celestiaKey = CelestiaCore::Key_Home;
break;
case NSEndFunctionKey:
celestiaKey = CelestiaCore::Key_End;
break;
case NSInsertFunctionKey:
celestiaKey = CelestiaCore::Key_Insert;
break;
default:
if ((key < 128) && (key > 33))
{
celestiaKey = (int) (key & 0x00FF);
}
break;
}
return celestiaKey;
}
-(int)toCelestiaModifiers:(unsigned int)modifiers buttons:(unsigned int)buttons {
int cModifiers = 0;
if (modifiers & NSCommandKeyMask)
cModifiers |= CelestiaCore::ControlKey;
if (modifiers & NSControlKeyMask)
cModifiers |= CelestiaCore::ControlKey;
if (modifiers & NSShiftKeyMask)
cModifiers |= CelestiaCore::ShiftKey;
if (buttons & 1)
cModifiers |= CelestiaCore::LeftButton;
if (buttons & 2)
cModifiers |= CelestiaCore::MiddleButton;
if (buttons & 4)
cModifiers |= CelestiaCore::RightButton;
return cModifiers;
}
+(void)initialize
{
_sharedCelestiaAppCore = nil;
appCore = NULL;
}
+(CelestiaAppCore *)sharedAppCore
{
if (_sharedCelestiaAppCore != nil) return _sharedCelestiaAppCore;
_sharedCelestiaAppCore = [[CelestiaAppCore alloc] init];
return _sharedCelestiaAppCore;
}
-(CelestiaAppCore *)init
{
if (_sharedCelestiaAppCore != nil) {
[[super init] release];
return _sharedCelestiaAppCore;
}
self = [super init];
appCore = NULL;
_destinations = nil;
return self;
}
-(void)archive
{
//NSLog(@"[CelestiaAppCore archive]");
[[CelestiaFavorites sharedFavorites] archive];
[[self renderer] archive];
}
- (void)dealloc
{
if (_destinations != nil) {
[_destinations release];
_destinations = nil;
}
if (appCore != NULL) {
// appCore doesn't own the custom alerter and cursor
// handler for some reason so we assume responsibility
// for cleanup
if (appCore->getAlerter())
delete appCore->getAlerter();
if (appCore->getCursorHandler())
delete appCore->getCursorHandler();
delete appCore;
appCore = NULL;
}
_sharedCelestiaAppCore = nil;
[super dealloc];
}
-(BOOL)initSimulation
{
BOOL result = NO;
appCore = new CelestiaCore();
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *confFileSetting;
std::string confFile;
NSArray *existingResourceDirsSetting;
NSArray *extrasDirsSetting;
std::vector<std::string> extrasDirs;
NSString *extrasDir = nil;
MacOSXSplashProgressNotifier progressNotifier;
if ((confFileSetting = [prefs stringForKey:@"conf"]))
{
confFile = [confFileSetting stdString];
}
if ((existingResourceDirsSetting = [prefs stringArrayForKey:@"existingResourceDirs"]))
{
NSFileManager *fm = [NSFileManager defaultManager];
BOOL isFolder = NO;
NSEnumerator *resouceDirEnum = [existingResourceDirsSetting objectEnumerator];
NSString *resourceDir = nil;
NSString *existingConfFile = nil;
while ((resourceDir = [resouceDirEnum nextObject]))
{
existingConfFile = [resourceDir stringByAppendingPathComponent:@"celestia.cfg"];
CelestiaConfig *config = ReadCelestiaConfig([existingConfFile stdString], NULL);
if (config)
{
for (vector<string>::const_iterator iter = config->extrasDirs.begin();
iter != config->extrasDirs.end(); iter++)
{
if (*iter != "")
{
extrasDir = [NSString stringWithStdString: (*iter)];
if (([fm fileExistsAtPath: extrasDir = [extrasDir stringByStandardizingPath] isDirectory: &isFolder] && isFolder) ||
[fm fileExistsAtPath: extrasDir = [resourceDir stringByAppendingPathComponent:extrasDir] isDirectory: &isFolder] && isFolder)
{
extrasDirs.push_back([extrasDir stdString]);
}
}
}
delete config;
}
else
{
if ([fm fileExistsAtPath: extrasDir = [resourceDir stringByAppendingPathComponent: @"extras"] isDirectory: &isFolder] && isFolder)
extrasDirs.push_back([extrasDir stdString]);
}
}
}
if ((extrasDirsSetting = [prefs stringArrayForKey:@"extrasDirs"]))
{
NSEnumerator *iter = [extrasDirsSetting objectEnumerator];
while ((extrasDir = [iter nextObject]))
extrasDirs.push_back([extrasDir stdString]);
}
appCore->setAlerter(new MacOSXAlerter());
appCore->setCursorHandler(new MacOSXCursorHandler());
result = appCore->initSimulation(!confFile.empty() ? &confFile : nil,
&extrasDirs,
&progressNotifier);
if (result)
{
CelestiaSettings *settings = [CelestiaSettings shared];
new MacSettingsWatcher(self, settings); // adds itself to the appCore
}
return result;
}
-(BOOL)initRenderer
{
return (BOOL)appCore->initRenderer();
}
-(void)start:(NSDate *)date
{
appCore->start([[Astro julianDate:date] doubleValue]);
}
-(void)charEntered:(char)c withModifiers:(int)modifiers
{
// moved to CelestiaOpenGLView... if (c == 127) c = 8;
appCore->charEntered(c, modifiers);
}
-(void)charEntered:(NSString *)string
{
appCore->charEntered([string UTF8String]);
}
-(void)keyDown:(int)c
{
appCore->keyDown(c);
}
-(void)keyUp:(int)c
{
appCore->keyUp(c);
}
-(void)keyDown:(int)c withModifiers:(int)m
{
appCore->keyDown(c,m);
}
-(void)keyUp:(int)c withModifiers:(int)m
{
appCore->keyUp(c,m);
}
-(void)mouseWheel:(float)motion modifiers:(int)modifiers
{
appCore->mouseWheel(motion, modifiers);
}
-(void)mouseButtonDown:(NSPoint)coord modifiers:(int)modifiers
{
appCore->mouseButtonDown(coord.x,coord.y,modifiers);
}
-(void)mouseButtonUp:(NSPoint)coord modifiers:(int)modifiers
{
appCore->mouseButtonUp(coord.x,coord.y,modifiers);
}
-(void)mouseMove:(NSPoint)coord
{
appCore->mouseMove(coord.x,coord.y);
}
-(void)mouseMove:(NSPoint)delta modifiers:(int)modifiers
{
appCore->mouseMove(delta.x,delta.y,modifiers);
}
-(void)joystickAxis:(int)axis value:(float)value
{
appCore->joystickAxis(axis,value);
}
-(void)joystickButton:(int)button state:(BOOL)state
{
appCore->joystickButton(button,(bool)state);
}
-(void)resize:(NSRect)r
{
appCore->resize((GLsizei)r.size.width,(GLsizei)r.size.height);
}
-(void)draw
{
appCore->draw();
}
-(void)tick
{
appCore->tick();
}
-(CelestiaSimulation *)simulation
{
return [[[CelestiaSimulation alloc] initWithSimulation:appCore->getSimulation()] autorelease];
}
-(CelestiaRenderer *)renderer
{
return [[[CelestiaRenderer alloc] initWithRenderer:appCore->getRenderer()] autorelease];
}
-(void)showText:(NSString *)text
{
appCore->showText([text stdString]);
}
-(void)activateFavorite:(id)fav
{
if ([fav isKindOfClass:[NSMenuItem class]])
fav = [(NSMenuItem*)fav representedObject];
appCore->activateFavorite(*[(CelestiaFavorite*)fav favorite]);
}
-(CelestiaFavorites *)favorites
{
return [CelestiaFavorites sharedFavorites];
}
-(CelestiaDestinations *)destinations
{
if (_destinations == nil || [_destinations destinations] != appCore->getDestinations()) {
if (_destinations != nil)
[_destinations release];
_destinations = [[CelestiaDestinations alloc] initWithDestinations:appCore->getDestinations()];
}
return _destinations;
}
-(NSTimeZone *)timeZone
{
NSTimeZone *bestZone=nil;
bestZone = [NSTimeZone timeZoneWithAbbreviation:[NSString stringWithStdString:appCore->getTimeZoneName()]];
if (bestZone == nil)
bestZone = [NSTimeZone timeZoneForSecondsFromGMT:appCore->getTimeZoneBias()];
return bestZone;
}
-(void)setTimeZone:(NSTimeZone *)timeZone withDate:(NSDate *)date
{
appCore->setTimeZoneBias([timeZone secondsFromGMTForDate:date]);
appCore->setTimeZoneName([[timeZone abbreviationForDate:date] stdString]);
}
-(int)textEnterMode
{
return appCore->getTextEnterMode();
}
-(void)cancelScript
{
appCore->cancelScript();
}
-(int)hudDetail
{
return appCore->getHudDetail();
}
-(void)setHudDetail:(int)hudDetail
{
appCore->setHudDetail(hudDetail);
}
-(void)back
{
appCore->back();
}
-(void)forward
{
std::vector<Url>::size_type historySize = appCore->getHistory().size();
if (historySize < 2) return;
if (appCore->getHistoryCurrent() > historySize-2) return;
appCore->forward();
}
-(NSString *) currentURL
{
CelestiaState appState;
appState.captureState(appCore);
Url currentUrl(appState, Url::CurrentVersion);
NSString *url = [ NSString stringWithStdString: currentUrl.getAsString() ];
return url;
}
-(void)goToUrl:(NSString *)url
{
appCore->goToUrl([url stdString]);
}
-(void)setStartURL:(NSString *)url
{
appCore->setStartURL([url stdString]);
}
-(unsigned int) getLocationFilter
{
return appCore->getSimulation()->getActiveObserver()->getLocationFilter();
}
-(void) setLocationFilter: (unsigned int) filter
{
appCore->getSimulation()->getActiveObserver()->setLocationFilter(filter);
}
-(void)runScript:(NSString *)fileName
{
appCore->runScript([fileName stdString]);
}
- (void) showInfoURL
{
Selection sel = appCore->getSimulation()->getSelection();
string url;
switch (sel.getType())
{
case Selection::Type_Body:
{
url = sel.body()->getInfoURL();
if (url.empty())
{
string name = sel.body()->getName();
for (int i = 0; i < (int)name.size(); i++)
name[i] = tolower(name[i]);
url = string("http://www.nineplanets.org/") + name + ".html";
}
}
break;
case Selection::Type_Star:
{
url = sel.star()->getInfoURL();
if (url.empty())
{
char name[32];
sprintf(name, "HIP%d", sel.star()->getCatalogNumber() & ~0xf0000000);
url = string("http://simbad.u-strasbg.fr/sim-id.pl?protocol=html&Ident=") + name;
}
}
break;
case Selection::Type_DeepSky:
url = sel.deepsky()->getInfoURL();
break;
case Selection::Type_Location:
break;
default:
break;
}
if (!url.empty())
{
NSString *unescUrl = [NSString stringWithStdString: url];
NSString *escUrl =
(NSString *)CFURLCreateStringByAddingPercentEscapes(NULL, (CFStringRef)unescUrl, NULL, NULL, kCFStringEncodingUTF8);
if (escUrl)
{
if ([escUrl length] > 0)
{
NSURL *theURL = [NSURL URLWithString: escUrl];
if (theURL != nil)
{
if (nil == [theURL scheme])
theURL = [NSURL fileURLWithPath: unescUrl];
if (theURL)
[[NSWorkspace sharedWorkspace] openURL: theURL];
}
}
[escUrl release];
}
}
}
-(unsigned int)aaSamples
{
return appCore->getConfig()->aaSamples;
}
-(BOOL)glExtensionIgnored:(NSString *)extension
{
CelestiaConfig *cfg = appCore->getConfig();
return std::find(cfg->ignoreGLExtensions.begin(), cfg->ignoreGLExtensions.end(), [extension stdString]) != cfg->ignoreGLExtensions.end();
}
-(void)setGLExtensionIgnored:(NSString *)extension
{
appCore->getConfig()->ignoreGLExtensions.push_back([extension stdString]);
}
- (BOOL) captureMovie: (NSString*) filename width: (int) width height: (int) height
frameRate: (float) framerate
{
MovieCapture* movieCapture = new QTCapture();
bool success = movieCapture->start([filename stdString], width, height, framerate);
if (success)
appCore->initMovieCapture(movieCapture);
else
delete movieCapture;
return success;
}
@end