This is TSAppManager.m in view mode; [Download] [Up]
/* TSAppManager.m created by tsengel on Thu 14-Aug-1997 */ #import "TSAppManager.h" #import <MiscAppKit/MiscAppKit.h> #import "TSWorkspace.h" #import "TSShelfWindow.h" #import "TSShelfDragView.h" #import "TSShelfItem.h" #import "NSDictionary+MiscDefaultsSupport.h" #import "NSUserDefaults+MiscColorExtensions.h" #import "NSColor+MiscExtensions.h" #import <OFInfoManager.h> @implementation TSAppManager - (void)applicationDidFinishLaunching:(NSNotification *)notification /*" Prepare the application. "*/ { [self registerDefaults]; // Build the shelf window // For some magical reasons autosizing only works in 80% of the cases when we open the shelf window // as it is defined in the NIB... In 20% it simply forgets to resize its content view...which is bad. // Therefore we create it "by hand". shelfWindow = [[TSShelfWindow alloc] initWithContentRect:NSMakeRect( 100, 100, 100, 100 ) styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:NO]; // Now that we have the final size and position we will insert the contentView. // We will record the current Matrix rect since we will often have to resize it again. [shelfWindow setContentView:shelfContentView]; _fixedMatrixRect = [tabMatrix frame]; [shelfWindow setGrabBorder:_fixedMatrixRect.size.height]; // For some strange reasons the setBorder above resets the "autoframe" frame ... hmmm ?? // So moving it down a couple of lines seems like a "good?" workaround. [shelfWindow setFrameUsingName:@"ShelfWindow"]; [shelfWindow setFrameAutosaveName:@"ShelfWindow"]; [dragMasterView establishTrackingRect]; // Get the prefs entries right...and then do like if we changed them via teh UI. // this ensure that all the right values get pushed into the UI elements // However..this also ensures that all values appear in the persisent storage domain // which is useful for debugging since you can see _all_ the stored defaults even if they // never get touched. //... but in a regular app you usually don't want this. // <<NOTE>> The order of the prefs inits must be that way to give a good looking startup... [self preparePrefsPanel]; [self desktopColorChanged:self]; [self loadAllShelfLevels]; [self preferencesChanged:self]; [shelfWindow orderFront:self]; // Now lets keep track of all the app launches etc pp whicha re going on. // For this we just make sure that there is one instance of our shared workspace listening [TSWorkspace sharedWorkspace]; } - (void)showPreferencesPanel:(id)sender { // Ensure that the prefs panel is in sync with the currently active preferences. // We should register for teh Defaults changed notifications since there are situatuions where the // prefs are visible and not in sync with the real values in the database... // Bad luck. [self preparePrefsPanel]; [preferencesPanel makeKeyAndOrderFront:self]; } - (void)registerDefaults { NSMutableDictionary* registrationDict = [NSMutableDictionary dictionary]; id anArray; id aDict = nil; // Internal defaults. Not accessible by the prefs panel [registrationDict setObject:@"~/Library/TheShelf" forKey:@"DefaultShelfLibraryPath"]; [registrationDict setObject:NSHomeDirectory() forKey:@"LastOpenDirectory"]; // Shelf behavior [registrationDict setInteger:TSShelfForegroundWindowLevel forKey:@"ShelfWindowLevel"]; [registrationDict setBool:YES forKey:@"ShelfWindowDoesAutoraise"]; // Performing workspace actions in a thread causes the DPS context to get corrupted. // If you rather live with that then waiting for shelf cations to complete...set this to YES. [registrationDict setBool:NO forKey:@"UseWorkspaceThreads"]; // Now specify the default shelf that we will come up with. // Title is the tab title, path the path to the storage...if there is no path then there should be // a type which is the shelf bundles name. anArray = [NSMutableArray new]; aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Processes", @"Title", @"ApplicationTrayShelf", @"ShelfType", nil]; [anArray addObject:aDict]; aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Applications", @"Title", @"IconViewShelf", @"ShelfType", nil]; [anArray addObject:aDict]; // aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Tray", @"Title", @"SystemTrayShelf", @"ShelfType", nil]; // [anArray addObject:aDict]; aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Documents", @"Title", @"IconViewShelf", @"ShelfType", nil]; [anArray addObject:aDict]; aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Temporary", @"Title", @"IconViewShelf", @"ShelfType", nil]; [anArray addObject:aDict]; // aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Clip Notes", @"Title", @"TextViewShelf", @"ShelfType", nil]; // [anArray addObject:aDict]; // aDict = [NSDictionary dictionaryWithObjectsAndKeys: @"Pastebaord", @"Title", @"IconViewShelf", @"ShelfType", nil]; // [anArray addObject:aDict]; [registrationDict setInteger:0 forKey:@"SelectedTabNumber"]; [registrationDict setColor:[NSColor whiteColor] forKey:@"DesktopColor"]; [registrationDict setObject:anArray forKey:@"Shelfs"]; [[NSUserDefaults standardUserDefaults] registerDefaults:registrationDict]; } - (void)preparePrefsPanel /*" Puts all the stored defaults right into the UI controls. This should become part of the PrefPanel class once we have one. "*/ { id aColor; [windowLevelMatrix selectCellWithTag:[[NSUserDefaults standardUserDefaults] integerForKey:@"ShelfWindowLevel"]]; [shelfAutohideSwitch setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"ShelfWindowDoesAutoraise"]]; [workspaceThreadingSwitch setState:[[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"]]; aColor = [[NSUserDefaults standardUserDefaults] colorForKey:@"DesktopColor"]; if( aColor ) [desktopColorPrefWell setColor:aColor]; } - (void)preferencesChanged:(id)sender /*" Called by the simple preferences items to force an update of the database. This really should be moved to a PrefsController once we have one... "*/ { int newLevel; // Our shelf levels are hardwired into the NIB... of we can take the tags directly. newLevel = [[windowLevelMatrix selectedCell] tag]; [[NSUserDefaults standardUserDefaults] setInteger:newLevel forKey:@"ShelfWindowLevel"]; if( newLevel != [shelfWindow level] ) { [shelfWindow setLevel:newLevel]; [shelfWindow orderFront:self]; } [[NSUserDefaults standardUserDefaults] setBool:[shelfAutohideSwitch state] forKey:@"ShelfWindowDoesAutoraise"]; [shelfWindow setAutoRaiseEnabled:[shelfAutohideSwitch state]]; [[NSUserDefaults standardUserDefaults] setBool:[workspaceThreadingSwitch state] forKey:@"UseWorkspaceThreads"]; } - (void)desktopColorChanged:(id)sender /*" Called by the prefs ColorWell to update teh defautl and "faked" desktop view. This really should be moved to a PrefsController once we have one... "*/ { [[NSUserDefaults standardUserDefaults] setColor:[desktopColorPrefWell color] forKey:@"DesktopColor"]; [desktopFakeView setBackgroundColor:[desktopColorPrefWell color]]; // The tab matrix needs a display as well to fix teh layering problem... [desktopFakeView setNeedsDisplay:YES]; [tabMatrix setNeedsDisplay:YES]; } - (BOOL)loadAllShelfLevels { id defaultShelfs = [[NSUserDefaults standardUserDefaults] objectForKey:@"Shelfs"]; id enumerator; id nextDict; id anItem; id loadPath; int selColumn = [[NSUserDefaults standardUserDefaults] integerForKey:@"SelectedTabNumber"]; // First we will remove all shelf levels which are currently active while( [tabMatrix numberOfColumns] > 0 ) [self removeShelfLevel:self]; // And then lets add the default tray level enumerator = [defaultShelfs objectEnumerator]; while( nextDict = [enumerator nextObject] ) { loadPath = [nextDict objectForKey:@"Path"]; if( loadPath ) anItem = [self newItemWithPath:loadPath]; else anItem = [self newItemWithName:[nextDict objectForKey:@"ShelfType"]]; if( anItem ) { [anItem setTitle:[nextDict objectForKey:@"Title"]]; [self addShelfLevelItem:anItem]; } } // Finally try to preselect the right shelf level... // Select the tab..ugly !!! if( selColumn < [tabMatrix numberOfColumns] ) [tabMatrix selectCellAtRow:0 column:selColumn]; [tabMatrix sendAction]; return YES; } - (void)applicationDidHide:(NSNotification *)aNotification { // Gee...this is dirty. After getting deactivated we will simply put the shelf window on screen again. // This at least keeps the shelf window around all the time..no matter what happens. // ...and this is done without low level DPS hacking. But it surely is not a very "professional" solution. [shelfWindow orderFront:self]; } - (void)applicationWillTerminate:(NSNotification *)aNotification { // Ensure that all shelf levels are saved. This also does a sync on the user defaults..so everything should be secure by now NSLog( @"TheShelf....terminating and saving..."); [self saveShelfs:self]; } - (BOOL)application:(NSApplication *)application openFile:(NSString *)filename { // <<HACk>> We should check if there are times when we have to return no here ! return [self _openFile:filename]; } - (void)open:(id)sender /*" Here comes mehtod docu...right here "*/ { NSArray* files; NSArray* acceptedFileTypes = [NSArray arrayWithObjects:@"aShelf", nil]; NSOpenPanel* openPanel = [NSOpenPanel openPanel]; int numberOfFiles; int index = 0; [self fixSpinningCursorBug]; [openPanel setAllowsMultipleSelection:YES]; [openPanel setTreatsFilePackagesAsDirectories:NO]; [openPanel setTitle:@"Open Shelf"]; if( [openPanel runModalForDirectory:[[NSUserDefaults standardUserDefaults] objectForKey:@"LastOpenDirectory"] file:nil types:acceptedFileTypes] ) { // This is overkill since ther are no multiplefiles right now... but we'll just leave the code in here..doesn't hurt. files = [openPanel filenames]; numberOfFiles = [files count]; while( index < numberOfFiles ) { [self _openFile:[files objectAtIndex:index]]; index++; } // Now remember where the user loaded the last file from. // We will by default look at this place first once we have to load a file again. [[NSUserDefaults standardUserDefaults] setObject:[openPanel directory] forKey:@"LastOpenDirectory"]; // Select the tab..ugly !!! [tabMatrix selectCellAtRow:0 column:( [tabMatrix numberOfColumns] - 1 )]; [tabMatrix sendAction]; } } - (BOOL)_openFile:(NSString *)filename { id anItem; anItem = [self newItemWithPath:filename]; if( anItem ) { [self addShelfLevelItem:anItem]; return YES; } return NO; } - (void)addShelfLevel:(id)sender { [self addIconShelfLevel:self]; } - (void)addIconShelfLevel:(id)sender { [self addShelfLevelItem:[self newItemWithName:@"IconViewShelf"]]; // Select the tab..ugly !!! [tabMatrix selectCellAtRow:0 column:( [tabMatrix numberOfColumns] - 1 )]; [tabMatrix sendAction]; } - (void)addTextShelfLevel:(id)sender { [self addShelfLevelItem:[self newItemWithName:@"ConsoleTextShelf"]]; // Select the tab..ugly !!! [tabMatrix selectCellAtRow:0 column:( [tabMatrix numberOfColumns] - 1 )]; [tabMatrix sendAction]; } - (void)addTrayShelfLevel:(id)sender { [self addShelfLevelItem:[self newItemWithName:@"ApplicationTrayShelf"]]; // Select the tab..ugly !!! [tabMatrix selectCellAtRow:0 column:( [tabMatrix numberOfColumns] - 1 )]; [tabMatrix sendAction]; } - (id)newItemWithName:(NSString *)bundleName { id path; id newItem; id ourBundle; path = [[NSBundle mainBundle] pathForResource:bundleName ofType:@"bundle"]; ourBundle = [NSBundle bundleWithPath:path]; newItem = [[ourBundle principalClass] new]; // IF the default name is not very useful..we will at least add a number to make them unique... if( [[newItem title] isEqual:@"Untitled"] ) [newItem setTitle:[NSString stringWithFormat:@"Untitled-%i", [tabMatrix numberOfColumns]]]; return newItem; } - (id)newItemWithPath:(NSString *)shelfPath { id path; id newItem = nil; id ourBundle; id shelfType; id plistDict = [NSDictionary dictionaryWithContentsOfFile:[shelfPath stringByAppendingPathComponent:@"Info.plist"]]; // NSLog( @"Load shelf from path %@", plistDict ); shelfType = [plistDict objectForKey:@"ShelfType"]; if( shelfType ) { path = [[NSBundle mainBundle] pathForResource:shelfType ofType:@"bundle"]; ourBundle = [NSBundle bundleWithPath:path]; newItem = [[[ourBundle principalClass] alloc] initWithContentsOfFile:shelfPath]; } return newItem; } - (void)addShelfLevelItem:(id)newItem { int newColumn; id newCell; id title; [tabMatrix addColumn]; newColumn = [tabMatrix numberOfColumns] - 1; // Now finally creat the new shelf item and select the new tab that we just added. newCell = [tabMatrix cellAtRow:0 column:newColumn]; title = [newItem title]; [newCell setTitle:title]; [newCell setAllowsEditingTextAttributes:YES]; [newItem setIdentifier:newCell]; [swapView addSwapViewItem:newItem]; [tabMatrix setFrame:_fixedMatrixRect]; [tabMatrix setNeedsDisplay:YES]; } - (void)shiftShelfTabLeft:(id)sender { id affectedCell; id selectedCell; int oldColumn = [tabMatrix selectedColumn]; int newColumn = oldColumn - 1; if( oldColumn > 0 ) { affectedCell = [[tabMatrix cellAtRow:0 column:newColumn] retain]; selectedCell = [[tabMatrix cellAtRow:0 column:oldColumn] retain]; // Now swap them and reselect the new position... [tabMatrix putCell:selectedCell atRow:0 column:newColumn]; [tabMatrix putCell:affectedCell atRow:0 column:oldColumn]; [tabMatrix selectCellAtRow:0 column:oldColumn]; [tabMatrix selectCellAtRow:0 column:newColumn]; [tabMatrix setNeedsDisplay:YES]; [affectedCell release]; [selectedCell release]; } [self fixSpinningCursorBug]; } - (void)shiftShelfTabRight:(id)sender { id affectedCell; id selectedCell; int oldColumn = [tabMatrix selectedColumn]; int newColumn = oldColumn + 1; if( newColumn < [tabMatrix numberOfColumns] ) { affectedCell = [[tabMatrix cellAtRow:0 column:newColumn] retain]; selectedCell = [[tabMatrix cellAtRow:0 column:oldColumn] retain]; // Now swap them and reselect the new position... [tabMatrix putCell:selectedCell atRow:0 column:newColumn]; [tabMatrix putCell:affectedCell atRow:0 column:oldColumn]; [tabMatrix selectCellAtRow:0 column:newColumn]; [tabMatrix setNeedsDisplay:YES]; [affectedCell release]; [selectedCell release]; } [self fixSpinningCursorBug]; } - (void)showRenamingPanel:(id)sender { // Well..this really should be modal... [renamingPanel makeKeyAndOrderFront:self]; } - (void)renameShelfLevel:(id)sender { id selectedCell = [tabMatrix selectedCell]; id newTitle = [renamingText stringValue]; // Ok..this could be a lot simpler etc..pp.. if( newTitle && ![newTitle isEqual:@""] ) { [selectedCell setTitle:[renamingText stringValue]]; [[swapView swapViewItemWithIdentifier:selectedCell] setTitle:[renamingText stringValue]]; [[sender window] performClose:self]; } else { // Maybe we should bring up an alert... or ping and not close the window or that like ... ?? } [renamingText setStringValue:@""]; } - (void)removeShelfLevel:(id)sender /*" Removes the currently selected shelf level. "*/ { id selectedCell; id affectedItem; int selectedColumn; // Before we really remove the tab from the matrix we will remove the shelf item which belongs to it selectedCell = [tabMatrix selectedCell]; if( !selectedCell ) return; selectedColumn = [tabMatrix selectedColumn]; // Now its time to fix the swapView as well // and we will save teh shelf level since if someone was too fast in removing it..he can safely reopen it again affectedItem = [swapView swapViewItemWithIdentifier:selectedCell]; [affectedItem save]; [swapView removeSwapViewItem:affectedItem]; [tabMatrix removeColumn:selectedColumn]; [tabMatrix setFrame:_fixedMatrixRect]; [tabMatrix setNeedsDisplay:YES]; // Since the selected column is gone now we need to select some other tab... selectedColumn--; if( selectedColumn < 0 ) selectedColumn = 0; [tabMatrix selectCellAtRow:0 column:selectedColumn]; [tabMatrix sendAction]; } - (void)saveShelfs:(id)sender { // Trigger a save for all shelf items we know id anItem; id aCell; int column; id aDict; id itemArray = [NSMutableArray new]; // Now we also should put the current shelf info into our defaults database ! for( column=0; column<[tabMatrix numberOfColumns]; column++ ) { // Now get each shelf item and save it if its content has been changed. aCell = [tabMatrix cellAtRow:0 column:column]; anItem = [swapView swapViewItemWithIdentifier:aCell]; if( [anItem contentDidChange] ) [anItem save]; aDict = [NSMutableDictionary new]; [aDict setObject:[anItem title] forKey:@"Title"]; [aDict setObject:[anItem path] forKey:@"Path"]; [itemArray addObject:aDict]; } // NSLog( @"Writing defaults array %@", itemArray ); [[NSUserDefaults standardUserDefaults] setObject:itemArray forKey:@"Shelfs"]; [[NSUserDefaults standardUserDefaults] setInteger:[tabMatrix selectedColumn] forKey:@"SelectedTabNumber"]; [[NSUserDefaults standardUserDefaults] synchronize]; } - (void)showInfoPanel:sender /* Display the info panel and make it the key window. */ { // Ok...we are not really using an info "panel"...but its a // class that will do the trick. if( !infoPanel ) infoPanel = [OFInfoManager new]; [infoPanel makeKeyAndOrderFront:self]; } - (void)fixSpinningCursorBug { // Evil hack to fix Cursor problem... // But the problem seems to be related to a corrupted DPS stack and display behavior.. hmm. if( [[NSUserDefaults standardUserDefaults] boolForKey:@"UseWorkspaceThreads"] ) PSsetwaitcursorenabled( NO ); } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.