libs-gui/Source/GSXib5KeyedUnarchiver.m

3100 lines
118 KiB
Objective-C

/** <title>GSXib5KeyedUnarchiver.m</title>
<abstract>The XIB 5 keyed unarchiver</abstract>
Copyright (C) 2016,2017 Free Software Foundation, Inc.
Author: Marcian Lytwyn <gnustep@advcsi.com>
Date: 12/28/16
Modifications: Fred Kiefer <fredkiefer@gmx.de>
Date: December 2019
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#import "GNUstepGUI/GSXib5KeyedUnarchiver.h"
#import "AppKit/NSApplication.h"
#import "AppKit/NSBox.h"
#import "AppKit/NSBrowser.h"
#import "AppKit/NSBrowserCell.h"
#import "AppKit/NSButtonCell.h"
#import "AppKit/NSCell.h"
#import "AppKit/NSClipView.h"
#import "AppKit/NSFormCell.h"
#import "AppKit/NSImage.h"
#import "AppKit/NSMatrix.h"
#import "AppKit/NSMenu.h"
#import "AppKit/NSMenuItem.h"
#import "AppKit/NSNib.h"
#import "AppKit/NSParagraphStyle.h"
#import "AppKit/NSPopUpButton.h"
#import "AppKit/NSPopUpButtonCell.h"
#import "AppKit/NSScroller.h"
#import "AppKit/NSScrollView.h"
#import "AppKit/NSSliderCell.h"
#import "AppKit/NSSplitView.h"
#import "AppKit/NSTableColumn.h"
#import "AppKit/NSTableHeaderView.h"
#import "AppKit/NSTableView.h"
#import "AppKit/NSTabView.h"
#import "AppKit/NSToolbarItem.h"
#import "AppKit/NSView.h"
#import "GSCodingFlags.h"
#define DEBUG_XIB5 0
@implementation GSXib5KeyedUnarchiver
static NSDictionary *XmlTagToObjectClassMap = nil;
static NSArray *XmlTagsNotStacked = nil;
static NSArray *XmlTagsToSkip = nil;
static NSArray *ClassNamePrefixes = nil;
static NSDictionary *XmlKeyMapTable = nil;
static NSDictionary *XmlTagToDecoderSelectorMap = nil;
static NSDictionary *XmlKeyToDecoderSelectorMap = nil;
static NSArray *XmlKeysDefined = nil;
static NSArray *XmlReferenceAttributes = nil;
static NSArray *XmlConnectionRecordTags = nil;
static NSArray *XmlBoolDefaultYes = nil;
+ (void) initialize
{
if (self == [GSXib5KeyedUnarchiver class])
{
// Only check one since we're going to load all once...
if (XmlTagToObjectClassMap == nil)
{
// These define XML tags (i.e. <objects ...) that should be allocated as the
// associated class...
XmlTagToObjectClassMap =
[NSDictionary dictionaryWithObjectsAndKeys:
@"NSMutableArray", @"objects",
@"NSMutableArray", @"items",
@"NSMutableArray", @"tabViewItems",
@"NSMutableArray", @"connections",
@"NSMutableArray", @"subviews",
@"NSMutableArray", @"tableColumns",
@"NSMutableArray", @"cells",
@"NSMutableArray", @"column",
@"NSMutableArray", @"tabStops",
@"NSMutableArray", @"userDefinedRuntimeAttributes",
@"NSMutableArray", @"resources",
@"NSMutableArray", @"segments",
@"NSMutableArray", @"objectValues",
@"NSMutableArray", @"prototypeCellViews",
@"NSMutableArray", @"allowedToolbarItems",
@"NSMutableArray", @"defaultToolbarItems",
@"NSMutableArray", @"rowTemplates",
@"NSSegmentItem", @"segment",
@"NSCell", @"customCell",
@"NSCustomObject", @"customObject",
@"IBOutletConnection", @"outlet",
@"IBActionConnection", @"action",
@"NSNibBindingConnector", @"binding",
@"NSWindowTemplate", @"window",
@"NSView", @"tableCellView",
@"IBUserDefinedRuntimeAttribute", @"userDefinedRuntimeAttribute",
nil];
RETAIN(XmlTagToObjectClassMap);
XmlTagsNotStacked = [NSArray arrayWithObject: @"document"];
RETAIN(XmlTagsNotStacked);
XmlTagsToSkip = [NSArray arrayWithObject: @"dependencies"];
RETAIN(XmlTagsToSkip);
ClassNamePrefixes = [NSArray arrayWithObjects: @"NS", @"IB", nil];
RETAIN(ClassNamePrefixes);
XmlReferenceAttributes = [NSArray arrayWithObjects: @"headerView", @"initialItem",
@"selectedItem", nil];
RETAIN(XmlReferenceAttributes);
XmlConnectionRecordTags = [NSArray arrayWithObjects: @"action", @"outlet", @"binding", nil];
RETAIN(XmlConnectionRecordTags);
// These cross-reference from the OLD key to the NEW key that can be referenced and its value
// or object returned verbatim. If an OLD XIB key does not exist and contains the 'NS' prefix
// the key processing will strip the 'NS' prefix, make the first letter lowercase then check
// whether that key exists and use its presence during 'containsValueForKey:' processing, and
// use its value for 'decodeXxxForKey:' processing. So, the keys here should ONLY be those
// that cannot be generated autoamatically by this processing.
// (i.e. NSIsSeparator->isSeparatorItem, NSWindowStyleMask->styleMask, etc)
// Note, that unless the associated cross referenced key contains an attribute that matches the
// original OLD key type you will need to potentially add a decoding method, and if so, the
// 'XmlKeyToDecoderSelectorMap' variable below should contain the key to its associated decoding
// method for cross referencing...
XmlKeyMapTable = [NSDictionary dictionaryWithObjectsAndKeys:
@"isSeparatorItem", @"NSIsSeparator",
@"customClass", @"NSClassName",
@"catalog", @"NSCatalogName",
@"name" , @"NSColorName",
@"pullsDown", @"NSPullDown",
@"prototype", @"NSProtoCell",
@"metaFont", @"IBIsSystemFont",
@"defaultColumnWidth", @"NSPreferedColumnWidth",
@"borderColor", @"NSBorderColor2",
@"fillColor", @"NSFillColor2",
@"horizontalScroller", @"NSHScroller",
@"verticalScroller", @"NSVScroller",
@"keyEquivalent", @"NSKeyEquiv",
@"keyEquivalentModifierMask", @"NSKeyEquivModMask",
@"contentViewMargins", @"NSOffsets",
@"contentView", @"NSWindowView",
@"customClass", @"NSWindowClass",
@"contentRect", @"NSWindowRect",
@"insertionPointColor", @"NSInsertionColor",
@"vertical", @"NSIsVertical",
@"initialItem", @"NSSelectedTabViewItem",
@"allowsExpansionToolTips", @"NSControlAllowsExpansionToolTips",
@"segments", @"NSSegmentImages",
@"editable", @"NSIsEditable",
@"objectValues", @"NSPopUpListData",
@"maxNumberOfRows", @"NSMaxNumberOfGridRows",
@"maxNumberOfColumns", @"NSMaxNumberOfGridColumns",
@"sortKey", @"NSKey",
@"name", @"NSBinding",
@"items", @"NSMenuItems",
@"implicitIdentifier", @"NSToolbarIdentifier",
@"allowedToolbarItems", @"NSToolbarIBAllowedItems",
@"defaultToolbarItems", @"NSToolbarIBDefaultItems",
@"implicitItemIdentifier", @"NSToolbarItemIdentifier",
@"bordered", @"NSIsBordered",
@"altersStateOfSelectedItem", @"NSAltersState",
nil];
RETAIN(XmlKeyMapTable);
// These define keys that are always "CONTAINED" since they typically are a combination of key values
// stored as separate and/or multiple attributed values that may be combined as in the case of flags
// and masks. There are some that have NO direct cross reference (i.e. NSSupport, NSBGColor, etc)
// Each of the ones listed here will MOST PROBABLY have an entry in the 'XmlKeyToDecoderSelectorMap'
// below that provides a cross referenced to an asociated decoding method...
// If there is an easy way to check whether an existing OLD XIB key is contained within the XIB 5
// version the 'containsValueForKey:' method in this file should be modified and the key omitted from this
// list (i.e. NSIntercellSpacingWidth, NSIntercellSpacingHeight, etc)...
XmlKeysDefined = [NSArray arrayWithObjects: @"NSWindowBacking", @"NSWTFlags",
@"NSvFlags", @"NSBGColor",
@"NSSize",
@"NSHScroller", @"NSVScroller", @"NSsFlags", @"NSsFlags2",
@"NSColumnAutoresizingStyle", @"NSTvFlags", @"NScvFlags",
@"NSSupport", @"NSName",
@"NSMenuItem",
@"NSDocView",
@"NSSliderType",
@"NSCellPrototype", @"NSBrFlags", @"NSNumberOfVisibleColumns",
@"NSWhite", @"NSRGB", @"NSCYMK",
@"NSCellFlags", @"NSCellFlags2",
@"NSButtonFlags", @"NSButtonFlags2",
@"NSUsesItemFromMenu",
@"NSNormalImage", @"NSAlternateImage",
@"NSBorderType", @"NSBoxType", @"NSTitlePosition",
@"NSTitleCell", @"NSOffsets",
@"NSMatrixFlags", @"NSNumCols", @"NSNumRows",
@"NSSharedData", @"NSFlags", @"NSTVFlags",
@"NSDefaultParagraphStyle",
@"NSpiFlags", nil];
RETAIN(XmlKeysDefined);
// These define XML tags (i.e. '<autoresizingMask ...') to an associated decode method...
XmlTagToDecoderSelectorMap =
[NSDictionary dictionaryWithObjectsAndKeys:
@"decodeTableColumnResizingMaskForElement:", @"tableColumnResizingMask",
@"decodeWindowStyleMaskForElement:", @"windowStyleMask",
@"decodeTableViewGridLinesForElement:", @"tableViewGridLines",
nil];
RETAIN(XmlTagToDecoderSelectorMap);
// These define XML attribute keys (i.e. '<object key="name" key="name" ...') to an associated decode method...
// The associated decode method may process MULTIPLE keyed attributes as in such cases as
// decoding the integer flag masks...
XmlKeyToDecoderSelectorMap =
[NSDictionary dictionaryWithObjectsAndKeys:
@"decodeIntercellSpacingHeightForElement:", @"NSIntercellSpacingHeight",
@"decodeIntercellSpacingWidthForElement:", @"NSIntercellSpacingWidth",
@"decodeColumnAutoresizingStyleForElement:", @"NSColumnAutoresizingStyle",
@"decodeNameForElement:", @"NSName",
@"decodeSliderCellTypeForElement:", @"NSSliderType",
@"decodeColumnResizingTypeForElement:", @"NSColumnResizingType",
@"decodeNumberOfVisibleColumnsForElement:", @"NSNumberOfVisibleColumns",
@"decodeSliderCellTickMarkPositionForElement:", @"NSTickMarkPosition",
@"decodeCellsForElement:", @"NSCells",
@"decodeNumberOfColumnsInMatrixForElement:", @"NSNumCols",
@"decodeNumberOfRowsInMatrixForElement:", @"NSNumRows",
@"decodeNoAutoenablesItemsForElement:", @"NSNoAutoenable",
@"decodeUsesItemFromMenuForElement:", @"NSUsesItemFromMenu",
@"decodeSelectedIndexForElement:", @"NSSelectedIndex",
@"decodePreferredEdgeForElement:", @"NSPreferredEdge",
@"decodeArrowPositionForElement:", @"NSArrowPosition",
@"decodeCellPrototypeForElement:", @"NSCellPrototype",
@"decodeTitleCellForElement:", @"NSTitleCell",
@"decodeBorderTypeForElement:", @"NSBorderType",
@"decodeBoxTypeForElement:", @"NSBoxType",
@"decodeTitlePositionForElement:", @"NSTitlePosition",
@"decodeModifierMaskForElement:", @"keyEquivalentModifierMask",
@"decodeMenuItemStateForElement:", @"NSState",
@"decodeCellForElement:", @"NSCell",
@"decodeFontSizeForElement:", @"NSSize",
@"decodeProgressIndicatorFlagsForElement:", @"NSpiFlags",
@"decodeTextViewSharedDataFlagsForElement:", @"NSFlags",
@"decodeSharedDataForElement:", @"NSSharedData",
@"decodeDefaultParagraphStyleForElement:", @"NSDefaultParagraphStyle",
@"decodeTextViewFlagsForElement:", @"NSTVFlags",
@"decodeMatrixFlagsForElement:", @"NSMatrixFlags",
@"decodeScrollClassFlagsForElement:", @"NSsFlags",
@"decodeScrollerFlags2ForElement:", @"NSsFlags2",
@"decodeScrollViewHeaderClipViewForElement:", @"NSHeaderClipView",
@"decodeBackgroundColorForElement:", @"NSBGColor",
@"decodeBrowserFlagsForElement:", @"NSBrFlags",
@"decodeClipViewFlagsForElement:", @"NScvFlags",
@"decodeTViewFlagsForElement:", @"NSTvFlags",
@"decodeViewFlagsForElement:", @"NSvFlags",
@"decodeCellContentsForElement:", @"NSContents",
@"decodeCellAlternateContentsForElement:", @"NSAlternateContents",
@"decodeCellFlags1ForElement:", @"NSCellFlags",
@"decodeCellFlags2ForElement:", @"NSCellFlags2",
@"decodeButtonFlags1ForElement:", @"NSButtonFlags",
@"decodeButtonFlags2ForElement:", @"NSButtonFlags2",
@"decodeCellNormalImageForElement:", @"NSNormalImage",
@"decodeCellAlternateImageForElement:", @"NSAlternateImage",
@"decodeWindowTemplateFlagsForElement:", @"NSWTFlags",
@"decodeWindowBackingStoreForElement:", @"NSWindowBacking",
@"decodeClipViewDocumentViewForElement:", @"NSDocView",
@"decodeColorWhiteForElement:", @"NSWhite",
@"decodeColorRGBForElement:", @"NSRGB",
@"decodeColorSpaceForElement:", @"NSColorSpace",
@"decodeColorCYMKForElement:", @"NSCYMK",
@"decodeSegmentItemImageForElement:", @"NSSegmentItemImage",
@"decodeBackgroundColorsForElement:", @"NSBackgroundColors",
@"decodeDividerStyleForElement:", @"NSDividerStyle",
@"decodeToolbarIdentifiedItemsForElement:", @"NSToolbarIBIdentifiedItems",
@"decodeToolbarImageForElement:", @"NSToolbarItemImage",
nil];
RETAIN(XmlKeyToDecoderSelectorMap);
// boolean fields that should be treated as YES when missing.
XmlBoolDefaultYes = [[NSArray alloc] initWithObjects:
@"altersStateOfSelectedItem",
@"bordered",
@"prefersToBeShown",
@"editable",
nil];
}
}
}
+ (NSString*) classNameForXibTag: (NSString*)xibTag
{
NSString *className = [XmlTagToObjectClassMap objectForKey: xibTag];
if (nil == className)
{
NSEnumerator *iter = [ClassNamePrefixes objectEnumerator];
NSString *prefix = nil;
NSString *baseString = [[[xibTag substringToIndex: 1] capitalizedString]
stringByAppendingString: [xibTag substringFromIndex: 1]];
// Try to generate a default name from tag...
while ((prefix = [iter nextObject]))
{
NSString *theClassName = [NSString stringWithFormat: @"%@%@", prefix, baseString];
if (NSClassFromString(theClassName))
{
className = theClassName;
break;
}
}
}
return className;
}
+ (Class) classForXibTag: (NSString*)xibTag
{
return NSClassFromString([self classNameForXibTag: xibTag]);
}
- (NSString*) alternateName: (NSString*)name
startIndex: (NSInteger)start
{
return [[[name substringWithRange: NSMakeRange(start, 1)] lowercaseString]
stringByAppendingString: [name substringFromIndex: start + 1]];
}
/*
Remove the "NS" prefix, only called after a check for this prefix.
If the remaining name starts with the current element name, remove this as well.
NSToolbarDisplayMode -> displayMode
*/
- (NSString*) alternateName: (NSString*)name
{
NSString *postfix = [self alternateName: name startIndex: 2];
NSString *typeName = [currentElement attributeForKey: @"key"];
if ((typeName != nil) && [postfix hasPrefix: typeName])
{
return [self alternateName: name startIndex: [typeName length] + 2];
}
return postfix;
}
- (GSXibElement*) createReference: (NSString *)oid
{
GSXibElement *element = [[GSXibElement alloc] initWithType: @"reference"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"object", @"key",
oid, @"ref",
nil]];
return AUTORELEASE(element);
}
- (void) addConnection: (GSXibElement*)element
{
GSXibElement *connectionRecord;
// Get the parent of the parent object...
// The current object at this point is the 'connections' array element.
// The parent of connections array element is the object ID we need...
GSXibElement *parent = [stack objectAtIndex: [stack count] - 1];
NSString *parentId = [parent attributeForKey: @"id"];
NSString *objKey = [@"action" isEqualToString: [element attributeForKey: @"key"]] ?
@"destination" : @"source";
if (parentId == nil)
{
NSLog(@"Missing parent Id for connection on parent %@", parent);
// Fake an id for parent
parentId = [[NSUUID UUID] UUIDString];
[parent setAttribute: parentId forKey: @"id"];
[objects setObject: parent forKey: parentId];
}
// Store the ID reference of the parent object...
[element setElement: [self createReference: parentId] forKey: objKey];
if ([element attributeForKey: @"target"])
{
[element setElement: [self createReference: [element attributeForKey: @"target"]]
forKey: @"target"];
}
if ([element attributeForKey: @"source"])
{
[element setElement: [self createReference: [element attributeForKey: @"source"]]
forKey: @"source"];
}
if ([element attributeForKey: @"destination"])
{
[element setElement: [self createReference: [element attributeForKey: @"destination"]]
forKey: @"destination"];
}
// Build a connection recort
connectionRecord = [[GSXibElement alloc] initWithType: @"object"
andAttributes:
[NSDictionary dictionaryWithObjectsAndKeys:
@"IBConnectionRecord", @"class",
[[NSUUID UUID] UUIDString], @"id",
nil]];
[connectionRecord setElement: element forKey: @"connection"];
// Add the connection record element that includes this element...
[_connectionRecords addElement: connectionRecord];
RELEASE(connectionRecord);
}
- (GSXibElement*) orderedObjectForElement: (GSXibElement*)element
{
// Mimic the old IBObjectRecord instance...
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:
@"IBObjectRecord", @"class",
[[NSUUID UUID] UUIDString], @"id",
nil];
GSXibElement *objectRecord = [[GSXibElement alloc] initWithType: @"object"
andAttributes: attributes];
GSXibElement *parent = [[GSXibElement alloc] initWithType: @"nil"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"parent", @"key",
nil]];
GSXibElement *children = [[GSXibElement alloc] initWithType: @"nil"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"children", @"key",
nil]];
GSXibElement *reference = [self createReference: [element attributeForKey: @"id"]];
[objectRecord setElement: element forKey: @"object"];
[objectRecord setElement: parent forKey: @"parent"];
[objectRecord setElement: children forKey: @"children"];
[objectRecord setElement: reference forKey: @"reference"];
RELEASE(parent);
RELEASE(children);
return AUTORELEASE(objectRecord);
}
- (void) addRuntimeAttributesForElement: (GSXibElement*)element forID: (NSString*)idString
{
NSString *refID = [NSString stringWithFormat: @"%@.IBAttributePlaceholdersKey", idString];
GSXibElement *objectRecord = (GSXibElement*)[_flattenedProperties elementForKey: refID];
// Mimic the old IBAttributePlaceholders instance...
if (objectRecord == nil)
{
objectRecord = [[GSXibElement alloc] initWithType: @"dictionary"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"NSMutableDictionary", @"class",
refID, @"id",
nil]];
GSXibElement *stringRecord = [[GSXibElement alloc] initWithType: @"string"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"NS.key.0", @"key",
nil]];
GSXibElement *placeholderRec = [[GSXibElement alloc] initWithType: @"object"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"IBUserDefinedRuntimeAttributesPlaceholder", @"class",
@"IBUserDefinedRuntimeAttributesPlaceholderName", @"key",
nil]];
GSXibElement *placeStringRec = [[GSXibElement alloc] initWithType: @"string"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"name", @"key",
nil]];
GSXibElement *referenceRec = [self createReference: [element attributeForKey: @"id"]];
// Setup placeholder record...
[placeStringRec setValue: @"IBUserDefinedRuntimeAttributesPlaceholderName"];
[placeholderRec setElement: placeStringRec forKey: @"name"];
[placeholderRec setElement: referenceRec forKey: @"object"];
[placeholderRec setElement: element forKey: @"userDefinedRuntimeAttributes"];
// Attach placeholder and string records to the object record...
[objectRecord setElement: stringRecord forKey: @"NS.key.0"];
[objectRecord setElement: placeholderRec forKey: @"IBUserDefinedRuntimeAttributesPlaceholderName"];
// Add to flattened properties...
[_flattenedProperties setElement: objectRecord forKey: refID];
// Cleanup...
RELEASE(stringRecord);
RELEASE(placeholderRec);
RELEASE(placeStringRec);
RELEASE(objectRecord);
}
}
- (id) initForReadingWithData: (NSData*)data
{
// Check data...
if (data == nil)
{
DESTROY(self);
return nil;
}
else
{
NSXMLParser *theParser = nil;
// Initialize...
[self _initCommon];
// Create the parser and parse the data...
theParser = [[NSXMLParser alloc] initWithData: data];
[theParser setDelegate: self];
NS_DURING
{
// Parse the XML data
[theParser parse];
// Decode optional resources
_resources = RETAIN([self decodeObjectForKey: @"resources"]);
}
NS_HANDLER
{
NSLog(@"Exception occurred while parsing Xib: %@", [localException reason]);
DESTROY(self);
}
NS_ENDHANDLER
DESTROY(theParser);
}
return self;
}
- (void) _initCommon
{
[super _initCommon];
_orderedObjectsDict = RETAIN([NSMutableDictionary dictionary]);
// Create our object(s)...
_orderedObjects = [[GSXibElement alloc] initWithType: @"array"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"orderedObjects", @"key",
nil]];
_objectRecords = [[GSXibElement alloc] initWithType: @"object"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"IBMutableOrderedSet", @"class",
@"objectRecords", @"key",
nil]];
_connectionRecords = [[GSXibElement alloc] initWithType: @"array"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"connectionRecords", @"key",
nil]];
_flattenedProperties = [[GSXibElement alloc] initWithType: @"dictionary"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"NSMutableDictionary", @"class",
@"flattenedProperties", @"key",
nil]];
_runtimeAttributes = [[GSXibElement alloc] initWithType: @"dictionary"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"NSMutableDictionary", @"class",
@"connectionRecords", @"key",
nil]];
// objectRecords...
[_objectRecords setElement: _orderedObjects forKey: @"orderedObjects"];
// We will imitate the old XIB loading using an IBObjectContainer
// stored with key "IBDocument.Objects"...
_IBObjectContainer = [[GSXibElement alloc] initWithType: @"object"
andAttributes: [NSDictionary dictionaryWithObjectsAndKeys:
@"IBObjectContainer", @"class",
@"IBDocument.Objects", @"key",
nil]];
// Create the linked set of XIB elements...
[_IBObjectContainer setElement: _connectionRecords forKey: @"connectionRecords"];
[_IBObjectContainer setElement: _objectRecords forKey: @"objectRecords"];
[_IBObjectContainer setElement: _flattenedProperties forKey: @"flattenedProperties"];
}
- (void)dealloc
{
RELEASE(_IBObjectContainer);
RELEASE(_connectionRecords);
RELEASE(_objectRecords);
RELEASE(_flattenedProperties);
RELEASE(_runtimeAttributes);
RELEASE(_orderedObjects);
RELEASE(_orderedObjectsDict);
RELEASE(_resources);
[super dealloc];
}
- (void) parser: (NSXMLParser*)parser
didStartElement: (NSString*)elementName
namespaceURI: (NSString*)namespaceURI
qualifiedName: (NSString*)qualifiedName
attributes: (NSDictionary*)attributeDict
{
// Skip certain element names - for now...
if ([XmlTagsToSkip containsObject: elementName] == NO)
{
NSMutableDictionary *attributes = AUTORELEASE([attributeDict mutableCopy]);
NSString *className = nil;
NSString *elementType = elementName;
if (([@"window" isEqualToString: elementName] == NO) &&
([@"customView" isEqualToString: elementName] == NO) &&
([@"customObject" isEqualToString: elementName] == NO))
{
className = [attributes objectForKey: @"customClass"];
}
if (nil == className)
{
className = [[self class] classNameForXibTag: elementName];
}
if (nil != className)
{
if ([NSClassFromString(className) isSubclassOfClass: [NSArray class]])
elementType = @"array";
else if (([@"string" isEqualToString: elementName] == NO) &&
([@"dictionary" isEqualToString: elementName] == NO))
elementType = @"object";
}
// Add the necessary attribute(s)...
if (className)
{
[attributes setObject: className forKey: @"class"];
}
if ([attributes objectForKey: @"key"] == nil)
{
// Special cases to allow current initWithCoder methods to obtain objects..._IBObjectContainer
if ([@"objects" isEqualToString: elementName])
{
[attributes setObject: @"IBDocument.RootObjects" forKey: @"key"];
}
else
{
[attributes setObject: elementName forKey: @"key"];
}
}
if ([[attributes objectForKey: @"userLabel"] isEqualToString: @"Application"] &&
(([attributes objectForKey: @"customClass"] == nil) ||
([NSClassFromString([attributes objectForKey: @"customClass"]) isSubclassOfClass: [NSApplication class]] == NO)))
{
[attributes setObject: @"NSApplication" forKey: @"customClass"];
}
// Generate the XIB element object...
GSXibElement *element;
if ([attributes objectForKey: @"reference"])
{
element = RETAIN([self createReference: [attributes objectForKey: @"reference"]]);
}
else
{
element = [[GSXibElement alloc] initWithType: elementType
andAttributes: attributes];
}
if ([@"array" isEqualToString: [currentElement type]])
{
// For arrays...
[currentElement addElement: element];
if ([XmlConnectionRecordTags containsObject: elementName])
{
// Need to store element for making the connections...
[self addConnection: element];
}
}
else
{
NSString *key = [attributes objectForKey: @"key"];
// For elements...
[currentElement setElement: element forKey: key];
// If top level document add our generated connection records...
if ([@"document" isEqualToString: elementName])
{
[element setElement: _IBObjectContainer forKey: @"IBDocument.Objects"];
}
}
// Reference(s)...
NSString *ref = [attributes objectForKey: @"id"];
if (ref != nil)
{
[objects setObject: element forKey: ref];
}
if ([XmlTagsNotStacked containsObject: elementName] == NO)
{
// Push element onto stack...
[stack addObject: currentElement];
}
// Set as current element being processed...
currentElement = element;
AUTORELEASE(element);
}
}
- (void) parser: (NSXMLParser*)parser
didEndElement: (NSString*)elementName
namespaceURI: (NSString*)namespaceURI
qualifiedName: (NSString*)qName
{
// Skip certain element names - for now...
if ([XmlTagsToSkip containsObject: elementName] == NO)
{
if ([XmlTagsNotStacked containsObject: elementName] == NO)
{
// Pop element...
currentElement = [stack lastObject];
[stack removeLastObject];
}
}
}
- (id) findResourceWithName: (NSString*)name
{
NSEnumerator *iter = [_resources objectEnumerator];
id resource = nil;
while ((resource = [iter nextObject]))
{
if ([[resource name] isEqual: name])
{
return resource;
}
}
return nil;
}
// All this code should eventually move into their respective initWithCoder class
// methods - however note - there are a couple that may be duplicated...
- (id) decodeAutoresizingMaskForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
if (attributes)
{
NSUInteger mask = NSViewNotSizable;
if ([[attributes objectForKey: @"flexibleMinX"] boolValue])
mask |= NSViewMinXMargin;
if ([[attributes objectForKey: @"widthSizable"] boolValue])
mask |= NSViewWidthSizable;
if ([[attributes objectForKey: @"flexibleMaxX"] boolValue])
mask |= NSViewMaxXMargin;
if ([[attributes objectForKey: @"flexibleMinY"] boolValue])
mask |= NSViewMinYMargin;
if ([[attributes objectForKey: @"heightSizable"] boolValue])
mask |= NSViewHeightSizable;
if ([[attributes objectForKey: @"flexibleMaxY"] boolValue])
mask |= NSViewMaxYMargin;
return [NSNumber numberWithUnsignedInt: mask];
}
return nil;
}
- (id) decodeViewFlagsForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = nil;
if ([class isSubclassOfClass: [NSView class]] == NO)
{
NSWarnMLog(@"called for a class that is NOT a sub-class of NSView - class: %@", NSStringFromClass(class));
}
else
{
GSvFlagsUnion mask = { { 0 } };
NSDictionary *attributes = [element attributes];
GSXibElement *autoresizingMask = (GSXibElement*)[element elementForKey: @"autoresizingMask"];
mask.flags.autoresizingMask = [[self decodeAutoresizingMaskForElement: autoresizingMask] unsignedIntegerValue];
mask.flags.isHidden = [[attributes objectForKey: @"hidden"] boolValue];
mask.flags.autoresizesSubviews = YES;
if ([attributes objectForKey: @"autoresizesSubviews"])
mask.flags.autoresizesSubviews = [[attributes objectForKey: @"autoresizesSubviews"] boolValue];
// Return value...
object = [NSNumber numberWithUnsignedInt: mask.value];
}
return object;
}
- (id) decodeClipViewDocumentViewForElement: (GSXibElement*)element
{
NSArray *subviews = [self decodeObjectForKey: @"subviews"];
if ([subviews count])
return [subviews objectAtIndex: 0];
NSWarnMLog(@"no clipview document view for element: %@", element);
return nil;
}
- (id) decodeWindowPositionMaskForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
if (attributes)
{
NSUInteger mask = 0;
// FIXME
return [NSNumber numberWithUnsignedInteger: mask];
}
return nil;
}
- (id) decodeWindowTemplateFlagsForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
if (attributes)
{
GSWindowTemplateFlagsUnion mask = { { 0 } };
GSXibElement *winPosMaskEleme = (GSXibElement*)[currentElement elementForKey: @"initialPositionMask"];
NSUInteger winPosMask = [[self decodeWindowPositionMaskForElement: winPosMaskEleme] unsignedIntegerValue];
NSString *autorecalculatesKeyViewLoop = [element attributeForKey: @"autorecalculatesKeyViewLoop"];
mask.flags.isHiddenOnDeactivate = [[attributes objectForKey: @"hidesOnDeactivate"] boolValue];
mask.flags.isNotReleasedOnClose = !([attributes objectForKey: @"releasedWhenClosed"] ?
[[attributes objectForKey: @"releasedWhenClosed"] boolValue] : YES);
mask.flags.isDeferred = ([attributes objectForKey: @"deferred"] ?
[[attributes objectForKey: @"deferred"] boolValue] : YES);
mask.flags.isOneShot = ([attributes objectForKey: @"oneShot"] ?
[[attributes objectForKey: @"oneShot"] boolValue] : YES);
mask.flags.isVisible = ([attributes objectForKey: @"visibleAtLaunch"] ?
[[attributes objectForKey: @"visibleAtLaunch"] boolValue] : YES);
mask.flags.wantsToBeColor = 0; // ???;
mask.flags.dynamicDepthLimit = 0; // ???;
mask.flags.autoPositionMask = winPosMask;
mask.flags.savePosition = [attributes objectForKey: @"frameAutosaveName"] != nil;
mask.flags.style = 0; // ???
mask.flags.isNotShadowed = !([attributes objectForKey: @"hasShadow"] ?
[[attributes objectForKey: @"hasShadow"] boolValue] : YES);
mask.flags.autorecalculatesKeyViewLoop = (autorecalculatesKeyViewLoop ? [autorecalculatesKeyViewLoop boolValue] : YES);
// File GSNibLoading.m: 422. In -[NSWindowTemplate initWithCoder:] _flags: 0xf0781400 style: 147 backing: 2
// File GSNibLoading.m: 422. In -[NSWindowTemplate initWithCoder:] _flags: 0xf0001000 style: 147 backing: 2
#if 0 // FIXME:
mask.flags.allowsToolTipsWhenApplicationIsInactive = ([attributes objectForKey: @"allowsToolTipsWhenApplicationIsInactive"] ?
[[attributes objectForKey: @"allowsToolTipsWhenApplicationIsInactive"] boolValue] :
YES);
#endif
return [NSNumber numberWithUnsignedInteger: mask.value];
}
return nil;
}
- (id) decodeWindowBackingStoreForElement: (GSXibElement*)element
{
NSUInteger value = NSBackingStoreBuffered; // Default for Cocoa...
NSString *backingType = [element attributeForKey: @"backingType"];
if (backingType)
{
if ([@"retained" isEqualToString: backingType])
value = NSBackingStoreRetained;
else if ([@"nonretained" isEqualToString: backingType])
value = NSBackingStoreNonretained;
else
NSWarnMLog(@"unknown backing store type: %@", backingType);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeWindowStyleMaskForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
if (attributes)
{
NSUInteger mask = 0;
if ([[attributes objectForKey: @"titled"] boolValue])
mask |= NSTitledWindowMask;
if ([[attributes objectForKey: @"closable"] boolValue])
mask |= NSClosableWindowMask;
if ([[attributes objectForKey: @"miniaturizable"] boolValue])
mask |= NSMiniaturizableWindowMask;
if ([[attributes objectForKey: @"resizable"] boolValue])
mask |= NSResizableWindowMask;
if ([[attributes objectForKey: @"texturedBackground"] boolValue])
mask |= NSTexturedBackgroundWindowMask;
if ([[attributes objectForKey: @"unifiedTitleAndToolbar"] boolValue])
mask |= NSUnifiedTitleAndToolbarWindowMask;
if ([[attributes objectForKey: @"fullSizeContentView"] boolValue])
mask |= NSWindowStyleMaskFullSizeContentView;
if ([[attributes objectForKey: @"utility"] boolValue])
mask |= NSWindowStyleMaskUtilityWindow;
if ([[attributes objectForKey: @"nonactivatingPanel"] boolValue])
mask |= NSWindowStyleMaskNonactivatingPanel;
return [NSNumber numberWithUnsignedInteger: mask];
}
return nil;
}
- (id) decodeMatrixFlagsForElement: (GSXibElement*)element
{
NSString *mode = [element attributeForKey: @"mode"];
NSString *allowsEmptySelection = [element attributeForKey: @"allowsEmptySelection"];
NSString *autosizesCells = [element attributeForKey: @"autosizesCells"];
NSString *drawsBackground = [element attributeForKey: @"drawsBackground"];
NSString *selectionByRect = [element attributeForKey: @"selectionByRect"];
GSMatrixFlagsUnion mask = { { 0 } };
// mode...
if ([@"list" isEqualToString: mode])
{
mask.flags.isList = 1;
}
else if ([@"highlight" isEqualToString: mode])
{
mask.flags.isHighlight = 1;
}
else if ([@"radio" isEqualToString: mode])
{
mask.flags.isRadio = 1;
}
else if ([@"track" isEqualToString: mode])
{
// What do we do with this type???
}
else if (mode)
{
NSWarnMLog(@"unknown matrix mode: %@", mode);
}
// allows empty selection...
if (allowsEmptySelection == nil)
mask.flags.allowsEmptySelection = 1;
else
mask.flags.allowsEmptySelection = [allowsEmptySelection boolValue];
// autosizes cells...
if (autosizesCells == nil)
mask.flags.autosizesCells = 1;
else
mask.flags.autosizesCells = [autosizesCells boolValue];
// draw background/cell background...
if (drawsBackground)
mask.flags.drawBackground = [drawsBackground boolValue];
mask.flags.drawCellBackground = mask.flags.drawBackground;
// selection by rectangle...
if (selectionByRect == nil)
mask.flags.selectionByRect = 1;
else
mask.flags.selectionByRect = [selectionByRect boolValue];
return [NSNumber numberWithUnsignedInt: mask.value];
}
- (id) decodeNumberOfColumnsInMatrixForElement: (GSXibElement*)element
{
id object = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSMatrix class]])
{
NSArray *cells = [self decodeObjectForKey: @"cells"];
object = [NSNumber numberWithUnsignedInteger: [cells count]];
}
return object;
}
- (id) decodeNumberOfRowsInMatrixForElement: (GSXibElement*)element
{
id object = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSMatrix class]])
{
NSArray *cells = [self decodeObjectForKey: @"cells"];
NSArray *column = [cells objectAtIndex: 0];
object = [NSNumber numberWithUnsignedInteger: [column count]];
}
return object;
}
- (id) decodeFormCellsForElement: (GSXibElement*)element
{
id object = [NSMutableArray array];
NSArray *columns = [self decodeObjectForKey: @"cells"];
NSInteger numCols = [columns count];
NSInteger numRows = [[columns objectAtIndex: 0] count];
NSInteger row = 0;
NSInteger col = 0;
// NSForm's cells now encoded as two dimensional array but we need
// the cells in a single array by column/row...
for (row = 0; row < numRows; ++row)
{
for (col = 0; col < numCols; ++col)
{
// Add the row/column object...
[object addObject: [[columns objectAtIndex: col] objectAtIndex: row]];
}
}
return object;
}
- (id) decodeNameForElement: (GSXibElement*)element
{
id object = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSMenu class]])
{
object = [element attributeForKey: @"systemMenu"];
if ([@"main" isEqualToString: object])
object = @"_NSMainMenu";
else if ([@"apple" isEqualToString: object])
object = @"_NSAppleMenu";
else if ([@"window" isEqualToString: object])
object = @"_NSWindowsMenu";
else if ([@"services" isEqualToString: object])
object = @"_NSServicesMenu";
else if ([@"recentDocuments" isEqualToString: object])
object = @"_NSRecentDocumentsMenu";
else if ([@"font" isEqualToString: object])
object = @"_NSFontMenu";
}
else if ([element attributeForKey: @"name"])
{
object = [self decodeObjectForKey: @"name"];
}
else if ([class isSubclassOfClass: [NSFont class]] == NO)
{
NSWarnMLog(@"no name object for class: %@", [element attributeForKey: @"class"]);
}
return object;
}
- (id) decodeSliderCellTickMarkPositionForElement: (GSXibElement*)element
{
NSUInteger value = NSTickMarkBelow; // Default...
NSString *tickMarkPosition = [element attributeForKey: @"tickMarkPosition"];
if ([@"below" isEqualToString: tickMarkPosition])
value = NSTickMarkBelow;
else if ([@"above" isEqualToString: tickMarkPosition])
value = NSTickMarkAbove;
else if ([@"leading" isEqualToString: tickMarkPosition])
value = NSTickMarkLeft;
else if ([@"trailing" isEqualToString: tickMarkPosition])
value = NSTickMarkRight;
else if (tickMarkPosition)
NSWarnMLog(@"unknown slider cell tick mark position: %@", tickMarkPosition);
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeSliderCellTypeForElement: (GSXibElement*)element
{
NSUInteger value = NSCircularSlider; // Default...
NSString *sliderType = [element attributeForKey: @"sliderType"];
if ([@"linear" isEqualToString: sliderType])
value = NSLinearSlider;
else if ([@"circular" isEqualToString: sliderType])
value = NSCircularSlider;
else if (sliderType)
NSWarnMLog(@"unknown slider cell type: %@", sliderType);
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeCellsForElement: (GSXibElement*)element
{
id object = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSMatrix class]])
object = [self decodeFormCellsForElement: element];
else
object = [self decodeObjectForKey: @"cells"];
return object;
}
- (id) decodeNoAutoenablesItemsForElement: (GSXibElement*)element
{
NSString *autoenablesItems = [element attributeForKey: @"autoenablesItems"];
BOOL value = NO; // Default if not present...
if (autoenablesItems)
value = ![autoenablesItems boolValue];
return [NSNumber numberWithBool: value];
}
- (id) decodeTitleCellForElement: (GSXibElement*)element
{
NSString *title = [element attributeForKey: @"title"];
if (title)
{
id object = [[NSCell alloc] initTextCell: title];
NSFont *font = [self decodeObjectForKey: @"titleFont"];
// IF no font...
if (font == nil) // default to system-11...
font = [NSFont systemFontOfSize: 11];
[object setAlignment: NSCenterTextAlignment];
[object setBordered: NO];
[object setEditable: NO];
[object setFont: font];
return AUTORELEASE(object);
}
return nil;
}
- (id) decodeBorderTypeForElement: (GSXibElement*)element
{
NSString *borderType = [element attributeForKey: @"borderType"];
NSBorderType value = NSGrooveBorder; // Cocoa default...
if (borderType)
{
if ([@"bezel" isEqualToString: borderType])
value = NSBezelBorder;
else if ([@"line" isEqualToString: borderType])
value = NSLineBorder;
else if ([@"none" isEqualToString: borderType])
value = NSNoBorder;
else
NSWarnMLog(@"unknown border type: %@", borderType);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeModifierMaskForElement: (GSXibElement*)element
{
id object = nil;
NSDictionary *attributes = [[element elementForKey: @"keyEquivalentModifierMask"] attributes];
// ??? SKIP modifier mask processing if BASE64-UTF8 string being used ???
if (attributes == nil)
{
if (([element elementForKey: @"keyEquivalent"]) &&
([[element elementForKey: @"keyEquivalent"] attributeForKey: @"base64-UTF8"]))
{
object = [NSNumber numberWithUnsignedInt: 0];
}
else
{
// Seems that Apple decided to omit this attribute IF certain default keys alone
// are applied. If this key is present WITH NO setting then the following is
// used for the modifier mask...
object = [NSNumber numberWithUnsignedInt: NSCommandKeyMask];
}
}
else
{
// If the modifier mask element is present then no modifier attributes
// equates to no key modifiers applied...
NSUInteger mask = 0;
if ([[attributes objectForKey: @"option"] boolValue])
{
mask |= NSAlternateKeyMask;
}
if ([[attributes objectForKey: @"alternate"] boolValue])
{
mask |= NSAlternateKeyMask;
}
if ([[attributes objectForKey: @"command"] boolValue])
{
mask |= NSCommandKeyMask;
}
if ([[attributes objectForKey: @"control"] boolValue])
{
mask |= NSControlKeyMask;
}
if ([[attributes objectForKey: @"shift"] boolValue])
{
mask |= NSShiftKeyMask;
}
if ([[attributes objectForKey: @"numeric"] boolValue])
{
mask |= NSNumericPadKeyMask;
}
if ([[attributes objectForKey: @"help"] boolValue])
{
mask |= NSHelpKeyMask;
}
if ([[attributes objectForKey: @"function"] boolValue])
{
mask |= NSFunctionKeyMask;
}
object = [NSNumber numberWithUnsignedInt: mask];
}
return object;
}
- (id) decodeBoxTypeForElement: (GSXibElement*)element
{
NSString *boxType = [element attributeForKey: @"boxType"];
NSBoxType value = NSBoxPrimary; // Cocoa default...
if (boxType)
{
if ([@"secondary" isEqualToString: boxType])
value = NSBoxSecondary;
else if ([@"separator" isEqualToString: boxType])
value = NSBoxSeparator;
else if ([@"oldStyle" isEqualToString: boxType])
value = NSBoxOldStyle;
else if ([@"custom" isEqualToString: boxType])
value = NSBoxCustom;
else if ([@"primary" isEqualToString: boxType])
value = NSBoxPrimary;
else
NSWarnMLog(@"unknown box type: %@", boxType);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeTitlePositionForElement: (GSXibElement*)element
{
NSString *titlePosition = [element attributeForKey: @"titlePosition"];
NSTitlePosition value = NSAtTop; // Default if not present...
if (titlePosition)
{
if ([@"noTitle" isEqualToString: titlePosition])
value = NSNoTitle;
else if ([@"aboveTop" isEqualToString: titlePosition])
value = NSAboveTop;
else if ([@"belowTop" isEqualToString: titlePosition])
value = NSBelowTop;
else if ([@"aboveBottom" isEqualToString: titlePosition])
value = NSAboveTop;
else if ([@"atBottom" isEqualToString: titlePosition])
value = NSAtBottom;
else if ([@"belowBottom" isEqualToString: titlePosition])
value = NSBelowBottom;
else if ([@"atTop" isEqualToString: titlePosition])
value = NSAtTop;
else
NSWarnMLog(@"unknown title position: %@", titlePosition);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeFontSizeForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
CGFloat size = [[attributes objectForKey: @"size"] floatValue];
if (size == 0)
{
NSString *metaFont = [[attributes objectForKey: @"metaFont"] lowercaseString];
// Default the value
size = 12;
// FIXME: We should try to get the corresponding user default value here
if ([metaFont containsString: @"mini"])
size = [NSFont systemFontSizeForControlSize: NSMiniControlSize];
else if ([metaFont containsString: @"small"])
size = [NSFont smallSystemFontSize];
else if ([metaFont containsString: @"message"])
size = 10;
else if ([metaFont containsString: @"medium"])
size = 11;
else if ([metaFont containsString: @"menu"])
size = 12;
else if ([metaFont containsString: @"celltitle"])
size = 12;
else if ([metaFont containsString: @"controlcontent"])
size = 12;
else if ([metaFont containsString: @"label"])
size = [NSFont labelFontSize];
else if ([metaFont containsString: @"system"])
size = [NSFont systemFontSize];
else if (metaFont)
NSWarnMLog(@"unknown meta font value: %@", metaFont);
}
return [NSNumber numberWithFloat: size];
}
- (id) decodeFontTypeForElement: (GSXibElement*)element
{
BOOL isSystem = NO;
NSString *metaFont = [[[element attributes] objectForKey: @"metaFont"] lowercaseString];
if ([metaFont containsString: @"system"] || [metaFont containsString: @"message"] )
{
isSystem = YES;
}
return [NSNumber numberWithBool: isSystem];
}
- (id) decodeDividerStyleForElement: (GSXibElement*)element
{
NSString *dividerStyle = [element attributeForKey: @"dividerStyle"];
NSSplitViewDividerStyle style = NSSplitViewDividerStyleThick; // Default...
if (dividerStyle)
{
if ([@"thin" isEqualToString: dividerStyle])
style = NSSplitViewDividerStyleThin;
else if ([@"paneSplitter" isEqualToString: dividerStyle])
style = NSSplitViewDividerStylePaneSplitter;
else if ([@"thick" isEqualToString: dividerStyle])
style = NSSplitViewDividerStyleThick;
else
NSWarnMLog(@"unknown divider style: %@", dividerStyle);
}
return [NSNumber numberWithInteger: style];
}
- (id) decodeBackgroundColorsForElement: (GSXibElement*)element
{
NSMutableArray *backgroundColors = [NSMutableArray array];
NSColor *primaryColor = [self decodeObjectForKey: @"primaryBackgroundColor"];
NSColor *secondaryColor = [self decodeObjectForKey: @"secondaryBackgroundColor"];
// If primary only - just one background color...
if (primaryColor)
[backgroundColors addObject: primaryColor];
// If secondary included - indicates alternating background color scheme...
if (secondaryColor)
[backgroundColors addObject: secondaryColor];
return backgroundColors;
}
- (id) decodeProgressIndicatorFlagsForElement: (GSXibElement*)element
{
unsigned int flags = 0;
#if 0
NSString *bezeled = [element attributeForKey: @"bezeled"];
#endif
NSString *style = [element attributeForKey: @"style"];
NSString *controlSize = [element attributeForKey: @"controlSize"];
NSString *indeterminate = [element attributeForKey: @"indeterminate"];
NSString *displayedWhenStopped = [element attributeForKey: @"displayedWhenStopped"];
if ([indeterminate boolValue])
flags |= 0x0002;
if ([@"small" isEqualToString: controlSize])
flags |= 0x0100;
if ([@"spinning" isEqualToString: style])
flags |= 0x1000;
if ((displayedWhenStopped == nil) || ([displayedWhenStopped boolValue]))
flags |= 0x2000;
return [NSNumber numberWithInt: flags];
}
- (id) decodeTextViewSharedDataFlagsForElement: (GSXibElement*)element
{
unsigned int flags = 0;
NSString *allowsUndo = [element attributeForKey: @"allowsUndo"];
NSString *importsGraphics = [element attributeForKey: @"importsGraphics"];
NSString *editable = [element attributeForKey: @"editable"];
NSString *selectable = [element attributeForKey: @"selectable"];
NSString *fieldEditor = [element attributeForKey: @"fieldEditor"];
NSString *findStyle = [element attributeForKey: @"findStyle"];
NSString *richText = [element attributeForKey: @"richText"];
NSString *smartInsertDelete = [element attributeForKey: @"smartInsertDelete"];
NSString *usesFontPanel = [element attributeForKey: @"usesFontPanel"];
NSString *usesRuler = [element attributeForKey: @"usesRuler"];
NSString *drawsBackground = [element attributeForKey: @"drawsBackground"];
NSString *continuousSpellChecking = [element attributeForKey: @"continuousSpellChecking"];
#if 0
// FIXME: if and when these are added to NSTextView...
NSString *allowsNonContiguousLayout = [element attributeForKey: @"allowsNonContiguousLayout"];
NSString *spellingCorrection = [element attributeForKey: @"spellingCorrection"];
NSString *allowsImageEditing = [element attributeForKey: @"allowsImageEditing"];
NSString *allowsDocumentBackgroundColorChange = [element attributeForKey: @"allowsDocumentBackgroundColorChange"];
#endif
if ((selectable == nil) || ([selectable boolValue]))
flags |= 0x01;
if ((editable == nil) || ([editable boolValue]))
flags |= 0x02;
if ((richText == nil) || ([richText boolValue]))
flags |= 0x04;
if ([importsGraphics boolValue])
flags |= 0x08;
if ([fieldEditor boolValue])
flags |= 0x10;
if ([usesFontPanel boolValue])
flags |= 0x20;
if ([usesRuler boolValue])
flags |= 0x40;
if ([continuousSpellChecking boolValue])
flags |= 0x80;
if ([usesRuler boolValue])
flags |= 0x100;
if ([smartInsertDelete boolValue])
flags |= 0x200;
if ([allowsUndo boolValue])
flags |= 0x400;
if ((drawsBackground == nil) || ([drawsBackground boolValue]))
flags |= 0x800;
if (findStyle) //([@"panel" isEqualToString: findStyle])
flags |= 0x2000;
#if 0
// FIXME: when added to NSTextView...
if ([allowsImageEditing boolValue])
flags |= 0x00;
if ([allowsDocumentBackgroundColorChange boolValue])
flags |= 0x00;
#endif
return [NSNumber numberWithUnsignedInt: flags];
}
- (id) decodeSharedDataForElement: (GSXibElement*)element
{
id object = [[NSClassFromString(@"NSTextViewSharedData") alloc] initWithCoder: self];
return AUTORELEASE(object);
}
- (id) decodeTextViewFlagsForElement: (GSXibElement*)element
{
unsigned int flags = 0;
// horizontallyResizable...
if ([[element attributeForKey: @"horizontallyResizable"] boolValue])
flags |= 0x01;
// verticallyResizable...
if ([element attributeForKey: @"verticallyResizable"] == nil)
flags |= 0x02;
else if ([[element attributeForKey: @"verticallyResizable"] boolValue])
flags |= 0x02;
return [NSNumber numberWithUnsignedInt: flags];
}
- (unsigned int) decodeLineBreakMode: (GSXibElement*)element
{
unsigned int value = NSLineBreakByWordWrapping;
NSString *lineBreakMode = [element attributeForKey: @"lineBreakMode"];
if (lineBreakMode)
{
if ([@"clipping" isEqualToString: lineBreakMode])
value = NSLineBreakByClipping;
else if ([@"charWrapping" isEqualToString: lineBreakMode])
value = NSLineBreakByCharWrapping;
else if ([@"wordWrapping" isEqualToString: lineBreakMode])
value = NSLineBreakByWordWrapping;
else if ([@"truncatingHead" isEqualToString: lineBreakMode])
value = NSLineBreakByTruncatingHead;
else if ([@"truncatingMiddle" isEqualToString: lineBreakMode])
value = NSLineBreakByTruncatingMiddle;
else if ([@"truncatingTail" isEqualToString: lineBreakMode])
value = NSLineBreakByTruncatingTail;
else
NSWarnMLog(@"unknown line break mode: %@", lineBreakMode);
}
return value;
}
- (id) decodeDefaultParagraphStyleForElement: (GSXibElement*)element
{
NSMutableParagraphStyle *paragraphStyle = AUTORELEASE([NSMutableParagraphStyle new]);
NSString *baseWritingDirection = [element attributeForKey: @"baseWritingDirection"];
NSString *selectionGranularity = [element attributeForKey: @"selectionGranularity"];
if (baseWritingDirection == nil)
[paragraphStyle setBaseWritingDirection: NSWritingDirectionNaturalDirection];
else if ([@"leftToRight" isEqualToString: baseWritingDirection])
[paragraphStyle setBaseWritingDirection: NSWritingDirectionLeftToRight];
else if ([@"rightToLeft" isEqualToString: baseWritingDirection])
[paragraphStyle setBaseWritingDirection: NSWritingDirectionRightToLeft];
else
NSWarnMLog(@"unknown base writing direction: %@", baseWritingDirection);
// Line break mode...
[paragraphStyle setLineBreakMode: [self decodeLineBreakMode: element]];
if (selectionGranularity == nil)
; // NSSelectByCharacter
else if ([@"word" isEqualToString: selectionGranularity])
; // NSSelectByWord
else if ([@"paragraph" isEqualToString: selectionGranularity])
; // NSSelectByParagraph
return paragraphStyle;
}
- (id) decodeColorSpaceForElement: (GSXibElement*)element
{
// <color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
// <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
// <color key="textColor" red="0.0" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
// <color key="insertionPointColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
// <color key="backgroundColor" cyan="0.61524784482758621" magenta="0.17766702586206898" yellow="0.48752693965517241" black="0.60991379310344829"
// alpha="1" colorSpace="custom" customColorSpace="genericCMYKColorSpace"/>
// <color key=“textColor” red=“0.72941176470000002" green=“0.53333333329999999” blue=“0.1333333333" alpha=“1”
// colorSpace=“custom” customColorSpace=“sRGB”/>
NSDictionary *attributes = [element attributes];
NSString *colorSpace = [attributes objectForKey: @"colorSpace"];
if (colorSpace)
{
NSUInteger value = 0;
// Put most common first???
if ([@"catalog" isEqualToString: colorSpace])
{
value = 6;
}
else if ([@"calibratedRGB" isEqualToString: colorSpace])
{
value = 1;
}
else if ([@"deviceRGB" isEqualToString: colorSpace])
{
value = 2;
}
else if ([@"calibratedWhite" isEqualToString: colorSpace])
{
value = 3;
}
else if ([@"deviceWhite" isEqualToString: colorSpace])
{
value = 4;
}
else if ([@"custom" isEqualToString: colorSpace])
{
NSString *customSpace = [attributes objectForKey: @"customColorSpace"];
if ([@"genericCMYKColorSpace" isEqualToString: customSpace])
{
value = 5;
}
else if ([@"sRGB" isEqualToString: customSpace])
{
value = 2;
}
else if (customSpace)
{
NSLog(@"%s:unknown custom color space: %@", __PRETTY_FUNCTION__, customSpace);
}
}
else
{
NSLog(@"%s:unknown color space: %@", __PRETTY_FUNCTION__, colorSpace);
}
return [NSNumber numberWithUnsignedInteger: value];
}
return nil;
}
- (id) decodeColorCYMKForElement: (GSXibElement*)element
{
// <color key="backgroundColor" cyan="0.61524784482758621" magenta="0.17766702586206898"
// yellow="0.48752693965517241" black="0.60991379310344829"
// alpha="1" colorSpace="custom" customColorSpace="genericCMYKColorSpace"/>
double cyan = [self decodeDoubleForKey: @"cyan"];
double yellow = [self decodeDoubleForKey: @"yellow"];
double magenta = [self decodeDoubleForKey: @"magenta"];
double black = [self decodeDoubleForKey: @"black"];
double alpha = [self decodeDoubleForKey: @"alpha"];
NSString *string = [NSString stringWithFormat: @"%f %f %f %f %f", cyan, yellow, magenta, black, alpha];
return [string dataUsingEncoding: NSUTF8StringEncoding];
}
- (id) decodeColorRGBForElement: (GSXibElement*)element
{
// <color key="textColor" red="0.0" green="0.0" blue="1" alpha="1" colorSpace="calibratedRGB"/>
double red = [self decodeDoubleForKey: @"red"];
double green = [self decodeDoubleForKey: @"green"];
double blue = [self decodeDoubleForKey: @"blue"];
double alpha = [self decodeDoubleForKey: @"alpha"];
NSString *string = [NSString stringWithFormat: @"%f %f %f %f", red, green, blue, alpha];
return [string dataUsingEncoding: NSUTF8StringEncoding];
}
- (id) decodeColorWhiteForElement: (GSXibElement*)element
{
// <color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
double white = [self decodeDoubleForKey: @"white"];
double alpha = [self decodeDoubleForKey: @"alpha"];
NSString *string = [NSString stringWithFormat: @"%f %f", white, alpha];
return [string dataUsingEncoding: NSUTF8StringEncoding];
}
- (id) decodeBackgroundColorForElement: (GSXibElement*)element
{
id object = [self decodeObjectForKey: @"backgroundColor"];
// Return a default color if none available...
if (object == nil)
object = [NSColor whiteColor];
return object;
}
- (id) decodeScrollerFlagsForElement: (GSXibElement*)element
{
NSUInteger mask = (NSAllScrollerParts << 27); // Default...
NSDictionary *attributes = [element attributes];
NSString *arrowsPosition = [attributes objectForKey: @"arrowsPosition"];
NSString *controlTint = [attributes objectForKey: @"controlTint"];
// Decode arrows position...
if (arrowsPosition == nil)
mask |= (NSScrollerArrowsDefaultSetting << 29);
else if ([@"none" isEqualToString: arrowsPosition])
mask |= (NSScrollerArrowsNone << 29);
else if ([@"default" isEqualToString: arrowsPosition])
mask |= (NSScrollerArrowsDefaultSetting << 29);
else
NSWarnMLog(@"unknown scroller arrows position: %@", arrowsPosition);
// Decode control tint...
if (controlTint == nil)
mask |= NSDefaultControlTint << 16;
else if ([@"blue" isEqualToString: controlTint])
mask |= NSBlueControlTint << 16;
else if ([@"graphite" isEqualToString: controlTint])
mask |= NSGraphiteControlTint << 16;
else if ([@"clear" isEqualToString: controlTint])
mask |= NSClearControlTint << 16;
else
NSWarnMLog(@"unknown control tint: %@", controlTint);
// Return value...
return [NSNumber numberWithUnsignedInt: mask];
}
- (id) decodeScrollerFlags2ForElement: (GSXibElement*)element
{
NSUInteger mask = 0;
NSDictionary *attributes = [element attributes];
NSString *controlSize = [attributes objectForKey: @"controlSize"];
if (controlSize == nil)
mask |= NSControlSizeRegular << 26;
else if ([@"small" isEqualToString: controlSize])
mask |= NSControlSizeSmall << 26;
else if ([@"mini" isEqualToString: controlSize])
mask |= NSControlSizeMini << 26;
else if ([@"regular" isEqualToString: controlSize])
mask |= NSControlSizeRegular << 26;
else
NSWarnMLog(@"unknown scroller control size: %@", controlSize);
return [NSNumber numberWithUnsignedInteger: mask];
}
- (id) decodeScrollViewFlagsForElement: (GSXibElement*)element
{
NSUInteger mask = NSBezelBorder; // Default...
NSDictionary *attributes = [element attributes];
NSString *borderType = [attributes objectForKey: @"borderType"];
// borderType
if (borderType == nil)
{
mask = NSBezelBorder;
}
else if ([@"none" isEqualToString: borderType])
{
mask = NSNoBorder;
}
else if ([@"line" isEqualToString: borderType])
{
mask = NSLineBorder;
}
else if ([@"groove" isEqualToString: borderType])
{
mask = NSGrooveBorder;
}
else
{
NSWarnMLog(@"unknown border type: %@", borderType);
}
// hasVerticalScroller
if ([attributes objectForKey: @"hasVerticalScroller"] == nil)
mask |= (1 << 4);
else
mask |= ([[attributes objectForKey: @"hasVerticalScroller"] boolValue] ? (1 << 4) : 0);
// hasHorizontalScroller
if ([attributes objectForKey: @"hasHorizontalScroller"] == nil)
mask |= (1 << 5);
else
mask |= ([[attributes objectForKey: @"hasHorizontalScroller"] boolValue] ? (1 << 5) : 0);
// autohidesScrollers - if not present then disable...
if ([attributes objectForKey: @"autohidesScrollers"])
mask |= ([[attributes objectForKey: @"autohidesScrollers"] boolValue] ? (1 << 9) : 0);
// Return value...
return [NSNumber numberWithUnsignedInt: mask];
}
- (id) decodeScrollViewHeaderClipViewForElement: (GSXibElement*)element
{
NSTableHeaderView *headerView = [self decodeObjectForKey: @"headerView"];
id object = [[NSClipView alloc] initWithFrame: [headerView frame]];
#if 0
[object setAutoresizesSubviews: YES];
[object setAutoresizingMask: NSViewWidthSizable | NSViewMaxYMargin];
#endif
[object setNextKeyView: (NSView*)headerView];
[object setDocumentView: (NSView*)headerView];
// The header clip view is not retained by the scroll view as it is normally also a sub view.
// So we have to make this object a subview of the current object
{
NSString *parentId = [element attributeForKey: @"id"];
NSView *parent = [decoded objectForKey: parentId];
[parent addSubview: object];
}
return AUTORELEASE(object);
}
- (id) decodeScrollClassFlagsForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = nil;
if ([class isSubclassOfClass: [NSScrollView class]])
{
object = [self decodeScrollViewFlagsForElement: element];
}
else if ([class isSubclassOfClass: [NSScroller class]])
{
object = [self decodeScrollerFlagsForElement: element];
}
else
{
NSWarnMLog(@"called for a class that is NOT a sub-class of NSScrollView/NSScroller - class: %@", NSStringFromClass(class));
}
return object;
}
- (id) decodeTableViewFlagsForElement: (GSXibElement*)element
{
GSTableViewFlagsUnion mask = { { 0 } };
NSDictionary *attributes = [element attributes];
NSDictionary *gridStyleMask = [[element elementForKey: @"gridStyleMask"] attributes];
// These are the defaults...
mask.flags.columnOrdering = YES; // check if present - see below...
mask.flags.columnResizing = YES; // check if present - see below...
mask.flags.drawsGrid = (gridStyleMask != nil);
mask.flags.emptySelection = YES; // check if present - see below...
mask.flags.multipleSelection = YES;
mask.flags.columnSelection = [[attributes objectForKey: @"columnSelection"] boolValue];
mask.flags.columnAutosave = YES;
mask.flags.alternatingRowBackgroundColors = [[attributes objectForKey: @"alternatingRowBackgroundColors"] boolValue];
// Overide the defaults with any attributes present...
if ([attributes objectForKey: @"columnReordering"])
mask.flags.columnOrdering = [[attributes objectForKey: @"columnReordering"] boolValue];
if ([attributes objectForKey: @"columnResizing"])
mask.flags.columnResizing = [[attributes objectForKey: @"columnResizing"] boolValue];
if ([attributes objectForKey: @"emptySelection"])
mask.flags.emptySelection = [[attributes objectForKey: @"emptySelection"] boolValue];
if ([attributes objectForKey: @"multipleSelection"])
mask.flags.multipleSelection = [[attributes objectForKey: @"multipleSelection"] boolValue];
if ([attributes objectForKey: @"autosaveColumns"])
mask.flags.columnAutosave = [[attributes objectForKey: @"autosaveColumns"] boolValue];
// Unknown: typeSelect,
return [NSNumber numberWithUnsignedInteger: mask.value];
}
- (id) decodeTableViewGridLinesForElement: (GSXibElement*)element
{
NSUInteger mask = NSTableViewGridNone;
NSDictionary *attributes = [element attributes];
if ([[attributes objectForKey: @"dashed"] boolValue])
mask |= NSTableViewDashedHorizontalGridLineMask;
else if ([[attributes objectForKey: @"horizontal"] boolValue])
mask |= NSTableViewSolidHorizontalGridLineMask;
if ([[attributes objectForKey: @"vertical"] boolValue])
mask |= NSTableViewSolidHorizontalGridLineMask;
return [NSNumber numberWithUnsignedInteger: mask];
}
- (id) decodeIntercellSpacingHeightForElement: (GSXibElement*)element
{
element = (GSXibElement*)[element elementForKey: @"intercellSpacing"];
return [element attributeForKey: @"height"];
}
- (id) decodeIntercellSpacingWidthForElement: (GSXibElement*)element
{
element = (GSXibElement*)[element elementForKey: @"intercellSpacing"];
return [element attributeForKey: @"width"];
}
- (id) decodeColumnAutoresizingStyleForElement: (GSXibElement*)element
{
NSString *style = [element attributeForKey: @"columnAutoresizingStyle"];
NSUInteger value = NSTableViewUniformColumnAutoresizingStyle;
if ([@"none" isEqualToString: style])
value = NSTableViewNoColumnAutoresizing;
else if ([@"firstColumnOnly" isEqualToString: style])
value = NSTableViewFirstColumnOnlyAutoresizingStyle;
else if ([@"lastColumnOnly" isEqualToString: style])
value = NSTableViewLastColumnOnlyAutoresizingStyle;
else if ([@"sequential" isEqualToString: style])
value = NSTableViewSequentialColumnAutoresizingStyle;
else if ([@"reverseSequential" isEqualToString: style])
value = NSTableViewReverseSequentialColumnAutoresizingStyle;
return [NSString stringWithFormat: @"%"PRIuPTR,value];
}
- (id) decodeTableColumnResizingMaskForElement: (GSXibElement*)element
{
NSDictionary *attributes = [element attributes];
if (attributes)
{
NSUInteger mask = NSTableColumnNoResizing;
if ([[attributes objectForKey: @"resizeWithTable"] boolValue])
mask |= NSTableColumnAutoresizingMask;
if ([[attributes objectForKey: @"userResizable"] boolValue])
mask |= NSTableColumnUserResizingMask;
return [NSNumber numberWithUnsignedInteger: mask];
}
return nil;
}
- (id) decodeTabViewFlagsForElement: (GSXibElement*)element
{
GSTabViewTypeFlagsUnion mask = { { 0 } };
NSDictionary *attributes = [element attributes];
NSString *type = [attributes objectForKey: @"type"];
NSString *controlSize = [attributes objectForKey: @"controlSize"];
NSString *controlTint = [attributes objectForKey: @"controlTint"];
// Set defaults...
mask.flags.controlTint = NSDefaultControlTint;
mask.flags.controlSize = NSControlSizeRegular;
mask.flags.tabViewBorderType = NSTopTabsBezelBorder;
// Decode type...
if ([@"leftTabsBezelBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSLeftTabsBezelBorder;
else if ([@"bottomTabsBezelBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSBottomTabsBezelBorder;
else if ([@"rightTabsBezelBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSRightTabsBezelBorder;
else if ([@"noTabsBezelBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSNoTabsBezelBorder;
else if ([@"noTabsLineBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSNoTabsLineBorder;
else if ([@"noTabsNoBorder" isEqualToString: type])
mask.flags.tabViewBorderType = NSNoTabsNoBorder;
else if (type)
NSWarnMLog(@"unknown tabview type: %@", type);
// Decode control size...
if ([@"small" isEqualToString: controlSize])
mask.flags.controlSize = NSControlSizeSmall;
else if ([@"mini" isEqualToString: controlSize])
mask.flags.controlSize = NSControlSizeMini;
else if ([@"regular" isEqualToString: controlSize])
mask.flags.controlSize = NSControlSizeRegular;
else if (controlSize)
NSWarnMLog(@"unknown control size: %@", controlSize);
// Decode control tint...
if ([@"blue" isEqualToString: controlTint])
mask.flags.controlTint = NSBlueControlTint;
else if ([@"graphite" isEqualToString: controlTint])
mask.flags.controlTint = NSGraphiteControlTint;
else if ([@"clear" isEqualToString: controlTint])
mask.flags.controlTint = NSClearControlTint;
else if (controlTint)
NSWarnMLog(@"unknown control tint: %@", controlTint);
return [NSNumber numberWithUnsignedInteger: mask.value];
}
- (id) decodeTViewFlagsForElement: (GSXibElement*)element
{
NSString *classname = [element attributeForKey: @"class"];
id object = nil;
// Invoke decoding based on class type...
if ([NSClassFromString(classname) isSubclassOfClass: [NSTableView class]])
object = [self decodeTableViewFlagsForElement: element];
else
object = [self decodeTabViewFlagsForElement: element];
return object;
}
- (id) decodeBrowserFlagsForElement: (GSXibElement*)element
{
NSUInteger mask = 0;
NSDictionary *attributes = [element attributes];
id takesTitleFromPreviousColumn = [attributes objectForKey: @"takesTitleFromPreviousColumn"];
id allowsEmptySelection = [attributes objectForKey: @"allowsEmptySelection"];
id acceptsArrowKeys = [attributes objectForKey: @"acceptsArrowKeys"];
// Set the flags...
if ([[attributes objectForKey: @"hasHorizontalScroller"] boolValue])
mask |= 0x10000;
if ((allowsEmptySelection == nil) || ([allowsEmptySelection boolValue] == NO))
mask |= 0x20000;
if ([[attributes objectForKey: @"sendsActionOnArrowKeys"] boolValue])
mask |= 0x40000;
if ((acceptsArrowKeys == nil) || [acceptsArrowKeys boolValue])
mask |= 0x100000;
if ([[attributes objectForKey: @"separatesColumns"] boolValue])
mask |= 0x4000000;
if ((takesTitleFromPreviousColumn == nil) || [takesTitleFromPreviousColumn boolValue])
mask |= 0x8000000; // Cocoa default is YES if Omitted...
if ([[attributes objectForKey: @"titled"] boolValue])
mask |= 0x10000000;
if ([[attributes objectForKey: @"reusesColumns"] boolValue])
mask |= 0x20000000;
if ([[attributes objectForKey: @"allowsBranchSelection"] boolValue])
mask |= 0x40000000;
if ([[attributes objectForKey: @"allowsMultipleSelection"] boolValue])
mask |= 0x80000000;
if ([[attributes objectForKey: @"prefersAllColumnUserResizing"] boolValue])
mask |= 0; // FIXME: do we handle this yet???
return [NSNumber numberWithUnsignedInt: mask];
}
- (id) decodeCellPrototypeForElement: (GSXibElement*)element
{
id object = [[NSBrowserCell alloc] initTextCell: @"BrowserItem"];
[object setType: NSPushInCell];
[object setWraps: NO];
[object sendActionOn: NSLeftMouseUpMask];
[object setEnabled: YES];
return AUTORELEASE(object);
}
- (id) decodeColumnResizingTypeForElement: (GSXibElement*)element
{
NSUInteger value = NSBrowserNoColumnResizing; // Default...
NSString *columnResizingType = [element attributeForKey: @"columnResizingType"];
if ([@"user" isEqualToString: columnResizingType])
value = NSBrowserUserColumnResizing;
else if ([@"auto" isEqualToString: columnResizingType])
value = NSBrowserAutoColumnResizing;
else if (columnResizingType)
NSWarnMLog(@"unknown column resizing type: %@", columnResizingType);
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeNumberOfVisibleColumnsForElement: (GSXibElement*)element
{
NSInteger value = 0; // Cocoa default...
if ([element attributeForKey: @"maxVisibleColumns"])
value = [[element attributeForKey: @"maxVisibleColumns"] integerValue];
return [NSNumber numberWithInteger: value];
}
- (id) decodeClipViewFlagsForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = nil;
if ([class isSubclassOfClass: [NSClipView class]] == NO)
{
NSWarnMLog(@"called for a class that is NOT a sub-class of NSClipView - class: %@", NSStringFromClass(class));
}
else
{
NSUInteger mask = 0;
NSDictionary *attributes = [element attributes];
// copiesOnScroll - defaults to ON...
if ([attributes objectForKey: @"copiesOnScroll"] == nil)
mask |= (1 << 1);
else
mask |= ([[attributes objectForKey: @"copiesOnScroll"] boolValue] ? (1 << 1) : 0);
// drawsBackground - defaults to ON...
if ([attributes objectForKey: @"drawsBackground"] == nil)
mask |= (1 << 2);
else
mask |= ([[attributes objectForKey: @"drawsBackground"] boolValue] ? (1 << 2) : 0);
// Return value...
object = [NSNumber numberWithUnsignedInt: mask];
}
return object;
}
- (id) decodeCellContentsForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = @"";
if ([class isSubclassOfClass: [NSCell class]] == NO)
{
NSWarnMLog(@"called for a class that is NOT a sub-class of NSCell - class: %@", NSStringFromClass(class));
}
else if ([class isSubclassOfClass: [NSFormCell class]])
{
object = [element attributeForKey: @"stringValue"];
}
else
{
// Try the title attribute first as it is the more common encoding...
if ([element attributeForKey: @"title"])
{
object = [element attributeForKey: @"title"];
}
else if ([element elementForKey: @"title"])
{
// If the attribute does not exist check for a title element encoded
// the old way via <string>TITLE</string>...
object = [self decodeObjectForKey: @"title"];
}
else if ([element attributeForKey: @"image"])
{
object = [NSImage imageNamed: [element attributeForKey: @"image"]];
}
#if 0
// If a font is encoded then change the title to an attributed
// string and set the font on it...
if ([object isKindOfClass: [NSString class]] && [element elementForKey: @"font"])
{
NSFont *font = [self decodeObjectForKey: @"font"];
NSDictionary *attributes = [NSDictionary dictionaryWithObject: font forKey: NSFontAttributeName];
object = [[NSAttributedString alloc] initWithString: object attributes: attributes];
AUTORELEASE(object);
}
#endif
}
return object;
}
- (id) decodeCellAlternateContentsForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = @"";
if ([class isSubclassOfClass: [NSCell class]])
{
if ([element attributeForKey: @"alternateTitle"])
{
object = [element attributeForKey: @"alternateTitle"];
}
else if ([element attributeForKey: @"alternateImage"])
{
object = [NSImage imageNamed: [element attributeForKey: @"alternateImage"]];
}
}
return object;
}
- (NSUInteger) decodeStateForElement: (GSXibElement*)element
{
// default is off
NSUInteger state = NSOffState;
NSString *refstate = [element attributeForKey: @"state"];
if (refstate)
{
if ([@"on" isEqualToString: refstate])
{
state = NSOnState;
}
else if ([@"mixed" isEqualToString: refstate])
{
state = NSMixedState;
}
else
{
NSWarnMLog(@"unknown state: %@", refstate);
}
}
return state;
}
- (id) decodeCellFlags1ForElement: (GSXibElement*)element
{
NSNumber *value = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSCell class]])
{
GSCellFlagsUnion mask = { { 0 } };
NSDictionary *attributes = [element attributes];
#if 0
NSString *bezelStyle = [attributes objectForKey: @"bezelStyle"];
#endif
NSString *imageName = [attributes objectForKey: @"image"];
NSString *focusRingType = [attributes objectForKey: @"focusRingType"];
NSString *borderStyle = [attributes objectForKey: @"borderStyle"];
mask.flags.state = [self decodeStateForElement: element];
mask.flags.highlighted = [[attributes objectForKey: @"highlighted"] boolValue];
mask.flags.disabled = ([attributes objectForKey: @"enabled"] ?
[[attributes objectForKey: @"enabled"] boolValue] == NO : NO);
mask.flags.editable = [[attributes objectForKey: @"editable"] boolValue];
mask.flags.vCentered = [[attributes objectForKey: @"alignment"] isEqualToString: @"center"];
mask.flags.hCentered = [[attributes objectForKey: @"alignment"] isEqualToString: @"center"];
mask.flags.bordered = [[borderStyle lowercaseString] containsString: @"border"];
//mask.flags.bezeled = ((bezelStyle != nil) && ([@"regularSquare" isEqualToString: bezelStyle] == NO));
mask.flags.bezeled = [[borderStyle lowercaseString] containsString: @"bezel"];
mask.flags.selectable = [[attributes objectForKey: @"selectable"] boolValue];
mask.flags.scrollable = [[attributes objectForKey: @"scrollable"] boolValue];
mask.flags.lineBreakMode = [self decodeLineBreakMode: element];
mask.flags.truncateLastLine = [[attributes objectForKey: @"truncatesLastVisibleLine"] boolValue];
mask.flags.singleLineMode = [[attributes objectForKey: @"usesSingleLineMode"] boolValue];
mask.flags.continuous = [[attributes objectForKey: @"continuous"] boolValue];
mask.flags.actOnMouseDown = (mask.flags.continuous ? YES : NO);
mask.flags.actOnMouseDragged = (mask.flags.continuous ? YES : NO);
// FIXME: these are unknowns for now...
mask.flags.isLeaf = NO;
mask.flags.invalidObjectValue = NO;
mask.flags.invalidFont = NO;
mask.flags.weakTargetHelperFlag = NO;
mask.flags.allowsAppearanceEffects = NO;
mask.flags.isLoaded = NO;
mask.flags.dontActOnMouseUp = NO;
mask.flags.isWhite = NO;
mask.flags.useUserKeyEquivalent = NO;
mask.flags.showsFirstResponder = NO;
if (imageName)
mask.flags.type = NSImageCellType;
else
mask.flags.type = NSTextCellType;
mask.flags.focusRingType = NSFocusRingTypeDefault;
if ([@"exterior" isEqualToString: focusRingType])
mask.flags.focusRingType = NSFocusRingTypeExterior;
else if ([@"none" isEqualToString: focusRingType])
mask.flags.focusRingType = NSFocusRingTypeNone;
// Return mask...
value = [NSNumber numberWithUnsignedInteger: mask.value];
}
return value;
}
- (id) decodeCellFlags2ForElement: (GSXibElement*)element
{
NSNumber *value = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSCell class]])
{
GSCellFlags2Union mask = { { 0 } };
NSDictionary *attributes = [element attributes];
#if 0
NSString *type = [attributes objectForKey: @"type"];
#endif
NSString *alignment = [attributes objectForKey: @"alignment"];
NSString *controlSize = [attributes objectForKey: @"controlSize"];
mask.flags.allowsEditingTextAttributes = [[attributes objectForKey: @"allowsEditingTextAttributes"] boolValue];
mask.flags.importsGraphics = 0;
mask.flags.lineBreakMode = [self decodeLineBreakMode: element];
mask.flags.refusesFirstResponder = [[attributes objectForKey: @"refusesFirstResponder"] boolValue];
mask.flags.allowsMixedState = [[attributes objectForKey: @"allowsMixedState"] boolValue];
mask.flags.sendsActionOnEndEditing = [[attributes objectForKey: @"sendsActionOnEndEditing"] boolValue];
mask.flags.controlSize = NSRegularControlSize;
mask.flags.doesNotAllowUndo = 0;
mask.flags.controlTint = NSDefaultControlTint;
// Alignment
mask.flags.alignment = NSNaturalTextAlignment;
if ([@"left" isEqualToString: alignment])
mask.flags.alignment = NSLeftTextAlignment;
else if ([@"center" isEqualToString: alignment])
mask.flags.alignment = NSCenterTextAlignment;
else if ([@"right" isEqualToString: alignment])
mask.flags.alignment = NSRightTextAlignment;
else if ([@"justified" isEqualToString: alignment])
mask.flags.alignment = NSJustifiedTextAlignment;
else if (alignment)
NSWarnMLog(@"unknown text alignment: %@", alignment);
// Control size...
if ([@"small" isEqualToString: controlSize])
mask.flags.controlSize = NSSmallControlSize;
else if ([@"mini" isEqualToString: controlSize])
mask.flags.controlSize = NSMiniControlSize;
else if ([@"regular" isEqualToString: controlSize])
mask.flags.controlSize = NSRegularControlSize;
else if (controlSize)
NSWarnMLog(@"unknown control size: %@", controlSize);
value = [NSNumber numberWithUnsignedInteger: mask.value];
}
return value;
}
- (id) decodeCellNormalImageForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = nil;
if ([class isSubclassOfClass: [NSCell class]])
{
if ([element attributeForKey: @"image"])
{
object = [NSImage imageNamed: [element attributeForKey: @"image"]];
}
else
{
NSString *type = [element attributeForKey: @"type"];
if ([@"radio" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSRadioButton"];
}
else if ([@"check" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSSwitch"];
}
else if ([@"disclosure" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSDropDownIndicatorTemplate"];
}
}
}
return object;
}
- (id) decodeCellAlternateImageForElement: (GSXibElement*)element
{
Class class = NSClassFromString([element attributeForKey: @"class"]);
id object = nil;
if ([class isSubclassOfClass: [NSCell class]])
{
if ([element attributeForKey: @"alternateImage"])
{
object = [NSImage imageNamed: [element attributeForKey: @"alternateImage"]];
}
else
{
NSString *type = [element attributeForKey: @"type"];
if ([@"radio" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSRadioButton"];
}
else if ([@"check" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSSwitch"];
}
else if ([@"disclosure" isEqualToString: type])
{
object = [NSImage imageNamed: @"NSDropDownIndicatorTemplate-reversed"];
}
}
}
return object;
}
- (id) decodeSegmentItemImageForElement: (GSXibElement*)element
{
id object = nil;
if ([element attributeForKey: @"image"])
{
object = [NSImage imageNamed: [element attributeForKey: @"image"]];
}
return object;
}
- (id) decodeButtonFlags1ForElement: (GSXibElement*)element
{
NSNumber *value = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSButtonCell class]] == NO)
{
NSWarnMLog(@"attempt to access button flags 2 for NON-NSButtonCell based class");
}
else
{
GSButtonCellFlagsUnion mask = { { 0 } };
NSDictionary *behavior = [[element elementForKey: @"behavior"] attributes];
NSDictionary *attributes = [element attributes];
NSString *imagePos = [attributes objectForKey: @"imagePosition"];
mask.flags.isPushin = [[behavior objectForKey: @"pushIn"] boolValue];
mask.flags.changeContents = [[behavior objectForKey: @"changeContents"] boolValue];
mask.flags.changeBackground = [[behavior objectForKey: @"changeBackground"] boolValue];
mask.flags.changeGray = [[behavior objectForKey: @"changeGray"] boolValue];
mask.flags.highlightByContents = [[behavior objectForKey: @"lightByContents"] boolValue];
mask.flags.highlightByBackground = [[behavior objectForKey: @"lightByBackground"] boolValue];
mask.flags.highlightByGray = [[behavior objectForKey: @"lightByGray"] boolValue];
mask.flags.drawing = [[behavior objectForKey: @"drawing"] boolValue];
mask.flags.isBordered = [attributes objectForKey: @"borderStyle"] != nil;
mask.flags.imageDoesOverlap = [@"only" isEqualToString: imagePos];
mask.flags.imageDoesOverlap |= [@"overlaps" isEqualToString: imagePos];
mask.flags.isHorizontal = [@"left" isEqualToString: imagePos];
mask.flags.isHorizontal |= [@"right" isEqualToString: imagePos];
mask.flags.isBottomOrLeft = [@"left" isEqualToString: imagePos];
mask.flags.isBottomOrLeft |= [@"bottom" isEqualToString: imagePos];
mask.flags.isImageAndText = [@"only" isEqualToString: [attributes objectForKey: @"imagePosition"]] == NO;
mask.flags.isImageSizeDiff = 1; // FIXME...
//mask.flags.hasKeyEquiv = [[behavior objectForKey: @"hasKeyEquiv"] boolValue];
//mask.flags.lastState = [[behavior objectForKey: @"lastState"] boolValue];
mask.flags.isTransparent = [[behavior objectForKey: @"transparent"] boolValue];
mask.flags.inset = [[attributes objectForKey: @"inset"] intValue];
mask.flags.doesNotDimImage = [[behavior objectForKey: @"doesNotDimImage"] boolValue];
mask.flags.useButtonImageSource = 0; //[attributes objectForKey: @"imagePosition"] != nil;
//mask.flags.unused2 = [[behavior objectForKey: @"XXXXX"] boolValue]; // alt mnem loc???
// Return the value...
value = [NSNumber numberWithUnsignedInteger: mask.value];
}
return value;
}
- (id) decodeButtonFlags2ForElement: (GSXibElement*)element
{
NSNumber *value = nil;
Class class = NSClassFromString([element attributeForKey: @"class"]);
if ([class isSubclassOfClass: [NSButtonCell class]] == NO)
{
NSWarnMLog(@"attempt to access button flags 2 for NON-NSButtonCell based class");
}
else
{
GSButtonCellFlags2Union mask = { { 0 } };
NSDictionary *attributes = [element attributes];
NSString *bezelStyle = [attributes objectForKey:@"bezelStyle"];
NSString *imageScaling = [attributes objectForKey:@"imageScaling"];
if (bezelStyle)
{
uint32_t flag = NSRegularSquareBezelStyle; // Default if not specified...
if ([@"rounded" isEqualToString: bezelStyle])
flag = NSRoundedBezelStyle;
else if ([@"regularSquare" isEqualToString: bezelStyle])
flag = NSRegularSquareBezelStyle;
else if ([@"disclosure" isEqualToString: bezelStyle])
flag = NSDisclosureBezelStyle;
else if ([@"shadowlessSquare" isEqualToString: bezelStyle])
flag = NSShadowlessSquareBezelStyle;
else if ([@"circular" isEqualToString: bezelStyle])
flag = NSCircularBezelStyle;
else if ([@"texturedSquare" isEqualToString: bezelStyle])
flag = NSTexturedSquareBezelStyle;
else if ([@"helpButton" isEqualToString: bezelStyle])
flag = NSHelpButtonBezelStyle;
else if ([@"smallSquare" isEqualToString: bezelStyle])
flag = NSSmallSquareBezelStyle;
else if ([@"texturedRounded" isEqualToString: bezelStyle])
flag = NSTexturedRoundedBezelStyle;
else if ([@"roundedRectangle" isEqualToString: bezelStyle])
flag = NSRoundRectBezelStyle;
else if ([@"roundedRect" isEqualToString: bezelStyle])
flag = NSRoundRectBezelStyle;
else if ([@"recessed" isEqualToString: bezelStyle])
flag = NSRecessedBezelStyle;
else if ([@"roundedDisclosure" isEqualToString: bezelStyle])
flag = NSRoundedDisclosureBezelStyle;
#if 0
else if ([@"inline" isEqualToString: bezelStyle])
flag = NSInlineBezelStyle; // New value added in Cocoa version???
#endif
else
NSWarnMLog(@"unknown bezelStyle: %@", bezelStyle);
mask.flags.bezelStyle = (flag & 7);
mask.flags.bezelStyle2 = (flag & 8) >> 3;
if (flag == 0)
NSWarnMLog(@"_bezel_style: %ld", (long)mask.value);
}
// Image scaling...
if ([@"axesIndependently" isEqualToString: imageScaling])
{
mask.flags.imageScaling = 3;
}
else if ([@"proportionallyDown" isEqualToString: imageScaling])
{
mask.flags.imageScaling = 2;
}
else if ([@"proportionallyUpOrDown" isEqualToString: imageScaling])
{
mask.flags.imageScaling = 1;
}
else
{
// Warn about unknown image scaling to add later...
if (imageScaling && [imageScaling length])
NSWarnMLog(@"unknown image scaling: %@", imageScaling);
mask.flags.imageScaling = 0;
}
// keyEquivalentModifierMask...
mask.value |= [[self decodeModifierMaskForElement: element] unsignedIntValue];
// Return value...
value = [NSNumber numberWithUnsignedInteger: mask.value];
}
return value;
}
- (id) decodeMenuItemStateForElement: (GSXibElement*)element
{
return [NSNumber numberWithUnsignedInteger: [self decodeStateForElement: element]];
}
- (id) decodeCellForElement: (GSXibElement*)topElement
{
// Unfortunately cell classes can be overridden by their encompassing class so
// we need to check for these manually...
GSXibElement *element = (GSXibElement*)[topElement elementForKey: @"cell"];
id object = nil;
if (element != nil)
{
// If the current cell definition has no custom class defined...
if ([element attributeForKey: @"customClass"] == nil)
{
// Check encompassing class for cellClass diversion...
Class class = NSClassFromString([topElement attributeForKey: @"class"]);
// If the encompassing class supports cellClass type...
if ([class respondsToSelector: @selector(cellClass)])
[element setAttribute: NSStringFromClass([class cellClass]) forKey: @"class"];
}
// Generate the object normally...
object = [self objectForXib: element];
}
return object;
}
- (id) decodeSelectedIndexForElement: (GSXibElement*)element
{
// We need to get the index into the menuitems for menu...
NSMenu *menu = [self decodeObjectForKey: @"menu"];
NSMenuItem *item = [self decodeObjectForKey: @"selectedItem"];
NSArray *items = [menu itemArray];
NSUInteger index = [items indexOfObjectIdenticalTo: item];
return [NSNumber numberWithUnsignedInteger: index];
}
- (id) decodePreferredEdgeForElement: (GSXibElement*)element
{
NSUInteger value = NSMinXEdge;
NSString *preferredEdge = [element attributeForKey: @"preferredEdge"];
if (preferredEdge)
{
if ([@"minX" isEqualToString: preferredEdge])
value = NSMinXEdge;
else if ([@"maxX" isEqualToString: preferredEdge])
value = NSMaxXEdge;
else if ([@"minY" isEqualToString: preferredEdge])
value = NSMinYEdge;
else if ([@"maxY" isEqualToString: preferredEdge])
value = NSMaxYEdge;
else
NSWarnMLog(@"unknown preferred edge value: %@", preferredEdge);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeArrowPositionForElement: (GSXibElement*)element
{
NSUInteger value = NSPopUpArrowAtBottom; // If omitted Cocoa default...
NSString *arrowPosition = [element attributeForKey: @"arrowPosition"];
if (arrowPosition)
{
if ([@"noArrow" isEqualToString: arrowPosition])
value = NSPopUpNoArrow;
else if ([@"arrowAtCenter" isEqualToString: arrowPosition])
value = NSPopUpArrowAtCenter;
else
NSWarnMLog(@"unknown arrow position value: %@", arrowPosition);
}
return [NSNumber numberWithUnsignedInteger: value];
}
- (id) decodeUsesItemFromMenuForElement: (GSXibElement*)element
{
BOOL value = YES; // If omitted Cocoa default...
NSString *usesItemFromMenu = [element attributeForKey: @"usesItemFromMenu"];
if (usesItemFromMenu)
value = [usesItemFromMenu boolValue];
return [NSNumber numberWithBool: value];
}
- (id) decodeToolbarIdentifiedItemsForElement: (GSXibElement*)element
{
NSArray *allowedItems = [self decodeObjectForKey: @"allowedToolbarItems"];
NSMutableDictionary *map = [NSMutableDictionary dictionary];
NSEnumerator *iter = [allowedItems objectEnumerator];
NSToolbarItem *obj;
while ((obj = [iter nextObject]))
{
[map setObject: obj forKey: [obj itemIdentifier]];
}
return map;
}
- (id) decodeToolbarImageForElement: (GSXibElement*)element
{
NSString *name = [self decodeObjectForKey: @"image"];
return [self findResourceWithName: name];
}
- (id) objectForXib: (GSXibElement*)element
{
id object = [super objectForXib: element];
// If no object check other possibilities related to XIB 5...
if (object == nil)
{
NSString *elementName = [element type];
if ([@"range" isEqualToString: elementName])
{
NSRange range = [self decodeRangeForKey: [element attributeForKey: @"key"]];
object = [NSValue valueWithRange: range];
if ([element attributeForKey: @"id"])
[decoded setObject: object forKey: [element attributeForKey: @"id"]];
}
else if ([XmlTagToDecoderSelectorMap objectForKey: elementName])
{
SEL selector = NSSelectorFromString([XmlTagToDecoderSelectorMap objectForKey: elementName]);
object = [self performSelector: selector withObject: element];
if ([element attributeForKey: @"id"])
[decoded setObject: object forKey: [element attributeForKey: @"id"]];
}
}
return object;
}
- (void) addOrderedObject: (GSXibElement*)element
{
// Create an ordered object for this element...
// This probably needs to be qualified but I have yet to determine
// what that should be right now...
// OK - I think we need at least this qualifier here to avoid excess and
// objects and memory leaks...
NSString *oid = [element attributeForKey: @"id"];
if (oid && [_orderedObjectsDict objectForKey: oid] == nil)
{
id orderedObject = [self orderedObjectForElement: (GSXibElement*)element];
[_orderedObjectsDict setObject: orderedObject forKey: oid];
[_orderedObjects addElement: orderedObject];
}
}
- (id) decodeObjectForXib: (GSXibElement*)element
forClassName: (NSString*)classname
withID: (NSString*)objID
{
// Try decoding the object using super first...
id object = [super decodeObjectForXib: element forClassName: classname withID: objID];
[self addOrderedObject: element];
// Process tooltips...
if ([element attributeForKey: @"toolTip"])
{
if ([object respondsToSelector: @selector(setToolTip:)])
[object setToolTip: [element attributeForKey: @"toolTip"]];
else if ([object respondsToSelector: @selector(setHeaderToolTip:)])
[object setHeaderToolTip: [element attributeForKey: @"toolTip"]];
}
// Process IB runtime attributes for element...
// Ensure we don't process the placeholders...
if ([element elementForKey: @"userDefinedRuntimeAttributes"] &&
([[element attributeForKey: @"class"] isEqualToString: @"IBUserDefinedRuntimeAttributesPlaceholder"] == NO))
{
// Create the flattened property data for the runtime attributes in the OLD XIB format...
id runtimeAttributes = [element elementForKey: @"userDefinedRuntimeAttributes"];
id orderedObject = [_orderedObjectsDict objectForKey: [element attributeForKey: @"id"]];
[self addRuntimeAttributesForElement: runtimeAttributes forID: [orderedObject attributeForKey: @"id"]];
}
return object;
}
- (id) decodeSpecialButtonCellForKey: (NSString *)key
{
// Search field encoding is real basic now...does not include these by default...
// So we're going to generate them here for now...again should be moved into
// class initWithCoder method eventually...
id object = AUTORELEASE([NSButtonCell new]);
unsigned int bFlags = 0x8444000;
GSButtonCellFlags buttonCellFlags;
memcpy((void *)&buttonCellFlags,(void *)&bFlags,sizeof(struct _GSButtonCellFlags));
if ([@"NSSearchButtonCell" isEqualToString: key])
[object setTitle: @"search"];
else
[object setTitle: @"clear"];
[object setTransparent: buttonCellFlags.isTransparent];
[object setBordered: buttonCellFlags.isBordered];
[object setCellAttribute: NSPushInCell to: buttonCellFlags.isPushin];
[object setCellAttribute: NSCellLightsByBackground to: buttonCellFlags.highlightByBackground];
[object setCellAttribute: NSCellLightsByContents to: buttonCellFlags.highlightByContents];
[object setCellAttribute: NSCellLightsByGray to: buttonCellFlags.highlightByGray];
[object setCellAttribute: NSChangeBackgroundCell to: buttonCellFlags.changeBackground];
[object setCellAttribute: NSCellChangesContents to: buttonCellFlags.changeContents];
[object setCellAttribute: NSChangeGrayCell to: buttonCellFlags.changeGray];
if (buttonCellFlags.imageDoesOverlap)
{
if (buttonCellFlags.isImageAndText)
[object setImagePosition: NSImageOverlaps];
else
[object setImagePosition: NSImageOnly];
}
else if (buttonCellFlags.isImageAndText)
{
if (buttonCellFlags.isHorizontal)
{
if (buttonCellFlags.isBottomOrLeft)
[object setImagePosition: NSImageLeft];
else
[object setImagePosition: NSImageRight];
}
else
{
if (buttonCellFlags.isBottomOrLeft)
[object setImagePosition: NSImageBelow];
else
[object setImagePosition: NSImageAbove];
}
}
else
{
[object setImagePosition: NSNoImage];
}
#if 0
[object setBordered: NO];
[object setCellAttribute: NSPushInCell to: NO];
[object setCellAttribute: NSChangeBackgroundCell to: NO];
[object setCellAttribute: NSCellChangesContents to: NO];
[object setCellAttribute: NSChangeGrayCell to: NO];
[object setCellAttribute: NSCellLightsByContents to: YES];
[object setCellAttribute: NSCellLightsByBackground to: NO];
[object setCellAttribute: NSCellLightsByGray to: NO];
[object setImagePosition: NSImageOnly];
[object setImageScaling: NSImageScaleNone];
[object setBezelStyle: NSRoundedBezelStyle];
#endif
return object;
}
- (id) decodeObjectForKey: (NSString *)key
{
id object = [super decodeObjectForKey: key];
// If no object is found, try some other cases before defaulting to remove 'NS' prefix if present...
if (object == nil)
{
// Try to reinterpret the request...
if ([XmlKeyMapTable objectForKey: key])
{
object = [self decodeObjectForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([XmlKeyToDecoderSelectorMap objectForKey: key])
{
SEL selector = NSSelectorFromString([XmlKeyToDecoderSelectorMap objectForKey: key]);
object = [self performSelector: selector withObject: currentElement];
}
else if ([XmlReferenceAttributes containsObject: key])
{
// Elements not stored INSIDE current element potentially need to be cross
// referenced via attribute references...
NSString *idString = [currentElement attributeForKey: key];
GSXibElement *element = [self createReference: idString];
object = [self objectForXib: element];
}
else if ([currentElement attributeForKey: key])
{
// New xib stores values as attributes...
object = [currentElement attributeForKey: key];
}
else if (([@"NSSearchButtonCell" isEqualToString: key]) ||
([@"NSCancelButtonCell" isEqualToString: key]))
{
object = [self decodeSpecialButtonCellForKey: key];
}
else if ([@"NSSupport" isEqualToString: key])
{
// This is the key Cocoa uses for fonts...
// OR images - depending on what's encoded
if ([self containsValueForKey: @"font"])
object = [self decodeObjectForKey: @"font"];
else if ([self containsValueForKey: @"image"])
object = [self decodeObjectForKey: @"image"];
}
else if (([@"NSName" isEqualToString: key]) &&
([@"font" isEqualToString: [currentElement attributeForKey: @"key"]]))
{
// We have to be careful with NSName as it is used by Cocoa in at least three places...
object = [currentElement attributeForKey: @"name"];
}
else if ([@"NSFirstColumnTitle" isEqualToString: key])
{
object = @"Browser";
}
else if ([@"NSPathSeparator" isEqualToString: key])
{
// This would allow to do system dependent path separator decoding...
object = @"/";
}
else if ([key hasPrefix: @"NS"])
{
// Try a key minus a (potential) NS prefix...
NSString *newKey = [self alternateName: key];
object = [self decodeObjectForKey: newKey];
}
#if DEBUG_XIB5
else // DEBUG ONLY...
{
NSWarnMLog(@"no element/attribute for key: %@", key);
}
#endif
}
return object;
}
- (BOOL) decodeBoolForKey: (NSString *)key
{
BOOL flag = NO;
if ([super containsValueForKey: key])
{
flag = [super decodeBoolForKey: key];
}
else if ([currentElement attributeForKey: key])
{
flag = [[currentElement attributeForKey: key] boolValue];
}
else if ([XmlBoolDefaultYes containsObject: key])
{
// Missing values mean YES
flag = YES;
}
else if ([XmlKeyMapTable objectForKey: key])
{
flag = [self decodeBoolForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([XmlKeyToDecoderSelectorMap objectForKey: key])
{
SEL selector = NSSelectorFromString([XmlKeyToDecoderSelectorMap objectForKey: key]);
flag = [[self performSelector: selector withObject: currentElement] boolValue];
}
else if ([key hasPrefix:@"NS"])
{
NSString *newKey = [self alternateName: key];
flag = [self decodeBoolForKey: newKey];
}
#if DEBUG_XIB5
else
{
NSWarnMLog(@"no BOOL for key: %@", key);
}
#endif
return flag;
}
- (NSPoint) decodePointForKey:(NSString *)key
{
NSPoint point = NSZeroPoint;
// If the request element exists...
if ([currentElement elementForKey: key])
{
GSXibElement *element = (GSXibElement*)[currentElement elementForKey: key];
NSDictionary *object = [element attributes];
point.x = [[object objectForKey: @"x"] doubleValue];
point.y = [[object objectForKey: @"y"] doubleValue];
}
else if ([XmlKeyMapTable objectForKey: key])
{
point = [self decodePointForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([key hasPrefix: @"NS"])
{
NSString *newKey = [self alternateName: key];
point = [self decodePointForKey: newKey];
}
else
{
NSWarnMLog(@"no POINT for key: %@", key);
}
return point;
}
- (NSSize) decodeSizeForKey: (NSString*)key
{
NSSize size = NSZeroSize;
// If the request element exists...
if ([currentElement elementForKey: key])
{
GSXibElement *element = (GSXibElement*)[currentElement elementForKey: key];
NSDictionary *object = [element attributes];
size.width = [[object objectForKey: @"width"] doubleValue];
size.height = [[object objectForKey: @"height"] doubleValue];
}
else if ([XmlKeyMapTable objectForKey: key])
{
size = [self decodeSizeForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([key isEqual: @"NSSize"])
{
size.width = [[currentElement attributeForKey: @"width"] doubleValue];
size.height = [[currentElement attributeForKey: @"height"] doubleValue];
}
else if ([key hasPrefix: @"NS"])
{
NSString *newKey = [self alternateName: key];
size = [self decodeSizeForKey: newKey];
}
else
{
NSWarnMLog(@"no SIZE for key: %@", key);
}
return size;
}
- (NSRect) decodeRectForKey: (NSString*)key
{
NSRect frame = NSZeroRect;
// If the request element exists...
if ([currentElement elementForKey: key])
{
frame.origin = [self decodePointForKey: key];
frame.size = [self decodeSizeForKey: key];
}
else if ([XmlKeyMapTable objectForKey: key])
{
frame = [self decodeRectForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([key hasPrefix: @"NS"])
{
NSString *newKey = [self alternateName: key];
frame = [self decodeRectForKey: newKey];
}
else
{
NSWarnMLog(@"no RECT for key: %@", key);
}
return frame;
}
- (NSRange) decodeRangeForKey: (NSString*)key
{
NSRange range = NSMakeRange(0, 0);
GSXibElement *element = (GSXibElement*)[currentElement elementForKey: key];
// If the request element exists...
if (element)
{
range.location = [[element attributeForKey: @"location"] integerValue];
range.length = [[element attributeForKey: @"length"] integerValue];
}
else if ([XmlKeyMapTable objectForKey: key])
{
range = [self decodeRangeForKey: [XmlKeyMapTable objectForKey: key]];
}
else if ([key hasPrefix: @"NS"])
{
NSString *newKey = [self alternateName: key];
range = [self decodeRangeForKey: newKey];
}
else
{
NSWarnMLog(@"no RANGE for key: %@", key);
}
return range;
}
- (BOOL) containsValueForKey: (NSString *)key
{
BOOL hasValue = [super containsValueForKey: key];
// If that didn't work...
if (hasValue == NO)
{
// Try reinterpreting the request...
if ([XmlKeyMapTable objectForKey: key])
{
hasValue = [self containsValueForKey: [XmlKeyMapTable objectForKey: key]];
}
else if (([@"NSIntercellSpacingHeight" isEqualToString: key]) ||
([@"NSIntercellSpacingWidth" isEqualToString: key]))
{
hasValue = [currentElement elementForKey: @"intercellSpacing"] != nil;
}
else if ([@"NSContents" isEqualToString: key])
{
hasValue = [currentElement attributeForKey: @"title"] != nil;
hasValue |= [currentElement attributeForKey: @"image"] != nil;
}
else if ([@"NSAlternateContents" isEqualToString: key])
{
hasValue = [currentElement attributeForKey: @"alternateTitle"] != nil;
hasValue |= [currentElement attributeForKey: @"alternateImage"] != nil;
}
else if ([@"NSHeaderClipView" isEqualToString: key])
{
hasValue = [currentElement elementForKey: @"headerView"] != nil;
}
else if ([@"NSNoAutoenable" isEqualToString: key])
{
hasValue = [currentElement attributeForKey: @"autoenablesItems"] != nil;
}
else if ([@"NSToolbarItemImage" isEqualToString: key])
{
hasValue = [currentElement attributeForKey: @"image"] != nil;
}
else if ([@"NSSelectedIndex" isEqualToString: key])
{
hasValue = [currentElement attributeForKey: @"selectedItem"] != nil;
}
else if ([XmlKeysDefined containsObject: key])
{
// These are arbitrarily defined through hard-coding...
hasValue = YES;
}
else if ([currentElement attributeForKey: key])
{
// Check attributes (for XIB 5 and above) for additional values...
hasValue = YES;
}
else if ([XmlBoolDefaultYes containsObject: key])
{
// Missing values mean YES
hasValue = YES;
}
else if ([key hasPrefix: @"NS"])
{
// Try a key minus a (potential) NS prefix...
NSString *newKey = [self alternateName: key];
hasValue = [self containsValueForKey: newKey];
}
else
{
// Check special cases...
if (([@"action" isEqualToString: key]) || ([@"target" isEqualToString: key]))
{
// Target is stored in the action XIB element - if present - which is
// stored under the connections array element...
NSArray *connections = [self objectForXib: [currentElement elementForKey: @"connections"]];
// FIXME: Shouldn't this be IBActionConnection5 ?
NSPredicate *predicate = [NSPredicate predicateWithFormat: @"className == 'IBActionConnection'"];
NSArray *actions = [connections filteredArrayUsingPredicate: predicate];
hasValue = ([actions count] != 0);
}
}
}
return hasValue;
}
@end