diff --git a/Source/NSCachedImageRep.m b/Source/NSCachedImageRep.m index 30aa396a8..ecf85ef3a 100644 --- a/Source/NSCachedImageRep.m +++ b/Source/NSCachedImageRep.m @@ -38,6 +38,18 @@ #include #include +@interface GSCacheW : NSWindow +@end + +@implementation GSCacheW + +- (void) initDefaults +{ + [super initDefaults]; + menu_exclude = YES; // Don't show in windows menu. +} +@end + /* Backend protocol - methods that must be implemented by the backend to complete the class */ @protocol NXCachedImageRepBackend @@ -48,17 +60,26 @@ // Initializing an NSCachedImageRep - (id) initWithSize: (NSSize)aSize - depth: (NSWindowDepth)aDepth - separate: (BOOL)separate - alpha: (BOOL)alpha + depth: (NSWindowDepth)aDepth + separate: (BOOL)separate + alpha: (BOOL)alpha { - return nil; + NSWindow *win; + NSRect frame; + + frame.origin = NSMakePoint(0,0); + frame.size = aSize; + win = [[GSCacheW alloc] initWithContentRect: frame + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreRetained + defer: NO]; + self = [self initWithWindow: win rect: frame]; + [win release]; + return self; } -- initWithWindow: (NSWindow *)win rect: (NSRect)rect +- (id) initWithWindow: (NSWindow *)win rect: (NSRect)rect { - int style = NSClosableWindowMask; - [super init]; _window = [win retain]; @@ -72,16 +93,17 @@ if (!_window) { [NSException raise: NSInvalidArgumentException - format: @"Must specify either window or rect when creating NSCachedImageRep"]; + format: @"Must specify either window or rect when " + @"creating NSCachedImageRep"]; } _rect = [_window frame]; } if (!_window) - _window = [[NSWindow alloc] initWithContentRect: _rect - styleMask: style - backing: NSBackingStoreRetained - defer: NO]; + _window = [[GSCacheW alloc] initWithContentRect: _rect + styleMask: NSBorderlessWindowMask + backing: NSBackingStoreRetained + defer: NO]; return self; } @@ -104,6 +126,7 @@ - (BOOL)draw { + NSCopyBits([_window gState], _rect, _rect.origin); return NO; } diff --git a/Source/NSImage.m b/Source/NSImage.m index 906f8549e..bdd3b753a 100644 --- a/Source/NSImage.m +++ b/Source/NSImage.m @@ -43,6 +43,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,8 @@ #include #include +BOOL doesCaching = NO; + // Resource directories static NSString* gnustep_libdir = @GNUSTEP_INSTALL_LIBDIR; static NSString* NSImage_PATH = @"Images"; @@ -65,59 +68,62 @@ static NSString* NSImage_PATH = @"Images"; fraction: (float)aFloat; @end -typedef struct _rep_data_t +@interface GSRepData : NSObject { - NSString* fileName; - id rep; - id cache; - id original; - BOOL validCache; -} rep_data_t; +@public + NSString *fileName; + NSImageRep *rep; + NSImageRep *original; + NSColor *bg; +} +@end + +@implementation GSRepData +- (id) copyWithZone: (NSZone*)z +{ + GSRepData *c = (GSRepData*)NSCopyObject(self, 0, z); + + if (c->fileName) + c->fileName = [c->fileName copy]; + if (c->rep) + c->rep = [c->rep copy]; + if (c->bg) + c->bg = [c->bg copy]; + return c; +} + +- (void) dealloc +{ + if (fileName) + [fileName release]; + if (rep) + [rep release]; + if (bg) + [bg release]; + NSDeallocateObject(self); +} +@end NSArray *iterate_reps_for_types(NSArray *imageReps, SEL method); -/* Find the rep_data_t holding a representation */ -rep_data_t +/* Find the GSRepData object holding a representation */ +GSRepData* repd_for_rep(NSArray *_reps, NSImageRep *rep) { - int i, count; - rep_data_t repd; + unsigned i, count; + GSRepData *repd; count = [_reps count]; for (i = 0; i < count; i++) { - [[_reps objectAtIndex: i] getValue: &repd]; - if (repd.rep == rep) + repd = [_reps objectAtIndex: i]; + if (repd->rep == rep) return repd; } [NSException raise: NSInternalInconsistencyException format: @"Cannot find stored representation"]; /* NOT REACHED */ - return repd; -} - -void -set_repd_for_rep(NSMutableArray *_reps, NSImageRep *rep, rep_data_t *new_repd) -{ - int i, count; - rep_data_t repd; - BOOL found = NO; - - count = [_reps count]; - for (i = 0; i < count; i++) - { - [[_reps objectAtIndex: i] getValue: &repd]; - if (repd.rep == rep && !found) - { - [_reps replaceObjectAtIndex: i withObject: - [NSValue value: new_repd withObjCType: @encode(rep_data_t)]]; - found = YES; - break; - } - } - if (!found) - [_reps addObject: - [NSValue value: new_repd withObjCType: @encode(rep_data_t)]]; + return nil; } @interface NSImage (Backend) @@ -140,10 +146,10 @@ static NSDictionary* nsmapping = nil; { if (self == [NSImage class]) { - NSBundle *system = [NSBundle bundleWithPath: gnustep_libdir]; - NSString* path = [system pathForResource: @"nsmapping" - ofType: @"strings" - inDirectory: NSImage_PATH]; + NSBundle *system = [NSBundle bundleWithPath: gnustep_libdir]; + NSString *path = [system pathForResource: @"nsmapping" + ofType: @"strings" + inDirectory: NSImage_PATH]; // Initial version [self setVersion: 1]; @@ -158,7 +164,7 @@ static NSDictionary* nsmapping = nil; + imageNamed: (NSString *)aName { - NSString* realName = [nsmapping objectForKey: aName]; + NSString *realName = [nsmapping objectForKey: aName]; if (realName) aName = realName; @@ -215,8 +221,8 @@ static NSDictionary* nsmapping = nil; if (ext) path = [system pathForResource: the_name - ofType: ext - inDirectory: NSImage_PATH]; + ofType: ext + inDirectory: NSImage_PATH]; else { id o, e; @@ -229,8 +235,8 @@ static NSDictionary* nsmapping = nil; while ((o = [e nextObject])) { path = [system pathForResource: the_name - ofType: o - inDirectory: NSImage_PATH]; + ofType: o + inDirectory: NSImage_PATH]; if ([path length] != 0) break; } @@ -242,7 +248,7 @@ static NSDictionary* nsmapping = nil; NSImage *image; image = [[self allocWithZone: NSDefaultMallocZone()] - initByReferencingFile: path]; + initByReferencingFile: path]; if (image) { [image setName: aName]; @@ -463,8 +469,8 @@ static NSDictionary* nsmapping = nil; // Determining How the Image is Drawn - (BOOL) isValid { - BOOL valid = NO; - int i, count; + BOOL valid = NO; + unsigned i, count; /* Go through all our representations and determine if at least one is a valid cache */ @@ -472,9 +478,11 @@ static NSDictionary* nsmapping = nil; count = [_reps count]; for (i = 0; i < count; i++) { - rep_data_t repd; - [[_reps objectAtIndex: i] getValue: &repd]; - valid |= repd.validCache; + GSRepData *repd = (GSRepData*)[_reps objectAtIndex: i]; + + if (repd->bg != nil + || [repd->rep isKindOfClass: [NSCachedImageRep class]] == NO) + valid = YES; } return valid; } @@ -486,11 +494,14 @@ static NSDictionary* nsmapping = nil; count = [_reps count]; for (i = 0; i < count; i++) { - rep_data_t repd; - [[_reps objectAtIndex: i] getValue: &repd]; - repd.validCache = NO; - [_reps replaceObjectAtIndex: i withObject: - [NSValue value: &repd withObjCType: @encode(rep_data_t)]]; + GSRepData *repd; + + repd = (GSRepData*)[_reps objectAtIndex: i]; + if (repd->bg != nil) + { + [repd->bg release]; + repd->bg = nil; + } } } @@ -499,7 +510,7 @@ static NSDictionary* nsmapping = nil; _flags.scalable = flag; } -- (BOOL)scalesWhenResized +- (BOOL) scalesWhenResized { return _flags.scalable; } @@ -513,8 +524,10 @@ static NSDictionary* nsmapping = nil; } } -- (NSColor *)backgroundColor +- (NSColor *) backgroundColor { + if (_color == nil) + _color = [[NSColor clearColor] retain]; return _color; } @@ -522,25 +535,24 @@ static NSDictionary* nsmapping = nil; in and added to the representation list. */ - _loadImageFilenames { - unsigned i, count; - rep_data_t repd; + unsigned i, count; + GSRepData *repd; _syncLoad = NO; count = [_reps count]; for (i = 0; i < count; i++) { - [[_reps objectAtIndex: i] getValue: &repd]; - if (repd.fileName) - [self loadFromFile: repd.fileName]; + repd = (GSRepData*)[_reps objectAtIndex: i]; + if (repd->fileName) + [self loadFromFile: repd->fileName]; } // Now get rid of them since they are already loaded count = [_reps count]; while (count--) { - [[_reps objectAtIndex: count] getValue: &repd]; - if (repd.fileName) + repd = (GSRepData*)[_reps objectAtIndex: count]; + if (repd->fileName) { - [repd.fileName release]; [_reps removeObjectAtIndex: count]; } } @@ -553,50 +565,65 @@ static NSDictionary* nsmapping = nil; // image (if there is one). - (NSImageRep *)_doImageCache { - NSImageRep *rep = nil; - rep_data_t repd; + NSImageRep *rep = nil; + GSRepData *repd; repd = repd_for_rep(_reps, [self bestRepresentationForDevice: nil]); - rep = repd.rep; - if (repd.cache) - rep = repd.cache; + rep = repd->rep; - if (![rep isKindOfClass: [NSCachedImageRep class]]) + if (doesCaching) { -#if 0 - [self lockFocus]; + /* + * If this is not a cached image rep - create a cache to be used to + * render the image rep into, and switch to the cached rep. + */ + if ([rep isKindOfClass: [NSCachedImageRep class]] == NO) { - rep_data_t cached; - NSRect bounds; - _lockedView = [NSView focusView]; - bounds = [_lockedView bounds]; - [self _displayEraseRect: bounds view: _lockedView color: _color]; - [self unlockFocus]; - [[_reps lastObject] getValue: &cached]; - cached.original = rep; - cached.validCache = YES; - [_reps removeLastObject]; - [_reps addObject: - [NSValue value: &cached withObjCType: @encode(rep_data_t)]]; - } -#endif - } - else if (!repd.validCache) - { -#if 0 - [self lockFocusOnRepresentation: rep]; + NSScreen *cur = [NSScreen mainScreen]; + NSCachedImageRep *cachedRep; + NSSize imageSize; + + imageSize = [self size]; + if (imageSize.width == 0 || imageSize.height == 0) + return nil; + + cachedRep = [[NSCachedImageRep alloc] initWithSize: _size + depth: [cur depth] + separate: NO + alpha: NO]; + [self addRepresentation: cachedRep]; + [cachedRep release]; /* Retained in _reps array. */ + repd = repd_for_rep(_reps, cachedRep); + repd->original = rep; + rep = repd->rep; + } + + /* + * if the cache is not valid, it's background color will not exist + * and we must draw the background then render from the original + * image rep into the cache. + */ + if (repd->bg == nil) { - NSRect bounds; + NSRect bounds; + + [self lockFocusOnRepresentation: rep]; + /* + * If this is not a cache - the lockFocus will have created a + * cache that we can use instead. + */ + if (repd->original == nil) + { + repd = repd_for_rep(_reps, [self lastRepresentation]); + } bounds = [_lockedView bounds]; - [self _displayEraseRect: bounds view: _lockedView color: _color]; - repd = repd_for_rep(_reps, rep); - [self drawRepresentation: repd.original + [_color set]; + NSEraseRect(bounds); + [self drawRepresentation: repd->original inRect: NSMakeRect(0, 0, _size.width, _size.height)]; [self unlockFocus]; - repd.validCache = YES; - set_repd_for_rep(_reps, repd.rep, &repd); + repd->bg = [_color copy]; } -#endif } return rep; @@ -607,6 +634,7 @@ static NSDictionary* nsmapping = nil; operation: (NSCompositingOperation)op; { NSRect rect; + [self size]; rect = NSMakeRect(0, 0, _size.width, _size.height); [self compositeToPoint: aPoint fromRect: rect operation: op]; @@ -623,9 +651,7 @@ static NSDictionary* nsmapping = nil; // then we need to construct a subimage to draw rep = [self _doImageCache]; - [self lockFocusOnRepresentation: rep]; [self drawRepresentation: rep inRect: rect]; - [self unlockFocus]; } - (void) dissolveToPoint: (NSPoint)aPoint fraction: (float)aFloat; @@ -647,9 +673,7 @@ static NSDictionary* nsmapping = nil; // then we need to construct a subimage to draw rep = [self _doImageCache]; - [self lockFocusOnRepresentation: rep]; [self drawRepresentation: rep inRect: rect]; - [self unlockFocus]; } - (BOOL)drawRepresentation: (NSImageRep *)imageRep inRect: (NSRect)rect @@ -700,7 +724,7 @@ static NSDictionary* nsmapping = nil; { NSArray *array; NSString *ext; - rep_data_t repd; + GSRepData *repd; NSFileManager *manager = [NSFileManager defaultManager]; if ([manager fileExistsAtPath: fileName] == NO) @@ -714,8 +738,10 @@ static NSDictionary* nsmapping = nil; array = [[self class] imageFileTypes]; if ([array indexOfObject: ext] == NSNotFound) return NO; - repd.fileName = [fileName retain]; - [_reps addObject: [NSValue value: &repd withObjCType: @encode(rep_data_t)]]; + repd = [GSRepData new]; + repd->fileName = [fileName retain]; + [_reps addObject: repd]; + [repd release]; _syncLoad = YES; return YES; } @@ -727,8 +753,8 @@ static NSDictionary* nsmapping = nil; - (void) addRepresentations: (NSArray *)imageRepArray { - int i, count; - rep_data_t repd; + unsigned i, count; + GSRepData *repd; if (!imageRepArray) return; @@ -738,13 +764,10 @@ static NSDictionary* nsmapping = nil; count = [imageRepArray count]; for (i = 0; i < count; i++) { - repd.fileName = NULL; - repd.rep = [[imageRepArray objectAtIndex: i] retain]; - repd.cache = NULL; - repd.original = NULL; - repd.validCache = NO; - [_reps addObject: - [NSValue value: &repd withObjCType: @encode(rep_data_t)]]; + repd = [GSRepData new]; + repd->rep = [[imageRepArray objectAtIndex: i] retain]; + [_reps addObject: repd]; + [repd release]; } } @@ -757,6 +780,7 @@ static NSDictionary* nsmapping = nil; if (!imageSize.width || !imageSize.height) return NO; + // FIXME: determine alpha? separate? rep = [[NSCachedImageRep alloc] initWithSize: _size depth: depth @@ -768,21 +792,28 @@ static NSDictionary* nsmapping = nil; - (void) removeRepresentation: (NSImageRep *)imageRep { - int i, count; - rep_data_t repd; - count = [_reps count]; - for (i = 0; i < count; i++) + unsigned i; + GSRepData *repd; + + i = [_reps count]; + while (i-- > 0) { - [[_reps objectAtIndex: i] getValue: &repd]; - if (repd.rep == imageRep) - [_reps removeObjectAtIndex: i]; + repd = (GSRepData*)[_reps objectAtIndex: i]; + if (repd->rep == imageRep) + { + [_reps removeObjectAtIndex: i]; + } + else if (repd->original == imageRep) + { + repd->original = nil; + } } } - (void) lockFocus { - NSScreen *cur = [NSScreen mainScreen]; - NSImageRep *rep; + NSScreen *cur = [NSScreen mainScreen]; + NSImageRep *rep; if (!(rep = [self bestRepresentationForDevice: nil])) { @@ -794,107 +825,159 @@ static NSDictionary* nsmapping = nil; - (void) lockFocusOnRepresentation: (NSImageRep *)imageRep { -#if 0 - NSScreen *cur = [NSScreen mainScreen]; - NSWindow *window; -#endif + NSScreen *cur = [NSScreen mainScreen]; + NSWindow *window; if (!imageRep) [NSException raise: NSInvalidArgumentException format: @"Cannot lock focus on nil rep"]; -#if 0 - if (![imageRep isKindOfClass: [NSCachedImageRep class]]) + if (doesCaching) { - rep_data_t repd, cached; - int depth; - if (_flags.unboundedCacheDepth) - depth = [cur depth]; // FIXME: get depth correctly - else - depth = [cur depth]; - if (![self useCacheWithDepth: depth]) + if (![imageRep isKindOfClass: [NSCachedImageRep class]]) { - [NSException raise: NSImageCacheException - format: @"Unable to create cache"]; + GSRepData *repd, *cached; + int depth; + + if (_flags.unboundedCacheDepth) + depth = [cur depth]; // FIXME: get depth correctly + else + depth = [cur depth]; + if (![self useCacheWithDepth: depth]) + { + [NSException raise: NSImageCacheException + format: @"Unable to create cache"]; + } + cached = repd_for_rep(_reps, [self lastRepresentation]); + cached->original = imageRep; + imageRep = cached->rep; } - repd = repd_for_rep(_reps, imageRep); - cached = repd_for_rep(_reps, [self lastRepresentation]); - repd.cache = cached.rep; - cached.original = repd.rep; - set_repd_for_rep(_reps, imageRep, &repd); - set_repd_for_rep(_reps, cached.rep, &cached); - imageRep = cached.rep; + window = [(NSCachedImageRep *)imageRep window]; + _lockedView = [window contentView]; + [_lockedView lockFocus]; } - window = [(NSCachedImageRep *)imageRep window]; - _lockedView = [window contentView]; - [_lockedView lockFocus]; -#endif } - (void) unlockFocus { -#if 0 if (_lockedView) [_lockedView unlockFocus]; _lockedView = nil; -#endif } - (NSImageRep *) lastRepresentation { - // Reconstruct the repList if it has changed + // Reconstruct the rep list if it has changed [self representations]; return [_repList lastObject]; } - (NSImageRep*) bestRepresentationForDevice: (NSDictionary*)deviceDescription { - id o, e; - NSImageRep *rep = nil; - rep_data_t repd; + NSImageRep *rep = nil; + unsigned count; - // Make sure we have the images loaded in + /* Make sure we have the images loaded in. */ if (_syncLoad) [self _loadImageFilenames]; - if ([_reps count] == 0) - return nil; - - // What's the best representation? FIXME - e = [_reps objectEnumerator]; - o = [e nextObject]; - while (o) - { - [o getValue: &repd]; - if ([repd.rep isKindOfClass: [NSBitmapImageRep class]]) - rep = repd.rep; - o = [e nextObject]; - } -#if 0 - [[_reps lastObject] getValue: &repd]; - if (repd.cache) - rep = repd.cache; - else - rep = repd.rep; -#endif + count = [_reps count]; + if (count > 0) + { + GSRepData *reps[count]; + unsigned i; + + /* + * What's the best representation? FIXME + */ + [_reps getObjects: reps]; + for (i = 0; i < count; i++) + { + GSRepData *repd = reps[i]; + + if ([repd->rep isKindOfClass: [NSBitmapImageRep class]]) + { + rep = repd->rep; + } + } + + /* + * If we got a representation - see if we already have it cached. + */ + if (doesCaching) + { + if (rep != nil) + { + GSRepData *invalidCache = nil; + GSRepData *validCache = nil; + + /* + * Search the cached image reps for any whose original is our + * 'best' image rep. See if we can notice any invalidated + * cache as we go - if we don't find a valid cache, we want to + * re-use an invalidated one rather than createing a new one. + */ + for (i = 0; i < count; i++) + { + GSRepData *repd = reps[i]; + + if (repd->original == rep) + { + if (repd->bg == nil) + { + invalidCache = repd; + } + else if ([repd->bg isEqual: _color] == YES) + { + validCache = repd; + break; + } + } + } + + if (validCache) + { + /* + * If the image rep has transparencey and we are drawing + * without a background (background is clear) then the + * cache can't really be valid 'cos we might be drawing + * transparency on top of anything. So we invalidate + * the cache by removing the background color information. + */ + if ([rep hasAlpha] + && [validCache->bg isEqual: [NSColor clearColor]]) + { + [validCache->bg release]; + validCache->bg = nil; + } + rep = validCache->rep; + } + else if (invalidCache) + { + rep = invalidCache->rep; + } + } + } + } return rep; } - (NSArray *) representations { - unsigned i, count; + unsigned i, count; + if (!_repList) _repList = [[NSMutableArray alloc] init]; if (_syncLoad) [self _loadImageFilenames]; - count = [_reps count]; [_repList removeAllObjects]; + count = [_reps count]; for (i = 0; i < count; i++) { - rep_data_t repd; - [[_reps objectAtIndex: i] getValue: &repd]; - [_repList addObject: repd.rep]; + GSRepData *repd = [_reps objectAtIndex: i]; + + [_repList addObject: repd->rep]; } return _repList; } @@ -1000,10 +1083,9 @@ static NSDictionary* nsmapping = nil; NSArray * iterate_reps_for_types(NSArray* imageReps, SEL method) { - NSImageRep *rep; - id e; -// int i, count; - NSMutableArray* types; + NSImageRep *rep; + NSEnumerator *e; + NSMutableArray *types; types = [NSMutableArray arrayWithCapacity: 2]; diff --git a/Source/NSImageRep.m b/Source/NSImageRep.m index 21cb2e88e..f9e3d08e3 100644 --- a/Source/NSImageRep.m +++ b/Source/NSImageRep.m @@ -279,17 +279,27 @@ static NSMutableArray* imageReps = NULL; // Drawing the Image - (BOOL) draw { - [self subclassResponsibility: _cmd]; - return NO; + return YES; /* Subclass should implement this. */ } - (BOOL) drawAtPoint: (NSPoint)aPoint { - return NO; + NSRect r; + + if (aPoint.x == 0 && aPoint.y == 0) + return [self draw]; + r.origin = aPoint; + r.size = size; + return [self drawInRect: r]; } - (BOOL) drawInRect: (NSRect)aRect { + float x, y; + + if (size.height == 0 || size.width == 0) + return NO; +/* FIXME - should scale and move as necessary. */ return NO; } diff --git a/Source/NSWorkspace.m b/Source/NSWorkspace.m index 97bff96eb..597b0502a 100644 --- a/Source/NSWorkspace.m +++ b/Source/NSWorkspace.m @@ -43,51 +43,29 @@ #include #include #include +#include #define stringify_it(X) #X #define mkpath(X) stringify_it(X) "/Tools" -static NSDictionary *applications = nil; - @implementation NSWorkspace static NSWorkspace *sharedWorkspace = nil; static NSNotificationCenter *workspaceCenter = nil; +static NSMutableDictionary *iconMap = nil; static BOOL userDefaultsChanged = NO; -static NSString *appListName = @".GNUstepAppList"; +static NSString *appListName = @"Services/.GNUstepAppList"; static NSString *appListPath = nil; -static NSDictionary *_suffixes = nil; -static NSString *defaultIconPath = nil; +static NSDictionary *applications = nil; + +static NSString *extPrefName = @".GNUstepExtPrefs"; +static NSString *extPrefPath = nil; +static NSDictionary *extPreferences = nil; + static NSString *_rootPath = @"/"; - -static NSString* gnustep_target_dir = -#ifdef GNUSTEP_TARGET_DIR - @GNUSTEP_TARGET_DIR; -#else - nil; -#endif -static NSString* gnustep_target_cpu = -#ifdef GNUSTEP_TARGET_CPU - @GNUSTEP_TARGET_CPU; -#else - nil; -#endif -static NSString* gnustep_target_os = -#ifdef GNUSTEP_TARGET_OS - @GNUSTEP_TARGET_OS; -#else - nil; -#endif -static NSString* library_combo = -#ifdef LIBRARY_COMBO - @LIBRARY_COMBO; -#else - nil; -#endif - // // Class methods // @@ -97,6 +75,9 @@ static NSString* library_combo = { static BOOL beenHere; NSDictionary *env; + NSString *home; + NSData *data; + NSDictionary *dict; // Initial version [self setVersion: 1]; @@ -111,49 +92,44 @@ static NSString* library_combo = beenHere = YES; workspaceCenter = [NSNotificationCenter new]; + iconMap = [NSMutableDictionary new]; + + /* + * The home directory for per-user information is given by + * the GNUSTEP_USER_ROOT environment variable, or is assumed + * to be the 'GNUstep' subdirectory of the users home directory. + */ env = [[NSProcessInfo processInfo] environment]; - if (env) + if (!env || !(home = [env objectForKey: @"GNUSTEP_USER_ROOT"])) { - NSString *str; - NSData *data; - NSDictionary *newApps; - - str = [env objectForKey: @"GNUSTEP_USER_ROOT"]; - if (str == nil) - str = [NSString stringWithFormat: @"%@/GNUstep", - NSHomeDirectory()]; - str = [str stringByAppendingPathComponent: @"Services"]; - str = [str stringByAppendingPathComponent: appListName]; - appListPath = [str retain]; - - if ((str = [env objectForKey: @"GNUSTEP_TARGET_DIR"]) != nil) - gnustep_target_dir = [str retain]; - else if ((str = [env objectForKey: @"GNUSTEP_HOST_DIR"]) != nil) - gnustep_target_dir = [str retain]; - - if ((str = [env objectForKey: @"GNUSTEP_TARGET_CPU"]) != nil) - gnustep_target_cpu = [str retain]; - else if ((str = [env objectForKey: @"GNUSTEP_HOST_CPU"]) != nil) - gnustep_target_cpu = [str retain]; - - if ((str = [env objectForKey: @"GNUSTEP_TARGET_OS"]) != nil) - gnustep_target_os = [str retain]; - else if ((str = [env objectForKey: @"GNUSTEP_HOST_OS"]) != nil) - gnustep_target_os = [str retain]; - - if ((str = [env objectForKey: @"LIBRARY_COMBO"]) != nil) - library_combo = [str retain]; - - data = [NSData dataWithContentsOfFile: appListPath]; - if (data) - newApps = [NSDeserializer deserializePropertyListFromData: data - mutableContainers: NO]; - applications = [newApps retain]; + home = [NSString stringWithFormat: @"%@/GNUstep", NSHomeDirectory()]; } - _suffixes = [NSDictionary dictionaryWithContentsOfFile:@"Suffixes.plist"]; - _suffixes = [_suffixes retain]; - defaultIconPath = [_suffixes objectForKey: @"ICON_PATH"]; + /* + * Load file extension preferences. + */ + extPrefPath = [home stringByAppendingPathComponent: extPrefName]; + [extPrefPath retain]; + data = [NSData dataWithContentsOfFile: extPrefPath]; + if (data) + { + dict = [NSDeserializer deserializePropertyListFromData: data + mutableContainers: NO]; + extPreferences = [dict retain]; + } + + /* + * Load cached application information. + */ + appListPath = [home stringByAppendingPathComponent: appListName]; + [appListPath retain]; + data = [NSData dataWithContentsOfFile: appListPath]; + if (data) + { + dict = [NSDeserializer deserializePropertyListFromData: data + mutableContainers: NO]; + applications = [dict retain]; + } [gnustep_global_lock unlock]; } @@ -179,31 +155,46 @@ static NSString* library_combo = sharedWorkspace = (NSWorkspace*)NSAllocateObject(self, 0, NSDefaultMallocZone()); + [NSNotificationCenter addObserver: sharedWorkspace + selector: @selector(noteUserDefaultsChanged) + name: NSUserDefaultsDidChangeNotification + object: nil]; } [gnustep_global_lock unlock]; } return sharedWorkspace; } +static NSImage* +extIconForApp(NSWorkspace *ws, NSString *appName, NSDictionary *typeInfo) +{ + NSString *file = [typeInfo objectForKey: @"NSIcon"]; + + if (file) + { + if ([file isAbsolutePath] == NO) + { + NSString *path; + + path = [ws fullPathForApplication: appName]; + file = [path stringByAppendingString: file]; + } + if ([[NSFileManager defaultManager] isReadableFileAtPath: file] == YES) + { + return [[[NSImage alloc] initWithContentsOfFile: file] autorelease]; + } + } + return nil; +} + - (NSImage*) _getImageWithName: (NSString *)name alternate: (NSString *)alternate { - NSString *iconName = nil; NSImage *image = nil; - iconName = (NSString *)[_suffixes objectForKey: name]; - if (iconName != nil) - { - NSString *iconPath; - - iconPath = [defaultIconPath stringByAppendingPathComponent: iconName]; - image = [[NSImage alloc] initWithContentsOfFile: iconPath]; - } - if ((image == nil) && (alternate != nil)) - { - image = [[NSImage imageNamed: alternate] retain]; // !!! was retained - } - + image = [NSImage imageNamed: name]; + if (image == nil) + image = [NSImage imageNamed: alternate]; return image; } @@ -214,8 +205,8 @@ static NSString* library_combo = if (image == nil) { - image = [self _getImageWithName: @"FOLDER_ICON" - alternate: @"Folder.tiff"]; + image = [[self _getImageWithName: @"Folder.tiff" + alternate: @"common_Folder.tiff"] retain]; } return image; @@ -228,8 +219,8 @@ static NSString* library_combo = if (image == nil) { - image = [self _getImageWithName: @"UNKNOWN_ICON" - alternate: @"Unknown"]; + image = [[self _getImageWithName: @"Unknown.tiff" + alternate: @"common_Unknown.tiff"] retain]; } return image; @@ -242,13 +233,222 @@ static NSString* library_combo = if (image == nil) { - image = [self _getImageWithName: @"ROOT_ICON" - alternate: @"Unknown"]; + image = [[self _getImageWithName: @"Root_PC.tiff" + alternate: @"common_Root_PC.tiff"] retain]; } return image; } +- (NSImage*) _iconForExtension: (NSString*)ext +{ + NSImage *icon = nil; + + if (ext == nil || [ext isEqualToString: @""]) + return nil; + /* + * extensions are case-insensitive - convert to lowercase. + */ + ext = [ext lowercaseString]; + if ((icon = [iconMap objectForKey: ext]) == nil) + { + NSDictionary *prefs; + NSDictionary *extInfo; + NSString *iconPath; + + /* + * If there is a user-specified preference for an image - + * try to use that one. + */ + prefs = [extPreferences objectForKey: ext]; + iconPath = [prefs objectForKey: @"Icon"]; + if (iconPath) + { + icon = [[NSImage alloc] initWithContentsOfFile: iconPath]; + [icon autorelease]; + } + + if (icon == nil && (extInfo = [self infoForExtension: ext]) != nil) + { + NSDictionary *typeInfo; + NSString *appName; + + /* + * If there are any application preferences given, try to use the + * icon for this file that is used by the preferred app. + */ + if (prefs) + { + if ((appName = [extInfo objectForKey: @"Editor"]) != nil) + { + typeInfo = [extInfo objectForKey: appName]; + icon = extIconForApp(self, appName, typeInfo); + } + if (icon == nil + && (appName = [extInfo objectForKey: @"Viewer"]) != nil) + { + typeInfo = [extInfo objectForKey: appName]; + icon = extIconForApp(self, appName, typeInfo); + } + } + + if (icon == nil) + { + NSEnumerator *enumerator; + + /* + * Still no icon - try all the apps that handle this file + * extension. + */ + enumerator = [extInfo keyEnumerator]; + while (icon == nil && (appName = [enumerator nextObject]) != nil) + { + typeInfo = [extInfo objectForKey: appName]; + icon = extIconForApp(self, appName, typeInfo); + } + } + } + + if (icon == nil) + { + } + + /* + * Nothing found at all - use the unknowntype icon. + */ + if (icon == nil) + { + icon = [self unknownFiletypeImage]; + } + + /* + * Set the icon in the cache for next time. + */ + if (icon != nil) + [iconMap setObject: icon forKey: ext]; + } + return icon; +} + +- (BOOL) _extension: (NSString*)ext + role: (NSString*)role + app: (NSString**)app + andInfo: (NSDictionary**)inf +{ + NSEnumerator *enumerator; + NSString *appName = nil; + NSDictionary *apps; + NSDictionary *prefs; + NSDictionary *info; + + ext = [ext lowercaseString]; + apps = [self infoForExtension: ext]; + if (apps == nil || [apps count] == 0) + return NO; + + /* + * Look for the name of the preferred app in this role. + * A 'nil' roll is a wildcard - find the preferred Editor or Viewer. + */ + prefs = [extPreferences objectForKey: ext]; + + if (role == nil || [role isEqualToString: @"Editor"]) + { + appName = [prefs objectForKey: @"Editor"]; + if (appName) + { + info = [apps objectForKey: appName]; + if (info) + { + if (app) + *app = appName; + if (inf) + *inf = info; + return YES; + } + } + } + if (role == nil || [role isEqualToString: @"Viewer"]) + { + appName = [prefs objectForKey: @"Viewer"]; + if (appName) + { + info = [apps objectForKey: appName]; + if (info) + { + if (app) + *app = appName; + if (inf) + *inf = info; + return YES; + } + } + } + + /* + * Go through the dictionary of apps that know about this file type and + * determine the best application to open the file by examining the + * type information for each app. + * The 'NSRole' field specifies what the app can do with the file - if it + * is missing, we assume an 'Editor' role. + */ + enumerator = [apps keyEnumerator]; + + if (role == nil) + { + BOOL found = NO; + + /* + * If the requested role is 'nil', we can accept an app that is either + * an Editor (preferred) or a Viewer. + */ + while ((appName = [enumerator nextObject]) != nil) + { + NSString *str; + + info = [apps objectForKey: appName]; + str = [info objectForKey: @"NSRole"]; + if (str == nil || [str isEqualToString: @"Editor"]) + { + if (app) + *app = appName; + if (inf) + *inf = info; + return YES; + } + else if ([str isEqualToString: @"Viewer"]) + { + if (app) + *app = appName; + if (inf) + *inf = info; + found = YES; + } + } + return found; + } + else + { + while ((appName = [enumerator nextObject]) != nil) + { + NSString *str; + + info = [apps objectForKey: appName]; + str = [info objectForKey: @"NSRole"]; + if ((str == nil && [role isEqualToString: @"Editor"]) + || [str isEqualToString: role]) + { + if (app) + *app = appName; + if (inf) + *inf = info; + return YES; + } + } + return NO; + } +} + // // Instance methods @@ -272,21 +472,9 @@ static NSString* library_combo = - (BOOL) openFile: (NSString *)fullPath { NSString *ext = [fullPath pathExtension]; - NSDictionary *map; - NSArray *apps; NSString *appName; - /* - * Get the applications cache (generated by the make_services tool) - * and lookup the special entry that contains a dictionary of all - * file extensions recognised by GNUstep applications. Then find - * the array of applications that can handle our file. - */ - if (applications == nil) - [self findApplications]; - map = [applications objectForKey: @"GSExtensionsMap"]; - apps = [map objectForKey: ext]; - if (apps == nil || [apps count] == 0) + if ([self _extension: ext role: nil app: &appName andInfo: 0] == NO) { NSRunAlertPanel(nil, [NSString stringWithFormat: @@ -295,9 +483,6 @@ static NSString* library_combo = return NO; } - /* FIXME - need a mechanism for determining default application */ - appName = [apps objectAtIndex: 0]; - return [self openFile: fullPath withApplication: appName]; } @@ -369,10 +554,10 @@ static NSString* library_combo = // Manipulating Files // - (BOOL) performFileOperation: (NSString *)operation - source: (NSString *)source - destination: (NSString *)destination - files: (NSArray *)files - tag: (int *)tag + source: (NSString *)source + destination: (NSString *)destination + files: (NSArray *)files + tag: (int *)tag { return NO; } @@ -428,45 +613,64 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath NSImage *image = nil; BOOL isDir = NO; NSString *iconPath = nil; - NSString *pathExtension = nil; + NSString *pathExtension = [[aPath pathExtension] lowercaseString]; NSFileManager *mgr = [NSFileManager defaultManager]; if ([mgr fileExistsAtPath: aPath isDirectory: &isDir] && isDir) { - // we have a directory - iconPath = [aPath stringByAppendingPathComponent: @".dir.tiff"]; - - NSLog(@"iconPath is '%@'", iconPath); - - NS_DURING + if ([pathExtension isEqualToString: @"app"] + || [pathExtension isEqualToString: @"debug"] + || [pathExtension isEqualToString: @"profile"]) { - image = [[NSImage alloc] initWithContentsOfFile: iconPath]; - [image autorelease]; - } - NS_HANDLER - { - NSLog(@"BAD TIFF FILE '%@'", iconPath); - } - NS_ENDHANDLER + NSBundle *bundle; - NSLog(@"aPath is '%@'", aPath); - - - if (((!image) - && (pathExtension = [aPath pathExtension])) - && ([pathExtension isEqual: @""] == NO)) - { - if ((iconPath = [[_suffixes objectForKey: pathExtension] - objectForKey: @"ICON"]) != nil) + bundle = [NSBundle bundleWithPath: aPath]; + iconPath = [[bundle infoDictionary] objectForKey: @"NSIcon"]; + if (iconPath && [iconPath isAbsolutePath] == NO) { - NSLog(@"using '%@'", - [defaultIconPath stringByAppendingPathComponent: iconPath]); + iconPath = [aPath stringByAppendingPathComponent: iconPath]; + } + /* + * If there is no icon specified in the Info.plist for app + * try 'wrapper/app.tiff' and 'wrapper/.dir.tiff' as + * possible locations for the application icon. + */ + if (iconPath == nil) + { + NSString *str; - image = [[NSImage alloc] initWithContentsOfFile: - [defaultIconPath stringByAppendingPathComponent: iconPath]]; - [image autorelease]; + str = [[aPath lastPathComponent] stringByDeletingPathExtension]; + iconPath = [aPath stringByAppendingPathComponent: str]; + iconPath = [iconPath stringByAppendingPathExtension: @"tiff"]; + if ([mgr isReadableFileAtPath: iconPath] == NO) + { + str = @".dir.tiff"; + iconPath = [aPath stringByAppendingPathComponent: str]; + if ([mgr isReadableFileAtPath: iconPath] == NO) + iconPath = nil; + } + } + + if (iconPath) + { + NS_DURING + { + image = [[NSImage alloc] initWithContentsOfFile: iconPath]; + [image autorelease]; + } + NS_HANDLER + { + NSLog(@"BAD TIFF FILE '%@'", iconPath); + } + NS_ENDHANDLER } } + + if (image == nil) + { + image = [self _iconForExtension: pathExtension]; + } + if (image == nil) { if ([aPath isEqual: _rootPath]) @@ -475,23 +679,11 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath image= [self folderImage]; } } - else // not a directory + else { - if (((!image) && (pathExtension = [aPath pathExtension])) - && ([pathExtension isEqual: @""] == NO)) - { - NSLog(@"pathExtension is '%@'",pathExtension); - if ((iconPath = [[_suffixes objectForKey: pathExtension] - objectForKey: @"ICON"]) != nil) - { - NSLog(@"using '%@'", - [defaultIconPath stringByAppendingPathComponent: iconPath]); + NSDebugLog(@"pathExtension is '%@'", pathExtension); - image = [[NSImage alloc] initWithContentsOfFile: - [defaultIconPath stringByAppendingPathComponent: iconPath]]; - [image autorelease]; - } - } + image = [self _iconForExtension: pathExtension]; } if (image == nil) @@ -538,7 +730,7 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath { static NSString *path = nil; NSData *data; - NSDictionary *newApps; + NSDictionary *dict; NSTask *task; /* @@ -552,14 +744,25 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath if (task != nil) [task waitUntilExit]; + data = [NSData dataWithContentsOfFile: extPrefPath]; + if (data) + { + dict = [NSDeserializer deserializePropertyListFromData: data + mutableContainers: NO]; + ASSIGN(extPreferences, dict); + } + data = [NSData dataWithContentsOfFile: appListPath]; if (data) - newApps = [NSDeserializer deserializePropertyListFromData: data - mutableContainers: NO]; - else - newApps = [NSDictionary dictionary]; - - ASSIGN(applications, newApps); + { + dict = [NSDeserializer deserializePropertyListFromData: data + mutableContainers: NO]; + ASSIGN(applications, dict); + } + /* + * Invalidate the cache of icons for file extensions. + */ + [iconMap removeAllObjects]; } // @@ -674,6 +877,7 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath - (void) noteUserDefaultsChanged { userDefaultsChanged = YES; + } - (BOOL) userDefaultsChanged @@ -702,3 +906,141 @@ inFileViewerRootedAtPath: (NSString *)rootFullpath @end +@implementation NSWorkspace (GNUstep) + +- (NSString*) getBestAppInRole: (NSString*)role + forExtension: (NSString*)ext +{ + NSString *appName = nil; + + if (extPreferences != nil) + { + NSDictionary *inf; + + inf = [extPreferences objectForKey: [ext lowercaseString]]; + if (inf != nil) + { + if (role == nil) + { + appName = [inf objectForKey: @"Editor"]; + if (appName == nil) + appName = [inf objectForKey: @"Viewer"]; + } + else + { + appName = [inf objectForKey: role]; + } + } + } + return appName; +} + +- (NSString*) getBestIconForExtension: (NSString*)ext +{ + NSString *iconPath = nil; + + if (extPreferences != nil) + { + NSDictionary *inf; + + inf = [extPreferences objectForKey: [ext lowercaseString]]; + if (inf != nil) + iconPath = [inf objectForKey: @"Icon"]; + } + return iconPath; +} + +- (NSDictionary*) infoForExtension: (NSString*)ext +{ + NSDictionary *map; + + ext = [ext lowercaseString]; + /* + * Get the applications cache (generated by the make_services tool) + * and lookup the special entry that contains a dictionary of all + * file extensions recognised by GNUstep applications. Then find + * the dictionary of applications that can handle our file. + */ + if (applications == nil) + [self findApplications]; + map = [applications objectForKey: @"GSExtensionsMap"]; + return [map objectForKey: ext]; +} + +- (void) setBestApp: (NSString*)appName + inRole: (NSString*)role + forExtension: (NSString*)ext +{ + NSMutableDictionary *map; + NSMutableDictionary *inf; + NSData *data; + + ext = [ext lowercaseString]; + if (extPreferences) + map = [extPreferences mutableCopy]; + else + map = [NSMutableDictionary new]; + + inf = [[map objectForKey: ext] mutableCopy]; + if (inf == nil) + { + inf = [NSMutableDictionary new]; + } + if (appName == nil) + { + if (role == nil) + { + NSString *iconPath = [inf objectForKey: @"Icon"]; + + [iconPath retain]; + [inf removeAllObjects]; + if (iconPath) + { + [inf setObject: iconPath forKey: @"Icon"]; + } + } + else + { + [inf removeObjectForKey: role]; + } + } + else + { + [inf setObject: appName forKey: (role ? role : @"Editor")]; + } + [map setObject: inf forKey: ext]; + [inf release]; + [extPreferences release]; + extPreferences = inf; + data = [NSSerializer serializePropertyList: extPreferences]; + [data writeToFile: extPrefPath atomically: YES]; +} + +- (void) setBestIcon: (NSString*)iconPath forExtension: (NSString*)ext +{ + NSMutableDictionary *map; + NSMutableDictionary *inf; + NSData *data; + + ext = [ext lowercaseString]; + if (extPreferences) + map = [extPreferences mutableCopy]; + else + map = [NSMutableDictionary new]; + + inf = [[map objectForKey: ext] mutableCopy]; + if (inf == nil) + inf = [NSMutableDictionary new]; + if (iconPath) + [inf setObject: iconPath forKey: @"Icon"]; + else + [inf removeObjectForKey: @"Icon"]; + [map setObject: inf forKey: ext]; + [inf release]; + [extPreferences release]; + extPreferences = inf; + data = [NSSerializer serializePropertyList: extPreferences]; + [data writeToFile: extPrefPath atomically: YES]; +} + +@end