Big improvement of XIB loading. Now all XIB files from the bug reports

get loaded.


git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@34050 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Fred Kiefer 2011-10-24 14:44:29 +00:00
parent 5d85ea1040
commit a47199291f
5 changed files with 352 additions and 103 deletions

View file

@ -1,3 +1,14 @@
2011-10-24 Fred Kiefer <FredKiefer@gmx.de>
* Source/NSBox.m (-initWithCoder:): Rearrange order of keyed
decoding.
* Source/GSNibLoading.m: Warn about missing custom resources.
* Headers/Additions/GNUstepGUI/GSXibLoading.h: Add class IBToolTipAttribute.
* Source/GSXibLoader.m: Add decoding of type "boolean". Fix
decoding of "real", "integer" and "dictionary". Add handling of
flattenedProperties to determine which windows should be visible.
Store all top level obejcts in array.
2011-10-17 Fred Kiefer <FredKiefer@gmx.de>
* Source/GSXibLoader.m (-objectForXib:): Add type "real" and

View file

@ -98,6 +98,14 @@
}
@end
@interface IBToolTipAttribute: NSObject
{
NSString *name;
id object;
NSString *toolTip;
}
@end
@interface IBObjectRecord: NSObject
{
int objectID;
@ -106,6 +114,7 @@
id parent;
}
- (id) object;
- (NSInteger) objectID;
@end
@interface IBMutableOrderedSet: NSObject
@ -113,6 +122,7 @@
NSArray *orderedObjects;
}
- (NSArray *)orderedObjects;
- (id) objectWithObjectID: (NSInteger)objID;
@end
@interface IBObjectContainer: NSObject <NSCoding>
@ -127,7 +137,6 @@
int maxID;
}
- (id) nibInstantiate;
- (NSEnumerator *) objectRecordEnumerator;
@end
@interface GSXibElement: NSObject
@ -156,6 +165,9 @@
GSXibElement *currentElement;
NSMutableDictionary *decoded;
}
- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element;
- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element;
@end
#endif

View file

