/**
GSXib5KeyedUnarchiver.m
The XIB 5 keyed unarchiver
Copyright (C) 2016,2017 Free Software Foundation, Inc.
Author: Marcian Lytwyn
Date: 12/28/16
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 or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#import "GSXib5KeyedUnarchiver.h"
#import "GNUstepGUI/GSNibLoading.h"
#import "GNUstepGUI/GSXibLoading.h"
#import "GNUstepGUI/GSXibElement.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
@interface NSCustomObject5 : NSCustomObject
{
NSString *_userLabel;
}
- (NSString*) userLabel;
@end
@implementation NSCustomObject5
static NSString *ApplicationClass = nil;
- (id) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
_userLabel = [coder decodeObjectForKey: @"userLabel"];
if (_className)
{
// If we've not set the general application class yet...
if (([NSClassFromString(_className) isKindOfClass: [NSApplication class]]) &&
(ApplicationClass == nil))
{
@synchronized([self class])
{
ASSIGNCOPY(ApplicationClass, _className);
}
}
}
// Override thie one type...
if (_userLabel)
{
if ([@"Application" isEqualToString:_userLabel])
{
if (ApplicationClass == nil)
ASSIGN(_className, @"NSApplication");
else
ASSIGN(_className, ApplicationClass);
}
}
}
return self;
}
- (NSString *) userLabel
{
return _userLabel;
}
@end
@interface NSWindowTemplate5 : NSWindowTemplate
{
BOOL _visibleAtLaunch;
NSToolbar *_toolbar;
}
@end
@implementation NSWindowTemplate5
- (id) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
_visibleAtLaunch = YES;
if ([coder containsValueForKey: @"visibleAtLaunch"])
_visibleAtLaunch = [coder decodeBoolForKey: @"visibleAtLaunch"];
if ([coder containsValueForKey: @"NSToolbar"])
{
_toolbar = [coder decodeObjectForKey: @"NSToolbar"];
}
}
return self;
}
- (id) nibInstantiate
{
if (_realObject == nil)
{
// Instantiate the real object...
[super nibInstantiate];
if (_toolbar)
{
[(NSWindow *)_realObject setToolbar: _toolbar];
}
// >= XIB 5 - startup visible windows...
if (_visibleAtLaunch)
{
// bring visible windows to front...
[(NSWindow *)_realObject orderFront: self];
}
}
return _realObject;
}
@end
@interface IBActionConnection5 : IBActionConnection
{
NSString *trigger;
}
@end
@implementation IBActionConnection5
- (instancetype) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
trigger = nil;
if ([coder allowsKeyedCoding])
{
// label and source string tags have changed for XIB5...
ASSIGN(label, [coder decodeObjectForKey: @"selector"]);
ASSIGN(source, [coder decodeObjectForKey: @"target"]);
// destination string tag is still the same (so far) and loaded
// by base class...
//ASSIGN(destination, [coder decodeObjectForKey: @"destination"]);
// Looks like the 'trigger' attribute should be used to override the
// target/action setup method...
if ([coder containsValueForKey: @"trigger"])
ASSIGN(trigger, [coder decodeObjectForKey: @"trigger"]);
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Can't decode %@ with %@.",NSStringFromClass([self class]),
NSStringFromClass([coder class])];
}
}
return self;
}
- (NSString*) trigger
{
return trigger;
}
- (void) establishConnection
{
if (trigger && [trigger length])
{
SEL sel = NSSelectorFromString(label);
NSString *selName = [NSString stringWithFormat: @"set%@%@:",
[[trigger substringToIndex: 1] uppercaseString],
[trigger substringFromIndex: 1]];
SEL trigsel = NSSelectorFromString(selName);
if (sel && trigsel && [destination respondsToSelector: trigsel])
{
NSWarnMLog(@"setting trigger %@ to selector %@", selName, label);
//[destination setTarget: source]; // Not needed???
[destination performSelector: trigsel withObject: (id)sel];
}
else if (!sel)
{
NSWarnMLog(@"label %@ does not correspond to any selector", label);
}
else if (!trigsel)
{
NSWarnMLog(@"trigger %@ does not correspond to any selector", trigger);
}
else
{
NSWarnMLog(@"destination class (%@) does not respond to trigger selector %@",
NSStringFromClass([destination class]), selName);
}
// PREMATURE RETURN...
return;
}
// Otherwise invoke the super class' method...
[super establishConnection];
}
@end
@interface IBOutletConnection5 : IBOutletConnection
@end
@implementation IBOutletConnection5
- (instancetype) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
if ([coder allowsKeyedCoding])
{
// label string tag has changed for XIB5...
ASSIGN(label, [coder decodeObjectForKey: @"property"]);
// destination and source string tags are still the same (so far) and loaded
// by base class...
}
else
{
[NSException raise: NSInvalidArgumentException
format: @"Can't decode %@ with %@.",NSStringFromClass([self class]),
NSStringFromClass([coder class])];
}
}
return self;
}
@end
@interface IBUserDefinedRuntimeAttribute5 : IBUserDefinedRuntimeAttribute
@end
@implementation IBUserDefinedRuntimeAttribute5
- (id) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self)
{
if ([coder allowsKeyedCoding])
{
[self setTypeIdentifier: [coder decodeObjectForKey: @"type"]];
// Decode value properly...
if ([@"boolean" isEqualToString: typeIdentifier])
[self setValue: [NSNumber numberWithBool: ([@"YES" isEqualToString: value] ? YES : NO)]];
else if ([@"image" isEqualToString: typeIdentifier])
[self setValue: [NSImage imageNamed: value]];
else if ([@"number" isEqualToString: typeIdentifier])
[self setValue: [coder decodeObjectForKey: @"value"]];
else if ([@"point" isEqualToString: typeIdentifier])
[self setValue: [coder decodeObjectForKey: @"value"]];
else if ([@"size" isEqualToString: typeIdentifier])
[self setValue: [coder decodeObjectForKey: @"size"]];
else if ([@"rect" isEqualToString: typeIdentifier])
[self setValue: [coder decodeObjectForKey: @"value"]];
else if ([@"nil" isEqualToString: typeIdentifier])
[self setValue: nil];
NSWarnMLog(@"type: %@ value: %@ (%@)", typeIdentifier, value, [value class]);
}
}
return self;
}
@end
@implementation GSXib5KeyedUnarchiver
static NSDictionary *XmltagToObjectClassCrossReference = 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 (XmltagToObjectClassCrossReference == nil)
{
// These define XML tags (i.e. isSeparatorItem, NSWindowStyleMask->styleMask, etc)
// Note, that unless the associated cross referenced key contains an aattribute 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 it's associated decoding
// method for cross referencing...
XmlKeyMapTable = [NSDictionary dictionaryWithObjectsAndKeys:
@"isSeparatorItem", @"NSIsSeparator",
//@"systemMenu", @"NSName",
@"customClass", @"NSClassName",
@"catalog", @"NSCatalogName",
@"name" , @"NSColorName",
@"selectedItem", @"NSSelectedIndex",
@"pullsDown", @"NSPullDown",
@"prototype", @"NSProtoCell",
@"metaFont", @"IBIsSystemFont",
//@"headerView", @"NSHeaderClipView",
//@"minColumnWidth", @"NSMinColumnWidth",
//@"maxVisibleColumns", @"NSNumberOfVisibleColumns",
@"defaultColumnWidth", @"NSPreferedColumnWidth",
//@"preferredColumnWidth", @"NSPreferedColumnWidth",
@"borderColor", @"NSBorderColor2",
@"fillColor", @"NSFillColor2",
@"horizontalScroller", @"NSHScroller",
@"verticalScroller", @"NSVScroller",
@"keyEquivalent", @"NSKeyEquiv",
@"keyEquivalentModifierMask", @"NSKeyEquivModMask",
@"contentViewMargins", @"NSOffsets",
@"styleMask", @"NSWindowStyleMask",
@"contentView", @"NSWindowView",
@"customClass", @"NSWindowClass",
@"title", @"NSWindowTitle",
@"contentRect", @"NSWindowRect",
@"insertionPointColor", @"NSInsertionColor",
@"vertical", @"NSIsVertical",
@"initialItem", @"NSSelectedTabViewItem",
@"allowsExpansionToolTips", @"NSControlAllowsExpansionToolTips",
@"segments", @"NSSegmentImages",
@"label", @"NSSegmentItemLabel",
@"image", @"NSSegmentItemImage",
@"editable", @"NSIsEditable",
@"objectValues", @"NSPopUpListData",
@"maxNumberOfRows", @"NSMaxNumberOfGridRows",
@"maxNumberOfColumns", @"NSMaxNumberOfGridColumns",
@"sortKey", @"NSKey",
@"name", @"NSBinding",
@"items", @"NSMenuItems",
@"implicitIdentifier", @"NSToolbarIdentifier",
@"allowedToolbarItems", @"NSToolbarIBAllowedItems",
@"displayMode", @"NSToolbarDisplayMode",
@"sizeMode", @"NSToolbarSizeMode",
@"autosavesConfiguration", @"NSToolbarAutosavesConfiguration",
@"allowsUserCustomization", @"NSToolbarAllowsUserCustomization",
@"defaultToolbarItems", @"NSToolbarIBDefaultItems",
@"prefersToBeShown", @"NSToolbarPrefersToBeShown",
@"label", @"NSToolbarItemLabel",
@"paletteLabel", @"NSToolbarItemPaletteLabel",
@"tag", @"NSToolbarItemTag",
@"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. NSContents, NSAlternateContents, NSIntercellSpacingWidth, NSIntercellSpacingHeight, etc)...
XmlKeysDefined = [NSArray arrayWithObjects: @"NSWindowBacking", @"NSWTFlags",
@"NSvFlags", @"NSBGColor",
@"NSSize", //@"IBIsSystemFont",
//@"NSHeaderClipView",
@"NSHScroller", @"NSVScroller", @"NSsFlags", @"NSsFlags2",
@"NSColumnAutoresizingStyle", @"NSTvFlags", @"NScvFlags",
@"NSSupport", @"NSName",
@"NSMenuItem",
@"NSDocView",
@"NSSliderType",
@"NSCellPrototype", @"NSBrFlags", @"NSNumberOfVisibleColumns",
@"NSWhite", @"NSRGB", @"NSCYMK",
//@"NSContents", @"NSAlternateContents",
@"NSCellFlags", @"NSCellFlags2",
@"NSButtonFlags", @"NSButtonFlags2",
@"NSSelectedIndex", @"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. '
//
//
//
//
//
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
{
//
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
{
//
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
{
//
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];
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's the 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 TITLE...
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;
}
- (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 = [[attributes objectForKey:@"state"] isEqualToString: @"on"];
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) decodeButtonStateForElement: (GSXibElement*)element
{
id object = nil;
NSUInteger state = NSOffState;
// If the current cell definition has no custom class defined...
if ([element attributeForKey: @"state"])
{
// Check encompassing class for cellClass diversion...
NSString *refstate = [element attributeForKey: @"state"];
if ([@"on" isEqualToString: refstate])
{
state = NSOnState;
}
else if ([@"mized" isEqualToString: refstate])
{
state = NSMixedState;
}
else if (state)
{
NSWarnMLog(@"unknown cell state: %@", refstate);
}
// Generate the object normally...
object = [NSNumber numberWithUnsignedInteger: state];
}
return object;
}
- (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 decodeMenuItemForElement: element];
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) 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 ([currentElement attributeForKey: key])
{
// New xib stores values as attributes...
object = [currentElement attributeForKey: key];
}
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 = [objects objectForKey: idString];
object = [self objectForXib: element];
}
else if (([@"NSSearchButtonCell" isEqualToString: key]) ||
([@"NSCancelButtonCell" isEqualToString: 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...
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
}
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 defined(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 0
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 ([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