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.