diff --git a/ChangeLog b/ChangeLog index 23bbfbe45..18023d7cf 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,22 @@ +2012-01-09 Eric Wasylishen + + * Source/GSTheme.m: Remove code which sets and unsets images + on theme activation/deactivation. + * Source/NSImage.m (+imageNamed:): Factor out code for finding + the path for a name to a separate method, +pathForImageNamed. + The code is unchanged otherwise, except for fixing the retrieval + of images from the theme bundle, which was broken. + * Source/NSImage.m (-setName:): Remove code for creating theme + proxy. Subscribe/unscribe to theme change notification when + the receiver is added/removed from the name dictionary. + * Source/NSImage.m (-themeDidActivate:): Method called in + response to a GSThemeDidActivateNotification on images with + a name set. It does a path lookup in the same way that + +imageNamed: would, and checks if the path has changed due + to the theme change. If it has, all reps are discarded and + the image at the new path is loaded. This avoids the need for + the theme proxy objects. + 2012-01-09 Fred Kiefer * Source/NSImage.m (+imageUnfilteredFileTypes, +imageFileTypes, diff --git a/Source/GSTheme.m b/Source/GSTheme.m index d25418666..5189d2158 100644 --- a/Source/GSTheme.m +++ b/Source/GSTheme.m @@ -206,12 +206,6 @@ GSStringFromBorderType(NSBorderType borderType) } } - - -@interface NSImage (GSTheme) -+ (NSImage*) _setImage: (NSImage*)image name: (NSString*)name; -@end - @interface GSTheme (Private) - (void) _revokeOwnerships; @end @@ -446,10 +440,7 @@ typedef struct { { NSUserDefaults *defs; NSMutableArray *searchList; - NSArray *imagePaths; NSEnumerator *enumerator; - NSString *imagePath; - NSArray *imageTypes; NSDictionary *infoDict; NSWindow *window; GSThemeControlState state; @@ -465,79 +456,6 @@ typedef struct { _extraColors[state] = nil; } - /* - * We step through all the bundle image resources and load them in - * to memory, setting their names so that they are visible to - * [NSImage+imageNamed:] and storing them in our local array. - */ - imageTypes = [_imageClass imageFileTypes]; - imagePaths = [_bundle pathsForResourcesOfType: nil - inDirectory: @"ThemeImages"]; - enumerator = [imagePaths objectEnumerator]; - while ((imagePath = [enumerator nextObject]) != nil) - { - NSString *ext = [imagePath pathExtension]; - - if (ext != nil && [imageTypes containsObject: ext] == YES) - { - NSImage *image; - NSString *imageName; - - imageName = [imagePath lastPathComponent]; - imageName = [imageName stringByDeletingPathExtension]; - - image = [_images objectForKey: imageName]; - if (image == nil) - { - image = [[_imageClass alloc] initWithContentsOfFile: imagePath]; - if (image == nil) - { - NSLog(@"GSTheme failed to load %@ from %@", - imageName, imagePath); - } - else - { - NSImage *old; - - /* OK, we have loaded a new image, so we cache it for the - * lifetime of the theme, and we also make a record of - * any previous/default image of the same name. - */ - [_images setObject: image forKey: imageName]; - RELEASE(image); - old = [NSImage imageNamed: imageName]; - if (old == nil) - { - /* This could potentially be a real problem ... if the - * image from the current theme with this name is used - * and the theme is unloaded, what happens when the - * app tries to draw using the proxy to the unloaded - * image? - * To avoid that possibility, we save the new image - * as if it were the old one ... so when the theme is - * unloaded the old image persists. - */ - [_oldImages setObject: image forKey: imageName]; - } - else - { - /* Store the actual image, not the proxy. - */ - old = [(id)old _resource]; - [_oldImages setObject: old forKey: imageName]; - } - } - } - - /* We try to ensure that our new image can be found by name. - */ - if (image != nil && [[image name] isEqualToString: imageName] == NO) - { - [NSImage _setImage: image name: imageName]; - } - } - } - /* * Use the GSThemeDomain key in the info dictionary of the theme to * set a defaults domain which will establish user defaults values @@ -757,10 +675,6 @@ typedef struct { - (void) deactivate { - NSEnumerator *enumerator; - NSImage *image; - NSString *name; - NSDebugMLLog(@"GSTheme", @"%@ %p", [self name], self); /* Tell everything that we will become inactive. @@ -783,30 +697,6 @@ typedef struct { } } - /* Unload all images created by this theme. - */ - enumerator = [_images objectEnumerator]; - while ((image = [enumerator nextObject]) != nil) - { - [image setName: nil]; - } - [_images removeAllObjects]; - - /* Restore old images in NSImage's lookup dictionary so that the app - * still has images to draw. - * The remove all cached bundle images from both NSImage's name dictionary - * and our cache dictionary, so that we can be sure we reload afresh - * when re-activated (in case the images on disk changed ... eg by a - * theme editor modifying the theme). - */ - enumerator = [_oldImages keyEnumerator]; - while ((name = [enumerator nextObject]) != nil) - { - image = [_oldImages objectForKey: name]; - [NSImage _setImage: image name: name]; - } - [_oldImages removeAllObjects]; - [self _revokeOwnerships]; /* Tell everything that we have become inactive. diff --git a/Source/NSImage.m b/Source/NSImage.m index e9f524e79..8f8f27119 100644 --- a/Source/NSImage.m +++ b/Source/NSImage.m @@ -37,6 +37,7 @@ #import #import #import +#import #import #import @@ -159,6 +160,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) - (BOOL) _loadFromFile: (NSString *)fileName; - (GSRepData*) _cacheForRep: (NSImageRep*)rep; - (NSCachedImageRep*) _doImageCache: (NSImageRep *)rep; +- (void) themeDidActivate: (NSNotification*)notif; @end @implementation NSImage @@ -195,10 +197,119 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) } } ++ (NSString *) pathForImageNamed: (NSString *)aName +{ + [imageLock lock]; + + NSString *realName = [nsmapping objectForKey: aName]; + NSString *ext; + NSString *path = nil; + NSBundle *main_bundle; + NSArray *array; + + if (realName == nil) + { + realName = aName; + } + + // FIXME: This should use [NSBundle pathForImageResource], but this will + // only allow imageUnfilteredFileTypes. + /* If there is no image with that name, search in the main bundle */ + main_bundle = [NSBundle mainBundle]; + ext = [realName pathExtension]; + if (ext != nil && [ext length] == 0) + { + ext = nil; + } + + /* Check if extension is one of the image types */ + array = [self imageFileTypes]; + if (ext != nil && [array indexOfObject: ext] != NSNotFound) + { + /* Extension is one of the image types + So remove from the name */ + realName = [realName stringByDeletingPathExtension]; + } + else + { + /* Otherwise extension is not an image type + So leave it alone */ + ext = nil; + } + + /* First search locally */ + if (ext) + path = [main_bundle pathForResource: realName ofType: ext]; + else + { + id o, e; + + e = [array objectEnumerator]; + while ((o = [e nextObject])) + { + path = [main_bundle pathForResource: realName + ofType: o]; + if (path != nil && [path length] != 0) + break; + } + } + + /* Second search on theme bundle */ + if (!path) + { + if (ext) + path = [[[GSTheme theme] bundle] pathForResource: realName + ofType: ext + inDirectory: @"ThemeImages"]; + else + { + id o, e; + + e = [array objectEnumerator]; + while ((o = [e nextObject])) + { + path = [[[GSTheme theme] bundle] pathForResource: realName + ofType: o + inDirectory: @"ThemeImages"]; + if (path != nil && [path length] != 0) + break; + } + } + } + + /* If not found then search in system */ + if (!path) + { + if (ext) + { + path = [NSBundle pathForLibraryResource: realName + ofType: ext + inDirectory: @"Images"]; + } + else + { + id o, e; + + e = [array objectEnumerator]; + while ((o = [e nextObject])) + { + path = [NSBundle pathForLibraryResource: realName + ofType: o + inDirectory: @"Images"]; + if (path != nil && [path length] != 0) + break; + } + } + } + + [imageLock unlock]; + return path; +} + + (id) imageNamed: (NSString *)aName { - NSImage *image; - + NSImage *image; + /* 2009-09-10 changed operation of nsmapping so that the loaded * image is stored under the key 'aName', not under the mapped * name. That way the image is created with the correct name and @@ -206,105 +317,9 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) */ [imageLock lock]; image = (NSImage*)[nameDict objectForKey: aName]; - if (image == nil || [(id)image _resource] == nil) + if (image == nil) { - NSString *realName = [nsmapping objectForKey: aName]; - NSString *ext; - NSString *path = nil; - NSBundle *main_bundle; - NSArray *array; - - if (realName == nil) - { - realName = aName; - } - - // FIXME: This should use [NSBundle pathForImageResource], but this will - // only allow imageUnfilteredFileTypes. - /* If there is no image with that name, search in the main bundle */ - main_bundle = [NSBundle mainBundle]; - ext = [realName pathExtension]; - if (ext != nil && [ext length] == 0) - { - ext = nil; - } - - /* Check if extension is one of the image types */ - array = [self imageFileTypes]; - if (ext != nil && [array indexOfObject: ext] != NSNotFound) - { - /* Extension is one of the image types - So remove from the name */ - realName = [realName stringByDeletingPathExtension]; - } - else - { - /* Otherwise extension is not an image type - So leave it alone */ - ext = nil; - } - - /* First search locally */ - if (ext) - path = [main_bundle pathForResource: realName ofType: ext]; - else - { - id o, e; - - e = [array objectEnumerator]; - while ((o = [e nextObject])) - { - path = [main_bundle pathForResource: realName - ofType: o]; - if (path != nil && [path length] != 0) - break; - } - } - - /* Second search on theme bundle */ - if (!path) - { - if (ext) - path = [[[GSTheme theme] bundle] pathForResource: realName ofType: ext]; - else - { - id o, e; - - e = [array objectEnumerator]; - while ((o = [e nextObject])) - { - path = [[[GSTheme theme] bundle] pathForResource: realName - ofType: o]; - if (path != nil && [path length] != 0) - break; - } - } - } - - /* If not found then search in system */ - if (!path) - { - if (ext) - { - path = [NSBundle pathForLibraryResource: realName - ofType: ext - inDirectory: @"Images"]; - } - else - { - id o, e; - - e = [array objectEnumerator]; - while ((o = [e nextObject])) - { - path = [NSBundle pathForLibraryResource: realName - ofType: o - inDirectory: @"Images"]; - if (path != nil && [path length] != 0) - break; - } - } - } + NSString *path = [self pathForImageNamed: aName]; if ([path length] != 0) { @@ -362,7 +377,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) _reps = [[NSMutableArray alloc] initWithCapacity: 2]; ASSIGN(_color, clearColor); _cacheMode = NSImageCacheDefault; - + return self; } @@ -549,8 +564,6 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) */ - (BOOL) setName: (NSString *)aName { - GSThemeProxy *proxy = nil; - [imageLock lock]; /* The name is already set... nothing to do. @@ -564,7 +577,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) /* If the new name is already in use by another image, * we must do nothing. */ - if (aName != nil && [[nameDict objectForKey: aName] _resource] != nil) + if (aName != nil && [nameDict objectForKey: aName] != nil) { [imageLock unlock]; return NO; @@ -577,6 +590,10 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) /* We retain self in case removing from the dictionary releases us */ IF_NO_GC([[self retain] autorelease]); [nameDict removeObjectForKey: _name]; + + [[NSNotificationCenter defaultCenter] removeObserver: self + name: GSThemeDidActivateNotification + object: nil]; DESTROY(_name); } @@ -589,14 +606,13 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep) } ASSIGN(_name, aName); - - if ((proxy = [nameDict objectForKey: _name]) == nil) - { - proxy = [GSThemeProxy alloc]; - [nameDict setObject: proxy forKey: _name]; - [proxy release]; - } - [proxy _setResource: self]; + + [nameDict setObject: self forKey: _name]; + + [[NSNotificationCenter defaultCenter] addObserver: self + selector: @selector(themeDidActivate:) + name: GSThemeDidActivateNotification + object: nil]; [imageLock unlock]; return YES; @@ -2156,53 +2172,18 @@ iterate_reps_for_types(NSArray* imageReps, SEL method) } } -@end - -@implementation NSImage (GSTheme) - -/* This method is used by the theming system to replace a named image - * without disturbing the proxy ... so that all views and cells using - * the named image are automatically updated to use the new image. - * This is the counterpart to the -setName: method, which replaces the - * proxy (to change a named image without updating the image used by - * existing views and cells). - */ -+ (NSImage*) _setImage: (NSImage*)image name: (NSString*)name +- (void) themeDidActivate: (NSNotification *)notif { - GSThemeProxy *proxy = nil; - - NSAssert([image isKindOfClass: [NSImage class]], NSInvalidArgumentException); - NSAssert(![image isProxy], NSInvalidArgumentException); - NSAssert([name isKindOfClass: [NSString class]], NSInvalidArgumentException); - NSAssert([name length] > 0, NSInvalidArgumentException); - NSAssert([image name] == nil, NSInvalidArgumentException); - - [imageLock lock]; - ASSIGNCOPY(image->_name, name); - if ((proxy = [nameDict objectForKey: image->_name]) == nil) + NSString *newPath = [[self class] pathForImageNamed: _name]; + if (newPath != nil && + ![newPath isEqual: _fileName]) { - proxy = [GSThemeProxy alloc]; - [nameDict setObject: proxy forKey: image->_name]; - [proxy release]; + // FIXME: Factor out into a private method for loading + // a new path into an existing NSImage instance? + [_reps removeAllObjects]; + _size = NSZeroSize; + [self _useFromFile: newPath]; } - else - { - /* Remove the name from the old image. - */ - DESTROY(((NSImage*)[proxy _resource])->_name); - } - [proxy _setResource: image]; - IF_NO_GC([[proxy retain] autorelease]); - - /* Force the image to be archived by name. This prevents - * problems such as when/if gorm is being used with a theme - * active, it will not save the image which was loaded - * here and will, instead save the name so that the proper - * image gets loaded in the future. - */ - image->_flags.archiveByName = YES; - - [imageLock unlock]; - return (NSImage*)proxy; } + @end