Fix bug setting name of an image which already has a name. Added thread safety

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@28743 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2009-09-27 08:54:03 +00:00
parent 106e20f047
commit c560ba5eb2
3 changed files with 114 additions and 61 deletions

View file

@ -1,3 +1,11 @@
2009-09-27 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSImage.m: ([-setName:]) fix bug which was removing the
wrong name when given an image which already had a name.
Make named images thread-safe using a lock.
Add private method to update names on theme change.
* Source/GSTheme.m: Use new method to update theme images.
2009-09-26 Fred Kiefer <FredKiefer@gmx.de> 2009-09-26 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSApplication.m (-setApplicationIconImage:, * Source/NSApplication.m (-setApplicationIconImage:,

View file

@ -78,6 +78,10 @@ NSString *GSThemeWillActivateNotification
NSString *GSThemeWillDeactivateNotification NSString *GSThemeWillDeactivateNotification
= @"GSThemeWillDeactivateNotification"; = @"GSThemeWillDeactivateNotification";
@interface NSImage (GSTheme)
+ (NSImage*) _setImage: (NSImage*)image name: (NSString*)name;
@end
@interface GSTheme (Private) @interface GSTheme (Private)
- (void) _revokeOwnerships; - (void) _revokeOwnerships;
@end @end
@ -366,18 +370,7 @@ typedef struct {
*/ */
if (image != nil && [[image name] isEqualToString: imageName] == NO) if (image != nil && [[image name] isEqualToString: imageName] == NO)
{ {
if ([image setName: imageName] == NO) [NSImage _setImage: image name: imageName];
{
NSImage *old;
/* Couldn't set image name ... presumably already
* in use ... so we remove the name from the old
* image and try again.
*/
old = [NSImage imageNamed: imageName];
[old setName: nil];
[image setName: imageName];
}
} }
} }
} }
@ -584,26 +577,26 @@ typedef struct {
userInfo: nil]; userInfo: nil];
/* /*
* Remove all cached bundle images from both NSImage's name dictionary * 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 * 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 * when re-activated (in case the images on disk changed ... eg by a
* theme editor modifying the theme). * theme editor modifying the theme).
* Restore old images in NSImage's lookup dictionary so that the app
* still has images to draw.
*/ */
enumerator = [_oldImages keyEnumerator];
while ((name = [enumerator nextObject]) != nil)
{
image = [_oldImages objectForKey: name];
[NSImage _setImage: image name: name];
}
[_oldImages removeAllObjects];
enumerator = [_images objectEnumerator]; enumerator = [_images objectEnumerator];
while ((image = [enumerator nextObject]) != nil) while ((image = [enumerator nextObject]) != nil)
{ {
[image setName: nil]; [image setName: nil];
} }
[_images removeAllObjects]; [_images removeAllObjects];
enumerator = [_oldImages keyEnumerator];
while ((name = [enumerator nextObject]) != nil)
{
image = [_oldImages objectForKey: name];
[image setName: name];
}
[_oldImages removeAllObjects];
[self _revokeOwnerships]; [self _revokeOwnerships];

View file

