New .gorm version updates.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@19649 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Gregory John Casamento 2004-06-29 03:04:28 +00:00
parent e87ca75d37
commit 95a20b1b16
5 changed files with 206 additions and 174 deletions

View file

@ -1,3 +1,25 @@
2004-06-28 Gregory John Casamento <greg_casamento@yahoo.com>
* 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 <stoyan255@ukr.net>
* Headers/AppKit/NSScroller.h: Added _pendingKnobProportion ivar.

View file

@ -35,7 +35,7 @@
#include <AppKit/NSControl.h>
// 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
/*

View file

@ -42,6 +42,7 @@
#include <Foundation/NSKeyValueCoding.h>
#include <Foundation/NSNotification.h>
#include <Foundation/NSArchiver.h>
#include <Foundation/NSSet.h>
#include <AppKit/NSMenu.h>
#include <AppKit/NSView.h>
#include <AppKit/NSTextView.h>
@ -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];
}

View file

@ -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

View file

@ -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