/** 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