@ -1239,11 +1239,21 @@ static BOOL _isInInterfaceBuilder = NO;
realObject = RETAIN([NSImage imageNamed: _resourceName]);
}
// if an object has been substituted, then release the placeholder.
if (realObject != nil)
if (realObject == nil)
{
RELEASE(self);
NSLog(@"Could not load NSCustomResource %@ for class %@", _resourceName, _className);
// Use a default instead of the missing object
if ([_className isEqual: @"NSSound"])
{
realObject = RETAIN([NSSound soundNamed: @"Ping"]);
}
else if ([_className isEqual: @"NSImage"])
{
realObject = RETAIN([NSImage imageNamed: @"GNUstep"]);
}
}
// The object has been substituted, release the placeholder.
RELEASE(self);
}
else
{

View file

@ -48,6 +48,10 @@
- (void) _setMainMenu: (NSMenu*)aMenu;
@end
@interface NSCustomObject (NibCompatibility)
- (void) setRealObject: (id)obj;
@end
@implementation FirstResponder
+ (id) allocWithZone: (NSZone*)zone
@ -360,6 +364,44 @@
@end
@implementation IBToolTipAttribute
- (id) initWithCoder: (NSCoder*)coder
{
if ([coder allowsKeyedCoding])
{
if ([coder containsValueForKey: @"name"])
{
name = [coder decodeObjectForKey: @"name"];
}
if ([coder containsValueForKey: @"object"])
{
ASSIGN(object, [coder decodeObjectForKey: @"object"]);
}
if ([coder containsValueForKey: @"toolTip"])
{
ASSIGN(toolTip, [coder decodeObjectForKey: @"toolTip"]);
}
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Can't decode %@ with %@.",NSStringFromClass([self class]),
NSStringFromClass([coder class])];
}
return self;
}
- (void) dealloc
{
DESTROY(name);
DESTROY(object);
DESTROY(toolTip);
[super dealloc];
}
@end
@implementation IBObjectRecord
- (id) initWithCoder: (NSCoder*)coder
@ -405,6 +447,11 @@
return object;
}
- (NSInteger) objectID
{
return objectID;
}
@end
@implementation IBMutableOrderedSet
@ -436,6 +483,23 @@
{
return orderedObjects;
}
- (id) objectWithObjectID: (NSInteger)objID
{
NSEnumerator *en;
IBObjectRecord *obj;
en = [orderedObjects objectEnumerator];
while ((obj = [en nextObject]) != nil)
{
if ([obj objectID] == objID)
{
return [obj object];
}
}
return nil;
}
@end
@implementation IBObjectContainer
@ -452,6 +516,10 @@
{
ASSIGN(objectRecords, [coder decodeObjectForKey: @"objectRecords"]);
}
if ([coder containsValueForKey: @"flattenedProperties"])
{
ASSIGN(flattenedProperties, [coder decodeObjectForKey: @"flattenedProperties"]);
}
// We could load more data here, but we currently don't need it.
}
else
@ -484,6 +552,7 @@
{
NSEnumerator *en;
id obj;
NSString *key;
en = [connectionRecords objectEnumerator];
// iterate over connections, instantiate, and then establish them.
@ -493,12 +562,45 @@
[obj establishConnection];
}
return self;
}
// awaken all objects.
en = [[objectRecords orderedObjects] objectEnumerator];
while ((obj = [en nextObject]) != nil)
{
obj = [obj object];
if ([obj respondsToSelector: @selector(awakeFromNib)])
{
[obj awakeFromNib];
}
}
- (NSEnumerator *) objectRecordEnumerator
{
return [[objectRecords orderedObjects] objectEnumerator];
// Activate windows
en = [flattenedProperties keyEnumerator];
while ((key = [en nextObject]) != nil)
{
if ([key hasSuffix: @"visibleAtLaunch"])
{
id value = [flattenedProperties objectForKey: key];
if ([value boolValue] == YES)
{
NSInteger objID = [key integerValue];
obj = [objectRecords objectWithObjectID: objID];
if ([obj respondsToSelector: @selector(nibInstantiate)])
{
obj = [obj nibInstantiate];
}
if ([obj isKindOfClass: [NSWindow class]])
{
// bring visible windows to front...
[(NSWindow *)obj orderFront: self];
}
}
}
}
return self;
}
@end
@ -527,55 +629,43 @@
NSEnumerator *en;
id obj;
NSMutableArray *topLevelObjects = [context objectForKey: NSNibTopLevelObjects];
//id owner = [context objectForKey: NSNibOwner];
id owner = [context objectForKey: NSNibOwner];
// FIXME: Use the owner as first root object
// Use the owner as first root object
[(NSCustomObject*)[rootObjects objectAtIndex: 0] setRealObject: owner];
en = [rootObjects objectEnumerator];
while ((obj = [en nextObject]) != nil)
{
if ([obj respondsToSelector: @selector(nibInstantiate)])
{
obj = [obj nibInstantiate];
}
if (obj != nil)
{
[topLevelObjects addObject: obj];
// All top level objects must be released by the caller to avoid
// leaking, unless they are going to be released by other nib
// objects on behalf of the owner.
RETAIN(obj);
}
// instantiate all windows and fill in the top level array.
if ([obj isKindOfClass: [NSWindow class]])
{
// bring visible windows to front...
//if ([obj isVisible])
{
[(NSWindow *)obj orderFront: self];
}
}
else if ([obj isKindOfClass: [NSMenu class]])
if ([obj isKindOfClass: [NSMenu class]])
{
// add the menu...
[NSApp _setMainMenu: obj];
}
}
// Load connections
// Load connections and awaken objects
[objects nibInstantiate];
// awaken all objects.
en = [objects objectRecordEnumerator];
while ((obj = [en nextObject]) != nil)
{
obj = [obj object];
if ([obj respondsToSelector: @selector(awakeFromNib)])
{
[obj awakeFromNib];
}
}
}
- (BOOL) loadModelData: (NSData *)data
externalNameTable: (NSDictionary *)context
withZone: (NSZone *)zone;
{
BOOL loaded = NO;
BOOL loaded = NO;
NSKeyedUnarchiver *unarchiver = nil;
NS_DURING
@ -719,8 +809,8 @@
- (NSString*) description
{
return [NSString stringWithFormat:
@"GSXibElement <%@> attrs (%@) elements [%@] %@",
type, attributes, elements, value, nil];
@"GSXibElement <%@> attrs (%@) elements [%@] values [%@] %@",
type, attributes, elements, values, value, nil];
}
@end
@ -823,13 +913,9 @@ didStartElement: (NSString *)elementName
}
}
- (id) decodeObjectForXib: (GSXibElement*)element
forClassName: (NSString *)classname
withKey: (NSString *)key
- (id) allocObjectForClassName: (NSString *)classname
{
GSXibElement *last;
Class c = [self classForClassName: classname];
id o, r;
id delegate = [self delegate];
if (c == nil)
@ -855,26 +941,44 @@ didStartElement: (NSString *)elementName
}
}
// Create instance.
return [c allocWithZone: [self zone]];
}
- (id) decodeObjectForXib: (GSXibElement*)element
forClassName: (NSString *)classname
withID: (NSString *)objID
{
GSXibElement *last;
id o, r;
id delegate = [self delegate];
// Create instance.
o = [self allocObjectForClassName: classname];
// Make sure the object stays around, even when replaced.
RETAIN(o);
if (objID != nil)
[decoded setObject: o forKey: objID];
// push
last = currentElement;
currentElement = element;
// Create instance.
o = [c allocWithZone: [self zone]];
// Make sure the object stays around, even when replaced.
RETAIN(o);
if (key != nil)
[decoded setObject: o forKey: key];
r = [o initWithCoder: self];
// pop
currentElement = last;
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (key != nil)
[decoded setObject: o forKey: key];
if (objID != nil)
[decoded setObject: o forKey: objID];
}
r = [o awakeAfterUsingCoder: self];
if (r != o)
{
@ -882,9 +986,10 @@ didStartElement: (NSString *)elementName
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (key != nil)
[decoded setObject: o forKey: key];
if (objID != nil)
[decoded setObject: o forKey: objID];
}
if (delegate != nil)
{
r = [delegate unarchiver: self didDecodeObject: o];
@ -894,36 +999,100 @@ didStartElement: (NSString *)elementName
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (key != nil)
[decoded setObject: o forKey: key];
if (objID != nil)
[decoded setObject: o forKey: objID];
}
}
// Balance the retain above
RELEASE(o);
if (objID != nil)
{
NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID);
}
return AUTORELEASE(o);
}
/*
This method is a copy of decodeObjectForXib:forClassName:withKey:
The only difference being in the way we decode the object and the
missing context switch.
*/
- (id) decodeDictionaryForXib: (GSXibElement*)element
forClassName: (NSString *)classname
withID: (NSString *)objID
{
id o, r;
id delegate = [self delegate];
// Create instance.
o = [self allocObjectForClassName: classname];
// Make sure the object stays around, even when replaced.
RETAIN(o);
if (objID != nil)
[decoded setObject: o forKey: objID];
r = [o initWithDictionary: [self _decodeDictionaryOfObjectsForElement: element]];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
r = [o awakeAfterUsingCoder: self];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
if (delegate != nil)
{
r = [delegate unarchiver: self didDecodeObject: o];
if (r != o)
{
[delegate unarchiver: self
willReplaceObject: o
withObject: r];
ASSIGN(o, r);
if (objID != nil)
[decoded setObject: o forKey: objID];
}
}
// Balance the retain above
RELEASE(o);
// pop
currentElement = last;
if (key != nil)
if (objID != nil)
{
NSDebugLLog(@"XIB", @"decoded object %@ for key %@", o, key);
NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID);
}
return AUTORELEASE(o);
}
- (id) objectForXib: (GSXibElement*)element
{
NSString *elementName;
NSString *key;
NSString *objID;
if (element == nil)
return nil;
NSDebugLLog(@"XIB", @"decoding element %@", element);
key = [element attributeForKey: @"id"];
if (key != nil)
objID = [element attributeForKey: @"id"];
if (objID)
{
id new = [decoded objectForKey: key];
id new = [decoded objectForKey: objID];
if (new != nil)
{
// The object was already decoded as a reference
@ -937,7 +1106,7 @@ didStartElement: (NSString *)elementName
NSString *classname = [element attributeForKey: @"class"];
return [self decodeObjectForXib: element
forClassName: classname
withKey: key];
withID: objID];
}
else if ([@"string" isEqualToString: elementName])
{
@ -956,8 +1125,8 @@ didStartElement: (NSString *)elementName
if (new == nil)
new = @"";
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
@ -965,17 +1134,8 @@ didStartElement: (NSString *)elementName
{
id new = [NSNumber numberWithInt: [[element value] intValue]];
if (key != nil)
[decoded setObject: new forKey: key];
return new;
}
else if ([@"real" isEqualToString: elementName])
{
id new = [NSNumber numberWithFloat: [[element value] floatValue]];
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
@ -983,8 +1143,8 @@ didStartElement: (NSString *)elementName
{
id new = [NSNumber numberWithDouble: [[element value] doubleValue]];
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
@ -992,11 +1152,40 @@ didStartElement: (NSString *)elementName
{
id new = [NSNumber numberWithBool: [[element value] boolValue]];
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"integer" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithInteger: [value integerValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"real" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithFloat: [value floatValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"boolean" isEqualToString: elementName])
{
NSString *value = [element attributeForKey: @"value"];
id new = [NSNumber numberWithBool: [value boolValue]];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
else if ([@"reference" isEqualToString: elementName])
{
@ -1033,17 +1222,8 @@ didStartElement: (NSString *)elementName
{
id new = [element value];
if (key != nil)
[decoded setObject: new forKey: key];
return new;
}
else if ([@"integer" isEqualToString: elementName])
{
id new = [NSNumber numberWithInteger: [[element value] integerValue]];
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
@ -1053,8 +1233,8 @@ didStartElement: (NSString *)elementName
allowLossyConversion: NO];
new = [GSMimeDocument decodeBase64: new];
if (key != nil)
[decoded setObject: new forKey: key];
if (objID != nil)
[decoded setObject: new forKey: objID];
return new;
}
@ -1068,7 +1248,7 @@ didStartElement: (NSString *)elementName
}
return [self decodeObjectForXib: element
forClassName: classname
withKey: key];
withID: objID];
}
else if ([@"dictionary" isEqualToString: elementName])
{
@ -1078,9 +1258,10 @@ didStartElement: (NSString *)elementName
{
classname = @"NSDictionary";
}
return [self decodeObjectForXib: element
forClassName: classname
withKey: key];
return [self decodeDictionaryForXib: element
forClassName: classname
withID: objID];
}
else
{
@ -1092,7 +1273,14 @@ didStartElement: (NSString *)elementName
- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey
{
NSArray *values = [currentElement values];
// FIXME: This is wrong but the only way to keep the code for
// [NSArray-initWithCoder:] working
return [self _decodeArrayOfObjectsForElement: currentElement];
}
- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element
{
NSArray *values = [element values];
int max = [values count];
id list[max];
int i;
@ -1107,6 +1295,27 @@ didStartElement: (NSString *)elementName
return [NSArray arrayWithObjects: list count: max];
}
- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element
{
NSDictionary *elements = [element elements];
NSEnumerator *en;
NSString *key;
NSMutableDictionary *dict;
dict = [[NSMutableDictionary alloc] init];
en = [elements keyEnumerator];
while ((key = [en nextObject]) != nil)
{
id obj = [self objectForXib: [elements objectForKey: key]];
if (obj == nil)
NSLog(@"No object for %@ at key %@", [elements objectForKey: key], key);
else
[dict setObject: obj forKey: key];
}
return AUTORELEASE(dict);
}
- (BOOL) containsValueForKey: (NSString*)aKey
{
GSXibElement *element = [currentElement elementForKey: aKey];

View file

@ -527,11 +527,6 @@
if ([aDecoder allowsKeyedCoding])
{
NSView *contentView = [aDecoder decodeObjectForKey: @"NSContentView"];
NSCell *titleCell = [aDecoder decodeObjectForKey: @"NSTitleCell"];
[self setContentView: contentView];
ASSIGN(_cell, titleCell);
if ([aDecoder containsValueForKey: @"NSBoxType"])
{
int boxType = [aDecoder decodeIntForKey: @"NSBoxType"];
@ -559,6 +554,18 @@
{
[self setContentViewMargins: [aDecoder decodeSizeForKey: @"NSOffsets"]];
}
if ([aDecoder containsValueForKey: @"NSTitleCell"])
{
NSCell *titleCell = [aDecoder decodeObjectForKey: @"NSTitleCell"];
ASSIGN(_cell, titleCell);
}
if ([aDecoder containsValueForKey: @"NSContentView"])
{
NSView *contentView = [aDecoder decodeObjectForKey: @"NSContentView"];
[self setContentView: contentView];
}
}
else
{