@ -28,31 +28,32 @@
#include <string.h> #include <string.h>
#include <math.h> #include <math.h>
#include <Foundation/NSArray.h> #import <Foundation/NSArray.h>
#include <Foundation/NSBundle.h> #import <Foundation/NSBundle.h>
#include <Foundation/NSDebug.h> #import <Foundation/NSDebug.h>
#include <Foundation/NSDictionary.h> #import <Foundation/NSDictionary.h>
#include <Foundation/NSException.h> #import <Foundation/NSException.h>
#include <Foundation/NSFileManager.h> #import <Foundation/NSFileManager.h>
#include <Foundation/NSKeyedArchiver.h> #import <Foundation/NSLock.h>
#include <Foundation/NSString.h> #import <Foundation/NSKeyedArchiver.h>
#include <Foundation/NSValue.h> #import <Foundation/NSString.h>
#import <Foundation/NSValue.h>
#include "AppKit/NSImage.h" #import "AppKit/NSImage.h"
#include "AppKit/AppKitExceptions.h" #import "AppKit/AppKitExceptions.h"
#include "AppKit/NSAffineTransform.h" #import "AppKit/NSAffineTransform.h"
#include "AppKit/NSBitmapImageRep.h" #import "AppKit/NSBitmapImageRep.h"
#include "AppKit/NSCachedImageRep.h" #import "AppKit/NSCachedImageRep.h"
#include "AppKit/NSColor.h" #import "AppKit/NSColor.h"
#include "AppKit/NSPasteboard.h" #import "AppKit/NSPasteboard.h"
#include "AppKit/NSPrintOperation.h" #import "AppKit/NSPrintOperation.h"
#include "AppKit/NSScreen.h" #import "AppKit/NSScreen.h"
#include "AppKit/NSView.h" #import "AppKit/NSView.h"
#include "AppKit/NSWindow.h" #import "AppKit/NSWindow.h"
#include "AppKit/PSOperators.h" #import "AppKit/PSOperators.h"
#include "GNUstepGUI/GSDisplayServer.h" #import "GNUstepGUI/GSDisplayServer.h"
#include "GSThemePrivate.h" #import "GSThemePrivate.h"
/* Helpers. Would be nicer to use the C99 fmin/fmax functions, but that /* Helpers. Would be nicer to use the C99 fmin/fmax functions, but that
@ -134,9 +135,10 @@ BOOL NSImageForceCaching = NO; /* use on missmatch */
@end @end
/* Class variables and functions for class methods */ /* Class variables and functions for class methods */
static NSMutableDictionary *nameDict = nil; static NSRecursiveLock *imageLock = nil;
static NSDictionary *nsmapping = nil; static NSMutableDictionary *nameDict = nil;
static NSColor *clearColor = nil; static NSDictionary *nsmapping = nil;
static NSColor *clearColor = nil;
static Class cachedClass = 0; static Class cachedClass = 0;
static Class bitmapClass = 0; static Class bitmapClass = 0;
@ -175,35 +177,42 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
+ (void) initialize + (void) initialize
{ {
if (self == [NSImage class]) if (imageLock == nil)
{ {
NSString *path = [NSBundle pathForLibraryResource: @"nsmapping" NSString *path;
ofType: @"strings"
inDirectory: @"Images"]; imageLock = [NSRecursiveLock new];
[imageLock lock];
// Initial version // Initial version
[self setVersion: 1]; [self setVersion: 1];
// initialize the class variables // initialize the class variables
nameDict = [[NSMutableDictionary alloc] initWithCapacity: 10]; nameDict = [[NSMutableDictionary alloc] initWithCapacity: 10];
path = [NSBundle pathForLibraryResource: @"nsmapping"
ofType: @"strings"
inDirectory: @"Images"];
if (path) if (path)
nsmapping = RETAIN([[NSString stringWithContentsOfFile: path] nsmapping = RETAIN([[NSString stringWithContentsOfFile: path]
propertyListFromStringsFileFormat]); propertyListFromStringsFileFormat]);
clearColor = RETAIN([NSColor clearColor]); clearColor = RETAIN([NSColor clearColor]);
cachedClass = [NSCachedImageRep class]; cachedClass = [NSCachedImageRep class];
bitmapClass = [NSBitmapImageRep class]; bitmapClass = [NSBitmapImageRep class];
[imageLock unlock];
} }
} }
+ (id) imageNamed: (NSString *)aName + (id) imageNamed: (NSString *)aName
{ {
NSImage *image = (NSImage*)[nameDict objectForKey: aName]; NSImage *image;
/* 2009-09-10 changed operation of nsmapping so that the loaded /* 2009-09-10 changed operation of nsmapping so that the loaded
* image is stored under the key 'aName', not under the mapped * image is stored under the key 'aName', not under the mapped
* name. That way the image is created with the correct name and * name. That way the image is created with the correct name and
* a later call to -setName: will work properly. * a later call to -setName: will work properly.
*/ */
[imageLock lock];
image = (NSImage*)[nameDict objectForKey: aName];
if (image == nil || [(id)image _resource] == nil) if (image == nil || [(id)image _resource] == nil)
{ {
NSString *realName = [nsmapping objectForKey: aName]; NSString *realName = [nsmapping objectForKey: aName];
@ -217,7 +226,6 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
realName = aName; realName = aName;
} }
// FIXME: This should use [NSBundle pathForImageResource], but this will // FIXME: This should use [NSBundle pathForImageResource], but this will
// only allow imageUnfilteredFileTypes. // only allow imageUnfilteredFileTypes.
/* If there is no image with that name, search in the main bundle */ /* If there is no image with that name, search in the main bundle */
@ -298,7 +306,8 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
image = (NSImage*)[nameDict objectForKey: aName]; image = (NSImage*)[nameDict objectForKey: aName];
} }
} }
IF_NO_GC([[image retain] autorelease]);
[imageLock unlock];
return image; return image;
} }
@ -512,9 +521,7 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
return copy; return copy;
} }
/* This method is used by the GSTheme class to set the names of system /*
* images. It *must* be possible to unset an old system image name by
* passing a nil value to aName.
* The images are actually accessed via proxy objects, so that when a * The images are actually accessed via proxy objects, so that when a
* new system image is set, the proxies for that image just start using * new system image is set, the proxies for that image just start using
* the new version. * the new version.
@ -523,10 +530,13 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
{ {
GSThemeProxy *proxy = nil; GSThemeProxy *proxy = nil;
[imageLock lock];
/* The name is already set... nothing to do. /* The name is already set... nothing to do.
*/ */
if (aName == _name || [aName isEqual: _name] == YES) if (aName == _name || [aName isEqual: _name] == YES)
{ {
[imageLock unlock];
return YES; return YES;
} }
@ -535,23 +545,25 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
*/ */
if (aName != nil && [[nameDict objectForKey: aName] _resource] != nil) if (aName != nil && [[nameDict objectForKey: aName] _resource] != nil)
{ {
[imageLock unlock];
return NO; return NO;
} }
/* If this image had another name, we remove it. /* If this image had another name, we remove it.
*/ */
if (_name && self == [(proxy = [nameDict objectForKey: _name]) _resource]) if (_name != nil)
{ {
/* We retain self in case removing from the dictionary releases us */ /* We retain self in case removing from the dictionary releases us */
IF_NO_GC([[self retain] autorelease]); IF_NO_GC([[self retain] autorelease]);
[nameDict removeObjectForKey: _name];
DESTROY(_name); DESTROY(_name);
[proxy _setResource: nil];
} }
/* If the new name is null, there is nothing more to do. /* If the new name is null, there is nothing more to do.
*/ */
if (aName == nil) if (aName == nil)
{ {
[imageLock unlock];
return NO; return NO;
} }
@ -565,12 +577,18 @@ repd_for_rep(NSArray *_reps, NSImageRep *rep)
} }
[proxy _setResource: self]; [proxy _setResource: self];
[imageLock unlock];
return YES; return YES;
} }
- (NSString *) name - (NSString *) name
{ {
return _name; NSString *name;
[imageLock lock];
name = [[_name retain] autorelease];
[imageLock unlock];
return name;
} }
- (void) setSize: (NSSize)aSize - (void) setSize: (NSSize)aSize
@ -2038,3 +2056,37 @@ iterate_reps_for_types(NSArray* imageReps, SEL method)
} }
@end @end
@implementation NSImage (GSTheme)
+ (NSImage*) _setImage: (NSImage*)image name: (NSString*)name
{
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)
{
proxy = [GSThemeProxy alloc];
[nameDict setObject: proxy forKey: image->_name];
[proxy release];
}
else
{
/* Remove the name from the old image.
*/
DESTROY(((NSImage*)[proxy _resource])->_name);
}
[proxy _setResource: image];
IF_NO_GC([[proxy retain] autorelease]);
[imageLock unlock];
return (NSImage*)proxy;
}
@end