From 2b9356b4de9a0a88a9b65ea98ea00614cceb7417 Mon Sep 17 00:00:00 2001 From: gcasa Date: Tue, 29 Jun 2004 03:04:28 +0000 Subject: [PATCH] New .gorm version updates. git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@19649 72102866-910b-0410-8b05-ffd578937521 --- ChangeLog | 22 ++ Headers/Additions/GNUstepGUI/GSNibTemplates.h | 9 +- Source/GSNibTemplates.m | 251 ++++++++++++------ Source/NSBundleAdditions.m | 71 +---- Source/NSNib.m | 27 +- 5 files changed, 206 insertions(+), 174 deletions(-) diff --git a/ChangeLog b/ChangeLog index 08da46d27..af0e314d6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2004-06-28 Gregory John Casamento + + * Headers/Additions/GNUstepGUI/GSNibTemplates.h: Updated the + GNUSTEP_NIB_VERSION. This constant defines the number used for + the GSNibContainer objects' version. Also added a new topLevelObjects + ivar to the GSNibContainer class and the method to retrieve it. + * Source/GSNibTemplates.m: Move the private class GSNibItemCollector + from NSBundleAdditions.m to GSNibTemplates.m. This helped clean up + code and also facilitated the changes necessary for the new .gorm version. + Also changed the signature of awakeWithContext: so it only takes the context + dictionary as an argument and no longer requires the items to be passed in. + Changed the awakeWithContext method to efficiently retain the objects. + In initWithCoder: and encodeWithCoder: added new section for updated + version. Added some logic to collect the top level items at that time + so that version 0 and version 1 .gorm files load in a precisely equivalent + manner. Also, updated a change made by Alex Malmberg which was causing + the designated initializers to not be called. + * Source/NSBundleAdditions.m: Moved class to GSNibTemplates as described + above. Cleaned up implementation of +loadNibNamed:context:. + * Source/NSNib.m: Removed some of the code which was there to collect top level + items. Also cleaned up some of the code with the new implementation. + 2004-06-28 Serg Stoyan * Headers/AppKit/NSScroller.h: Added _pendingKnobProportion ivar. diff --git a/Headers/Additions/GNUstepGUI/GSNibTemplates.h b/Headers/Additions/GNUstepGUI/GSNibTemplates.h index b5d029004..f563b948a 100644 --- a/Headers/Additions/GNUstepGUI/GSNibTemplates.h +++ b/Headers/Additions/GNUstepGUI/GSNibTemplates.h @@ -35,7 +35,7 @@ #include // versions of the nib container and the templates. -#define GNUSTEP_NIB_VERSION 0 +#define GNUSTEP_NIB_VERSION 1 #define GSSWAPPER_VERSION 0 #define GSWINDOWT_VERSION 0 #define GSVIEWT_VERSION 0 @@ -48,6 +48,7 @@ @class NSString; @class NSDictionary; @class NSMutableDictionary; +@class NSMutableSet; /* * This is the class that holds objects within a nib. @@ -56,11 +57,13 @@ { NSMutableDictionary *nameTable; NSMutableArray *connections; - BOOL _isAwake; + NSMutableSet *topLevelObjects; + BOOL isAwake; } -- (void) awakeWithContext: (NSDictionary *)context topLevelItems: (NSArray *)items; +- (void) awakeWithContext: (NSDictionary *)context; - (NSMutableDictionary*) nameTable; - (NSMutableArray*) connections; +- (NSMutableSet*) topLevelObjects; @end /* diff --git a/Source/GSNibTemplates.m b/Source/GSNibTemplates.m index 070ed48e3..1ed397129 100644 --- a/Source/GSNibTemplates.m +++ b/Source/GSNibTemplates.m @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -70,6 +71,68 @@ static const int currentVersion = 1; // GSNibItem version number... } @end +/* + * This private class is used to collect the nib items while the + * .gorm file is being unarchived. This is done to allow only + * the top level items to be retained in a clean way. The reason it's + * being done this way is because old .gorm files don't have any + * array within the nameTable which indicates the objects which are + * considered top level, so there is no clean and generic way to determine + * this. Basically the top level items are any instances of or instances + * of subclasses of NSMenu, NSWindow, or any controller class. + * It's the last one that's hairy. Controller classes are + * represented in .gorm files by the GSNibItem class, but once they transform + * into the actual class instance it's not easy to tell if it should be + * retained or not since there are a lot of other things stored in the nameTable + * as well. GJC + */ + +static NSString *GSInternalNibItemAddedNotification = @"_GSInternalNibItemAddedNotification"; + +@interface GSNibItemCollector : NSObject +{ + NSMutableArray *items; +} +- (void) handleNotification: (NSNotification *)notification; +- (NSMutableArray *)items; +@end + +@implementation GSNibItemCollector +- (void) handleNotification: (NSNotification *)notification; +{ + id obj = [notification object]; + [items addObject: obj]; +} + +- init +{ + if((self = [super init]) != nil) + { + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + // add myself as an observer and initialize the items array. + [nc addObserver: self + selector: @selector(handleNotification:) + name: GSInternalNibItemAddedNotification + object: nil]; + items = [[NSMutableArray alloc] init]; + } + return self; +} + +- (void) dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver: self]; + RELEASE(items); + [super dealloc]; +} + +- (NSMutableArray *)items +{ + return items; +} +@end + /* * The GSNibContainer class manages the internals of a nib file. */ @@ -84,19 +147,18 @@ static const int currentVersion = 1; // GSNibItem version number... } - (void) awakeWithContext: (NSDictionary *)context - topLevelItems: (NSArray *)items { - if (_isAwake == NO) + if (isAwake == NO) { NSEnumerator *enumerator; NSNibConnector *connection; NSString *key; NSArray *visible; NSMenu *menu; - NSMutableArray *topLevelObjects; + NSMutableArray *topObjects; id obj; - _isAwake = YES; + isAwake = YES; /* * Add local entries into name table. */ @@ -153,6 +215,24 @@ static const int currentVersion = 1; // GSNibItem version number... [NSApp setWindowsMenu: menu]; } + + /* + * See if the user has passed in the NSTopLevelObjects key. + * This is an implementation of an undocumented, but commonly used feature + * of nib files to allow the release of the top level objects in the nib + * file. + */ + obj = [context objectForKey: @"NSTopLevelObjects"]; + if([obj isKindOfClass: [NSMutableArray class]]) + { + topObjects = obj; + } + else + { + topObjects = nil; + } + + /* * Now tell all the objects that they have been loaded from * a nib. @@ -168,76 +248,49 @@ static const int currentVersion = 1; // GSNibItem version number... [key isEqualToString: @"NSVisible"] == NO && // also exclude any other special parts of the nameTable. [key isEqualToString: @"NSDeferred"] == NO && [key isEqualToString: @"NSTopLevelObjects"] == NO && - [key isEqualToString: @"GSCustomClassMap"] == NO) + [key isEqualToString: @"GSCustomClassMap"] == NO) { id o = [nameTable objectForKey: key]; + + // send the awake message, if it responds... if ([o respondsToSelector: @selector(awakeFromNib)]) { [o awakeFromNib]; } + + /* + * Retain all "top level" items so that, when the container is released, they will remain. + * The GSNibItems instantiated in the gorm need to be retained, + * since we are deallocating the container. We don't want to retain the owner. + */ + if([key isEqualToString: @"NSOwner"] == NO) + { + if([topLevelObjects containsObject: o]) // anything already designated a top level item.. + { + if(topObjects == nil) + { + // It is expected, if the NSTopLevelObjects key is not passed in, + // that the user has opted to either allow these objects to leak or + // to release them explicitly. + RETAIN(o); + } + else + { + // We don't want to do the extra retain if the items are added to the + // array, since the array will do the retain for us. When the array + // is released, the top level objects should be released as well. + [topObjects addObject: o]; + } + } + } } } } - /* - * See if the user has passed in the NSTopLevelObjects key. - * This is an implementation of an undocumented, but commonly used feature - * of nib files to allow the release of the top level objects in the nib - * file. - */ - obj = [context objectForKey: @"NSTopLevelObjects"]; - if([obj isKindOfClass: [NSMutableArray class]]) - { - topLevelObjects = obj; - } - else - { - topLevelObjects = nil; - } - - /* - * Retain all "top level" items so that, when the container is released, they will remain. - * The GSNibItems instantiated in the gorm need to be retained, - * since we are deallocating the container. - */ - enumerator = [nameTable keyEnumerator]; - while ((key = [enumerator nextObject]) != nil) - { - if ([context objectForKey: key] == nil && - [key isEqualToString: @"NSWindowsMenu"] == NO && // exclude special sections. - [key isEqualToString: @"NSServicesMenu"] == NO && - [key isEqualToString: @"NSVisible"] == NO && - [key isEqualToString: @"NSDeferred"] == NO && - [key isEqualToString: @"NSTopLevelObjects"] == NO && - [key isEqualToString: @"GSCustomClassMap"] == NO) - { - id o = [nameTable objectForKey: key]; - // RETAIN all top-level items... - if (([o isKindOfClass: [NSMenu class]] == YES && - [key isEqualToString: @"NSMenu"] == YES) || // the main menu... - ([o isKindOfClass: [NSWindow class]] == YES) || // any windows... - ([items containsObject: o] == YES)) - { - if(topLevelObjects == nil) - { - // It is expected, if the NSTopLevelObjects key is not passed in, - // that the user has opted to either allow these objects to leak or - // to release them explicitly. - RETAIN(o); - } - else - { - // We don't want to do the extra retain if the items are added to the - // array, since the array will do the retain for us. When the array - // is released, the top level objects should be released as well. - [topLevelObjects addObject: o]; - } - } - } - } - /* * See if there are objects that should be made visible. + * This is the last thing we should do since changes might be made + * in the awakeFromNib methods which are called on all of the objects. */ visible = [nameTable objectForKey: @"NSVisible"]; if (visible != nil @@ -274,6 +327,7 @@ static const int currentVersion = 1; // GSNibItem version number... { RELEASE(nameTable); RELEASE(connections); + RELEASE(topLevelObjects); [super dealloc]; } @@ -281,6 +335,7 @@ static const int currentVersion = 1; // GSNibItem version number... { [aCoder encodeObject: nameTable]; [aCoder encodeObject: connections]; + [aCoder encodeObject: topLevelObjects]; } - (id) init @@ -289,6 +344,7 @@ static const int currentVersion = 1; // GSNibItem version number... { nameTable = [[NSMutableDictionary alloc] initWithCapacity: 8]; connections = [[NSMutableArray alloc] initWithCapacity: 8]; + topLevelObjects = [[NSMutableSet alloc] initWithCapacity: 8]; } return self; } @@ -296,11 +352,45 @@ static const int currentVersion = 1; // GSNibItem version number... - (id) initWithCoder: (NSCoder*)aCoder { int version = [aCoder versionForClassName: @"GSNibContainer"]; - + + // save the version to the ivar, we need it later. if(version == GNUSTEP_NIB_VERSION) { [aCoder decodeValueOfObjCType: @encode(id) at: &nameTable]; [aCoder decodeValueOfObjCType: @encode(id) at: &connections]; + [aCoder decodeValueOfObjCType: @encode(id) at: &topLevelObjects]; + } + else if(version == 0) + { + GSNibItemCollector *nibitems = [[GSNibItemCollector alloc] init]; + NSEnumerator *en; + NSString *key; + + // initialize the set of top level objects... + topLevelObjects = [[NSMutableSet alloc] initWithCapacity: 8]; + + // unarchive... + [aCoder decodeValueOfObjCType: @encode(id) at: &nameTable]; + [aCoder decodeValueOfObjCType: @encode(id) at: &connections]; + [topLevelObjects addObjectsFromArray: [nibitems items]]; // get the top level items here... + RELEASE(nibitems); + + // iterate through the objects returned + en = [nameTable keyEnumerator]; + while((key = [en nextObject]) != nil) + { + id o = [nameTable objectForKey: key]; + if(([o isKindOfClass: [NSMenu class]] && [key isEqual: @"NSMenu"]) || + [o isKindOfClass: [NSWindow class]]) + { + [topLevelObjects addObject: o]; // if it's a top level object, add it. + } + } + } + else + { + [NSException raise: NSInternalInconsistencyException + format: @"Unable to read GSNibContainer version #%d. GSNibContainer version for the installed gui lib is %d.", version, GNUSTEP_NIB_VERSION]; } return self; @@ -310,6 +400,11 @@ static const int currentVersion = 1; // GSNibItem version number... { return nameTable; } + +- (NSMutableSet*) topLevelObjects +{ + return topLevelObjects; +} @end // The first standin objects here are for views and normal objects like controllers @@ -357,7 +452,7 @@ static const int currentVersion = 1; // GSNibItem version number... if (cls == nil) { [NSException raise: NSInternalInconsistencyException - format: @"Unable to find class '%@'", theClass]; + format: @"Unable to find class '%@', it is not linked into the application.", theClass]; } obj = [cls allocWithZone: [self zone]]; @@ -386,7 +481,7 @@ static const int currentVersion = 1; // GSNibItem version number... if (cls == nil) { [NSException raise: NSInternalInconsistencyException - format: @"Unable to find class '%@'", theClass]; + format: @"Unable to find class '%@', it is not linked into the application.", theClass]; } obj = [cls allocWithZone: [self zone]]; @@ -405,14 +500,16 @@ static const int currentVersion = 1; // GSNibItem version number... } // If this is a nib item and not a custom view, then we need to add it to - // the set of things to be retained. - if(obj != nil) + // the set of things to be retained. Also, the initial version of the nib container + // needed this code, but subsequent versions don't, so don't send the notification, + // if the version isn't zero. + if(obj != nil && [aCoder versionForClassName: NSStringFromClass([GSNibContainer class])] == 0) { if([self isKindOfClass: [GSNibItem class]] == YES && [self isKindOfClass: [GSCustomView class]] == NO) { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - [nc postNotificationName: @"__GSInternalNibItemAddedNotification" + [nc postNotificationName: GSInternalNibItemAddedNotification object: obj]; } } @@ -485,7 +582,7 @@ static const int currentVersion = 1; // GSNibItem version number... if(_superClass == nil) { [NSException raise: NSInternalInconsistencyException - format: @"Unable to find class '%@'", superClassName]; + format: @"Unable to find class '%@', it is not linked into the application.", superClassName]; } } return self; @@ -538,7 +635,7 @@ static const int currentVersion = 1; // GSNibItem version number... if(aClass == 0) { [NSException raise: NSInternalInconsistencyException - format: @"Unable to find class '%@'", _className]; + format: @"Unable to find class '%@', it is not linked into the application.", _className]; } // Initialize the object... dont call decode, since this wont @@ -606,7 +703,7 @@ static const int currentVersion = 1; // GSNibItem version number... if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class], @selector(initWithContentRect:styleMask:backing:defer:), NO, NO) != NULL) + if(GSGetMethod([obj class], @selector(initWithContentRect:styleMask:backing:defer:), YES, NO) != NULL) { // if we are not in interface builder, call // designated initializer per spec... @@ -648,7 +745,7 @@ static const int currentVersion = 1; // GSNibItem version number... { if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(initWithFrame:), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(initWithFrame:), YES, NO) != NULL) { NSRect theFrame = [obj frame]; obj = [obj initWithFrame: theFrame]; @@ -677,7 +774,7 @@ static const int currentVersion = 1; // GSNibItem version number... { if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(initWithFrame:), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(initWithFrame:), YES, NO) != NULL) { NSRect theFrame = [obj frame]; obj = [obj initWithFrame: theFrame]; @@ -706,7 +803,7 @@ static const int currentVersion = 1; // GSNibItem version number... { if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(initWithFrame:textContainer:), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(initWithFrame:textContainer:), YES, NO) != NULL) { NSRect theFrame = [obj frame]; id textContainer = [obj textContainer]; @@ -737,7 +834,7 @@ static const int currentVersion = 1; // GSNibItem version number... { if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(initWithTitle:), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(initWithTitle:), YES, NO) != NULL) { NSString *theTitle = [obj title]; obj = [obj initWithTitle: theTitle]; @@ -768,7 +865,7 @@ static const int currentVersion = 1; // GSNibItem version number... /* if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(initWithFrame:), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(initWithFrame:), YES, NO) != NULL) { NSRect theFrame = [obj frame]; obj = [obj initWithFrame: theFrame]; @@ -797,7 +894,7 @@ static const int currentVersion = 1; // GSNibItem version number... { if(![self respondsToSelector: @selector(isInInterfaceBuilder)]) { - if(GSGetMethod([obj class],@selector(init), NO, NO) != NULL) + if(GSGetMethod([obj class],@selector(init), YES, NO) != NULL) { obj = [self init]; } diff --git a/Source/NSBundleAdditions.m b/Source/NSBundleAdditions.m index e0ef3bd79..8ff99ebb5 100644 --- a/Source/NSBundleAdditions.m +++ b/Source/NSBundleAdditions.m @@ -185,66 +185,6 @@ } @end -/* - * This private class is used to collect the nib items while the - * .gorm file is being unarchived. This is done to allow only - * the top level items to be retained in a clean way. The reason it's - * being done this way is because old .gorm files don't have any - * array within the nameTable which indicates the objects which are - * considered top level, so there is no clean and generic way to determine - * this. Basically the top level items are any instances of or instances - * of subclasses of NSMenu, NSWindow, or any controller class. - * It's the last one that's hairy. Controller classes are - * represented in .gorm files by the GSNibItem class, but once they transform - * into the actual class instance it's not easy to tell if it should be - * retained or not since there are a lot of other things stored in the nameTable - * as well. GJC - */ -@interface _GSNibItemCollector : NSObject -{ - NSMutableArray *items; -} -- (void) handleNotification: (NSNotification *)notification; -- (NSMutableArray *)items; -@end - -@implementation _GSNibItemCollector -- (void) handleNotification: (NSNotification *)notification; -{ - id obj = [notification object]; - [items addObject: obj]; -} - -- init -{ - if((self = [super init]) != nil) - { - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - - // add myself as an observer and initialize the items array. - [nc addObserver: self - selector: @selector(handleNotification:) - name: @"__GSInternalNibItemAddedNotification" - object: nil]; - items = [[NSMutableArray alloc] init]; - } - return self; -} - -- (void) dealloc -{ - [[NSNotificationCenter defaultCenter] removeObserver: self]; - RELEASE(items); - [super dealloc]; -} - -- (NSMutableArray *)items -{ - return items; -} -@end - - @implementation NSBundle (NSBundleAdditions) static @@ -286,7 +226,6 @@ Class gmodel_class(void) BOOL loaded = NO; NSUnarchiver *unarchiver = nil; NSString *ext = [fileName pathExtension]; - id nibitems = nil; if ([ext isEqual: @"nib"]) { @@ -346,19 +285,15 @@ Class gmodel_class(void) { id obj; - nibitems = [[_GSNibItemCollector alloc] init]; NSDebugLog(@"Invoking unarchiver"); [unarchiver setObjectZone: zone]; obj = [unarchiver decodeObject]; if (obj != nil) { - NSArray *items = [nibitems items]; if ([obj isKindOfClass: [GSNibContainer class]]) { NSDebugLog(@"Calling awakeWithContext"); - - [obj awakeWithContext: context - topLevelItems: items]; + [obj awakeWithContext: context]; loaded = YES; } else @@ -366,7 +301,7 @@ Class gmodel_class(void) NSLog(@"Nib '%@' without container object!", fileName); } } - RELEASE(nibitems); + // RELEASE(nibitems); RELEASE(unarchiver); } } @@ -375,7 +310,7 @@ Class gmodel_class(void) NS_HANDLER { NSLog(@"Exception occured while loading model: %@",[localException reason]); - TEST_RELEASE(nibitems); + // TEST_RELEASE(nibitems); TEST_RELEASE(unarchiver); } NS_ENDHANDLER diff --git a/Source/NSNib.m b/Source/NSNib.m index c2f4b9578..550cf3923 100644 --- a/Source/NSNib.m +++ b/Source/NSNib.m @@ -111,25 +111,6 @@ NS_ENDHANDLER } -// handle the notification... -- (void) _handleNotification: (NSNotification *)notification -{ - id obj = [notification object]; - [_topLevelItems addObject: obj]; -} - -// subscribe to the notification... -- (void) _addObserver -{ - NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; - - // add myself as an observer and initialize the items array. - [nc addObserver: self - selector: @selector(_handleNotification:) - name: @"__GSInternalNibItemAddedNotification" - object: nil]; -} - - (NSDictionary *) _copyTable: (NSDictionary *)dict { NSMutableDictionary *ctx = nil; @@ -174,7 +155,6 @@ { // load the nib data into memory... _nibData = [NSData dataWithContentsOfURL: nibFileURL]; - [self _addObserver]; } return self; } @@ -202,7 +182,6 @@ // load the nib data into memory... [self _readNibData: fileName]; - [self _addObserver]; } return self; } @@ -226,7 +205,6 @@ { id obj; - _topLevelItems = [[NSMutableArray alloc] init]; [unarchiver setObjectZone: zone]; obj = [unarchiver decodeObject]; if (obj != nil) @@ -234,8 +212,7 @@ if ([obj isKindOfClass: [GSNibContainer class]]) { NSDictionary *nameTable = [self _copyTable: externalNameTable]; - [obj awakeWithContext: nameTable - topLevelItems: _topLevelItems]; + [obj awakeWithContext: nameTable]; loaded = YES; RELEASE(nameTable); } @@ -245,7 +222,6 @@ } } RELEASE(unarchiver); - RELEASE(_topLevelItems); } } } @@ -253,7 +229,6 @@ { NSLog(@"Exception occured while loading model: %@",[localException reason]); TEST_RELEASE(unarchiver); - TEST_RELEASE(_topLevelItems); } NS_ENDHANDLER