ftp.nice.ch/Attic/openStep/tools/workspace/TheShelf.0.3.3.sd.tgz#/TheShelf.0.3.3.sd/Source/TSAppManager.m

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.