// // BrowserWindowController.mm // celestia // // Created by Hank Ramsey on Tue Dec 28 2004. // Copyright (c) 2002 Chris Laurel. All rights reserved. // Modifications Copyright (c) 2004 Hank Ramsey. All rights reserved. // #import "BrowserWindowController.h" #import "BrowserItem.h" #import "NSString_ObjCPlusPlus.h" #import "CelestiaAppCore.h" #import "CelestiaStar_PrivateAPI.h" #import "CelestiaBody_PrivateAPI.h" #import "CelestiaDSO.h" #import "CelestiaLocation.h" #include "celestiacore.h" #include "celestia.h" #include "selection.h" #include "starbrowser.h" #define BROWSER_MAX_DSO_COUNT 500 #define BROWSER_MAX_STAR_COUNT 100 @interface BrowserWindowController(Private) - (NSDictionary *) deepSkyObjects; - (NSDictionary *) starsOfKind: (int) kind; - (BrowserItem *) sol; @end @implementation BrowserWindowController static NSDictionary *rootItems; static CelestiaCore *appCore; - (id) init { self = [super initWithWindowNibName: @"BrowserWindow" ]; if (self) { appCore = (CelestiaCore*) [[CelestiaAppCore sharedAppCore] appCore]; rootId = @"solarSystem"; } return self; } - (void) windowDidLoad { if ([self window]==nil) NSLog(@"loaded browser window is nil"); } //-------------------------------------------------------------- - (NSDictionary *) deepSkyObjects { int objCount; int i = 0; NSMutableDictionary *result; NSMutableDictionary *tempDict; NSDictionary *typeMap; NSMutableDictionary *group; DSODatabase* catalog = appCore->getSimulation()->getUniverse()->getDSOCatalog(); DeepSkyObject *obj; CelestiaDSO *dsoWrapper; NSString *name; NSString *type; objCount = catalog->size(); if (objCount > BROWSER_MAX_DSO_COUNT) objCount = BROWSER_MAX_DSO_COUNT; typeMap = [[NSDictionary alloc] initWithObjectsAndKeys: NSLocalizedString(@"Galaxies (Barred Spiral)",@""), @"SB", NSLocalizedString(@"Galaxies (Spiral)",@""), @"S", NSLocalizedString(@"Galaxies (Elliptical)",@""), @"E", NSLocalizedString(@"Galaxies (Irregular)",@""), @"Irr", NSLocalizedString(@"Nebulae",@""), @"Neb", NSLocalizedString(@"Open Clusters",@""), @"Clust", NSLocalizedString(@"Unknown",@""), @"Unknown", nil]; tempDict = [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSMutableDictionary dictionary], @"SB", [NSMutableDictionary dictionary], @"S", [NSMutableDictionary dictionary], @"E", [NSMutableDictionary dictionary], @"Irr", [NSMutableDictionary dictionary], @"Neb", [NSMutableDictionary dictionary], @"Clust", [NSMutableDictionary dictionary], @"Unknown", nil]; result = [NSMutableDictionary dictionaryWithCapacity: [tempDict count]]; for (; i < objCount; ++i) { obj = catalog->getDSO(i); if (obj) { dsoWrapper = [[[CelestiaDSO alloc] initWithDSO: obj] autorelease]; name = [NSString stringWithStdString: catalog->getDSOName(obj)]; type = [dsoWrapper type]; if ([type hasPrefix: @"SB"]) group = [tempDict objectForKey: @"SB"]; else if ([type hasPrefix: @"S"]) group = [tempDict objectForKey: @"S"]; else if ([type hasPrefix: @"E"]) group = [tempDict objectForKey: @"E"]; else if ([type hasPrefix: @"Irr"]) group = [tempDict objectForKey: @"Irr"]; else if ([type hasPrefix: @"Neb"]) group = [tempDict objectForKey: @"Neb"]; else if ([type hasPrefix: @"Clust"]) group = [tempDict objectForKey: @"Clust"]; else group = [tempDict objectForKey: @"Unknown"]; [group setObject: [[[BrowserItem alloc] initWithCelestiaDSO: dsoWrapper] autorelease] forKey: name]; } } NSEnumerator *tempEnum = [tempDict keyEnumerator]; while ((type = [tempEnum nextObject])) { if ([[tempDict objectForKey: type] count] > 0) { [result setObject: [[[BrowserItem alloc] initWithName: [typeMap objectForKey: type] children: [tempDict objectForKey: type]] autorelease] forKey: [typeMap objectForKey: type]]; } } [typeMap release]; return result; } - (BrowserItem *) sol { Selection sol = appCore->getSimulation()->getUniverse()->find("Sol"); return [[[BrowserItem alloc] initWithCelestiaStar: [[[CelestiaStar alloc] initWithStar: sol.star()] autorelease]] autorelease]; } - (NSDictionary *) starsOfKind: (int) kind { std::vector* nearStars; int starCount = 0; Simulation *sim = appCore->getSimulation(); StarBrowser* sb = new StarBrowser(sim,kind); nearStars = sb->listStars( BROWSER_MAX_STAR_COUNT ); if (nearStars == nil ) return [NSDictionary dictionary]; starCount = nearStars->size(); NSMutableDictionary* starDict = [NSMutableDictionary dictionaryWithCapacity: starCount+2]; const Star *aStar; NSString *starName; int i; for (i=0;igetUniverse()->getStarCatalog()->getStarName(*aStar) ]; [starDict setObject: [[[BrowserItem alloc] initWithCelestiaStar: [[[CelestiaStar alloc] initWithStar: aStar] autorelease]] autorelease] forKey: starName]; } delete sb; delete nearStars; return starDict; } -(BrowserItem *) root { if (rootItems) return [rootItems objectForKey: rootId]; BrowserItem *sol; BrowserItem *stars; BrowserItem *dsos; sol = [self sol]; [BrowserItem addChildrenToStar: sol]; stars = [[BrowserItem alloc] initWithName: @""]; [stars addChild: [[[BrowserItem alloc] initWithName: NSLocalizedString(@"Nearest Stars",@"") children: [self starsOfKind: StarBrowser::NearestStars]] autorelease]]; [stars addChild: [[[BrowserItem alloc] initWithName: NSLocalizedString(@"Brightest Stars",@"") children: [self starsOfKind: StarBrowser::BrighterStars]] autorelease]]; [stars addChild: [[[BrowserItem alloc] initWithName: NSLocalizedString(@"Stars With Planets",@"") children: [self starsOfKind: StarBrowser::StarsWithPlanets]] autorelease]]; dsos = [[BrowserItem alloc] initWithName: @"" children: [self deepSkyObjects]]; rootItems = [[NSDictionary alloc] initWithObjectsAndKeys: sol, @"solarSystem", stars, @"star", dsos, @"dso", nil]; [stars release]; [dsos release]; return [rootItems objectForKey: rootId]; } - (BrowserItem *) itemForPathArray: (NSArray*) pathNames { BrowserItem *lastItem = [self root]; BrowserItem *nextItem = lastItem; NSString *lastKey = nil; id body; unsigned i; for (i=1;i<[pathNames count];i++) { lastKey = [pathNames objectAtIndex: i]; nextItem = [lastItem childNamed: lastKey]; if (nil==nextItem) break; lastItem = nextItem; } if (nextItem) { body = [nextItem body]; if (body) { if ([body respondsToSelector: @selector(star)]) [BrowserItem addChildrenToStar: nextItem]; else if ([body respondsToSelector: @selector(body)]) [BrowserItem addChildrenToBody: nextItem]; } } return lastItem; } - (Selection *) selFromPathArray: (NSArray*) pathNames { Selection *sel = NULL; BrowserItem *item = [self itemForPathArray: pathNames]; id body = [item body]; if (body) { if ([body respondsToSelector: @selector(star)]) sel = new Selection([(CelestiaStar *)body star]); else if ([body respondsToSelector: @selector(body)]) sel = new Selection([(CelestiaBody *)body body]); else if ([body respondsToSelector: @selector(DSO)]) sel = new Selection([(CelestiaDSO *)body DSO]); else if ([body respondsToSelector: @selector(location)]) sel = new Selection([(CelestiaLocation *)body location]); } return sel; } - (int) browser: (NSBrowser*) sender numberOfRowsInColumn: (int) column { if (browser != sender) { browser = sender; // if ([browser respondsToSelector:@selector(setColumnResizingType:)]) // [browser setColumnResizingType: 2]; // [browser setMinColumnWidth: 80]; [browser setDoubleAction:@selector(doubleClick:)]; } BrowserItem *itemForColumn = [self itemForPathArray: [[sender pathToColumn: column ] componentsSeparatedByString: [sender pathSeparator] ] ]; return [itemForColumn childCount]; } - (BOOL) isLeaf: (BrowserItem *) aItem { if ([aItem childCount] > 0) return NO; id body = [aItem body]; if (body) { if ([body respondsToSelector: @selector(star)]) { if (appCore->getSimulation()->getUniverse()->getSolarSystem([(CelestiaStar *)body star])) return NO; } else if ([body respondsToSelector: @selector(body)]) { if ([(CelestiaBody *)body body]->getSatellites() || [(CelestiaBody *)body body]->getLocations()) return NO; } } return YES; } - (void) browser: (NSBrowser*) sender willDisplayCell: (id) cell atRow: (int) row column: (int) column { BrowserItem *itemForColumn = [self itemForPathArray: [[sender pathToColumn: column ] componentsSeparatedByString: [sender pathSeparator] ] ]; NSArray* colKeys = [itemForColumn allChildNames]; BOOL isLeaf = YES; NSString* itemName = [colKeys objectAtIndex: row]; if (!itemName) itemName = @"????"; else isLeaf = [self isLeaf: [itemForColumn childNamed: itemName ]]; NSRange rightParenRange = {NSNotFound, 0}; NSRange leftParenRange = {NSNotFound, 0}; NSRange parenRange = {NSNotFound, 0}; if (isLeaf) { rightParenRange = [itemName rangeOfString:@")" options:NSBackwardsSearch]; if (rightParenRange.length == 1) { leftParenRange = NSMakeRange(0, rightParenRange.location - 1); parenRange = [itemName rangeOfString:@"(" options:NSBackwardsSearch range:leftParenRange]; if (parenRange.length == 1) { parenRange.length = rightParenRange.location - parenRange.location + 1; } } } if (parenRange.location != NSNotFound) { NSDictionary *parenAttr = [NSDictionary dictionaryWithObjectsAndKeys: [NSColor grayColor], NSForegroundColorAttributeName, nil]; NSMutableAttributedString *attrStr = [[[NSMutableAttributedString alloc] initWithString: itemName] autorelease]; [attrStr setAttributes:parenAttr range:parenRange]; [cell setAttributedStringValue:attrStr]; } else { [cell setTitle: itemName ]; } [cell setLeaf: isLeaf]; } - (IBAction) go: (id) sender { Selection *sel = [self selFromPathArray: [[browser path] componentsSeparatedByString: [browser pathSeparator] ]]; if (sel) { appCore->getSimulation()->setSelection(*sel); if ([sender tag]!=0) appCore->charEntered([sender tag]); delete sel; } } - (void) doubleClick: (id) sender { browser = sender; NSArray *pathArray = [[browser path] componentsSeparatedByString: [browser pathSeparator]]; Selection *sel = [self selFromPathArray: pathArray]; if (sel) { appCore->getSimulation()->setSelection(*sel); appCore->charEntered('g'); delete sel; } } - (void)tabView: (NSTabView *)aTabView didSelectTabViewItem: (NSTabViewItem *)aTabViewItem { rootId = [aTabViewItem identifier]; } - (void)windowDidBecomeKey:(NSNotification *)aNotification { if ([aNotification object] == [self window]) { NSTabViewItem *curTab = [tabView selectedTabViewItem]; if (nil == curTab) return; rootId = [curTab identifier]; if (browser && [rootId isEqualToString: @"star"]) { BrowserItem *rootItem = [self root]; [rootItem addChild: [[[BrowserItem alloc] initWithName: NSLocalizedString(@"Nearest Stars",@"") children: [self starsOfKind: StarBrowser::NearestStars]] autorelease]]; // Immediately synch browser display with reloaded star list, // but only if star list is showing NSArray *selection = [[browser path] componentsSeparatedByString: [browser pathSeparator]]; if ([selection count]>1 && [[selection objectAtIndex: 1] isEqualToString: NSLocalizedString(@"Nearest Stars",@"")]) { for (int i=1; i<[browser numberOfVisibleColumns]; ++i) [browser reloadColumn: i]; } } } } @end