diff --git a/Headers/Additions/GNUstepGUI/GSNibLoading.h b/Headers/Additions/GNUstepGUI/GSNibLoading.h index da843bf8e..a4edde1f0 100644 --- a/Headers/Additions/GNUstepGUI/GSNibLoading.h +++ b/Headers/Additions/GNUstepGUI/GSNibLoading.h @@ -213,6 +213,7 @@ typedef struct _GSWindowTemplateFlags - (NSString *)className; - (void) setExtension: (NSString *)ext; - (NSString *)extension; +- (void) setRealObject: (id)obj; @end @interface NSCustomView : NSView diff --git a/Headers/Additions/GNUstepGUI/GSXibElement.h b/Headers/Additions/GNUstepGUI/GSXibElement.h index 68770d984..a6a75136b 100644 --- a/Headers/Additions/GNUstepGUI/GSXibElement.h +++ b/Headers/Additions/GNUstepGUI/GSXibElement.h @@ -38,7 +38,7 @@ @interface GSXibElement: NSObject { NSString *type; - NSDictionary *attributes; + NSMutableDictionary *attributes; NSString *value; NSMutableDictionary *elements; NSMutableArray *values; @@ -60,3 +60,6 @@ #endif +@interface GSXib5Element : GSXibElement +- (void) setAttribute: (id)attribute forKey: (NSString*)key; +@end \ No newline at end of file diff --git a/Headers/Additions/GNUstepGUI/GSXibLoading.h b/Headers/Additions/GNUstepGUI/GSXibLoading.h index 47864c585..db8172fc0 100644 --- a/Headers/Additions/GNUstepGUI/GSXibLoading.h +++ b/Headers/Additions/GNUstepGUI/GSXibLoading.h @@ -191,16 +191,4 @@ @end -@interface GSXibKeyedUnarchiver: NSKeyedUnarchiver -{ - NSMutableDictionary *objects; - NSMutableArray *stack; - GSXibElement *currentElement; - NSMutableDictionary *decoded; -} - -- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element; -- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element; -@end - #endif diff --git a/Headers/AppKit/NSButtonCell.h b/Headers/AppKit/NSButtonCell.h index e238a20a0..20a761009 100644 --- a/Headers/AppKit/NSButtonCell.h +++ b/Headers/AppKit/NSButtonCell.h @@ -114,6 +114,87 @@ typedef enum _NSGradientType { NSGradientConvexStrong } NSGradientType; +typedef struct _GSButtonCellFlags +{ +#if GS_WORDS_BIGENDIAN == 1 + unsigned int isPushin:1; + unsigned int changeContents:1; + unsigned int changeBackground:1; + unsigned int changeGray:1; + + unsigned int highlightByContents:1; + unsigned int highlightByBackground:1; + unsigned int highlightByGray:1; + unsigned int drawing:1; + + unsigned int isBordered:1; + unsigned int imageDoesOverlap:1; + unsigned int isHorizontal:1; + unsigned int isBottomOrLeft:1; + + unsigned int isImageAndText:1; + unsigned int isImageSizeDiff:1; + unsigned int hasKeyEquiv:1; + unsigned int lastState:1; + + unsigned int isTransparent:1; + unsigned int inset:2; // inset:2 + unsigned int doesNotDimImage:1; //doesn't dim:1 + + unsigned int gradient:3; // gradient:3 + unsigned int useButtonImageSource:1; + + unsigned int unused2:8; // alt mnemonic loc. +#else + unsigned int unused2:8; // alt mnemonic loc. + unsigned int useButtonImageSource:1; + unsigned int gradient:3; // gradient:3 + unsigned int doesNotDimImage:1; // doesn't dim:1 + unsigned int inset:2; // inset:2 + unsigned int isTransparent:1; + unsigned int lastState:1; + unsigned int hasKeyEquiv:1; + unsigned int isImageSizeDiff:1; + unsigned int isImageAndText:1; + unsigned int isBottomOrLeft:1; + unsigned int isHorizontal:1; + unsigned int imageDoesOverlap:1; + unsigned int isBordered:1; + unsigned int drawing:1; + unsigned int highlightByGray:1; + unsigned int highlightByBackground:1; + unsigned int highlightByContents:1; + unsigned int changeGray:1; + unsigned int changeBackground:1; + unsigned int changeContents:1; + unsigned int isPushin:1; +#endif +} GSButtonCellFlags; + +typedef struct _GSButtonCellFlags2 { +#if GS_WORDS_BIGENDIAN == 1 + unsigned int keyEquivalentModifierMask:24; + unsigned int imageScaling:2; + unsigned int bezelStyle2:1; + unsigned int mouseInside:1; + unsigned int showsBorderOnlyWhileMouseInside:1; + unsigned int bezelStyle:3; +#else + unsigned int bezelStyle:3; + unsigned int showsBorderOnlyWhileMouseInside:1; + unsigned int mouseInside:1; + unsigned int bezelStyle2:1; + unsigned int imageScaling:2; + unsigned int keyEquivalentModifierMask:24; +#endif +} GSButtonCellFlags2; + +@interface NSCell (Private) +- (NSSize) _scaleImageWithSize: (NSSize)imageSize + toFitInSize: (NSSize)canvasSize + scalingType: (NSImageScaling)scalingType; +@end + @interface NSButtonCell : NSActionCell { diff --git a/Headers/AppKit/NSCell.h b/Headers/AppKit/NSCell.h index eb09927a7..5657cf612 100644 --- a/Headers/AppKit/NSCell.h +++ b/Headers/AppKit/NSCell.h @@ -163,6 +163,184 @@ enum { }; typedef NSInteger NSBackgroundStyle; +enum __NSControlSize { + NSControlSizeRegular, + NSControlSizeSmall, + NSControlSizeMini +}; + +typedef struct _GSCellFlags { +#if GS_WORDS_BIGENDIAN == 1 + unsigned int state:1; + unsigned int highlighted:1; + unsigned int disabled:1; + unsigned int editable:1; + + NSCellType type:2; + unsigned int vCentered:1; + unsigned int hCentered:1; + + unsigned int bordered:1; + unsigned int bezeled:1; + unsigned int selectable:1; + unsigned int scrollable:1; + + unsigned int continuous:1; + unsigned int actOnMouseDown:1; + unsigned int isLeaf:1; + unsigned int invalidObjectValue:1; + + unsigned int invalidFont:1; + NSLineBreakMode lineBreakMode:3; + + unsigned int weakTargetHelperFlag:1; + unsigned int allowsAppearanceEffects:1; + unsigned int singleLineMode:1; + unsigned int actOnMouseDragged:1; + + unsigned int isLoaded:1; + unsigned int truncateLastLine:1; + unsigned int dontActOnMouseUp:1; + unsigned int isWhite:1; + + unsigned int useUserKeyEquivalent:1; + unsigned int showsFirstResponder:1; + unsigned int focusRingType:2; + +#if 0 // FROM COCOA... + // 32 BIT BREAK + unsigned int wasSelectable:1; + unsigned int hasInvalidObject:1; + unsigned int allowsEditingTextAttributes:1; + unsigned int importsGraphics:1; + NSTextAlignment alignment:3; + unsigned int layoutDirectionRTL:1; + unsigned int backgroundStyle:3; + unsigned int cellReserved2:4; + unsigned int refusesFirstResponder:1; + unsigned int needsHighlightedText:1; + unsigned int dontAllowsUndo:1; + unsigned int currentlyEditing:1; + unsigned int allowsMixedState:1; + unsigned int inMixedState:1; + unsigned int sendsActionOnEndEditing:1; + unsigned int inSendAction:1; + unsigned int menuWasSet:1; + unsigned int controlTint:3; + unsigned int controlSize:2; + unsigned int branchImageDisabled:1; + unsigned int drawingInRevealover:1; + unsigned int needsHighlightedTextHint:1; +#endif +#else +#if 0 // FROM COCOA... + unsigned int needsHighlightedTextHint:1; + unsigned int drawingInRevealover:1; + unsigned int branchImageDisabled:1; + unsigned int controlSize:2; + unsigned int controlTint:3; + unsigned int menuWasSet:1; + unsigned int inSendAction:1; + unsigned int sendsActionOnEndEditing:1; + unsigned int inMixedState:1; + unsigned int allowsMixedState:1; + unsigned int currentlyEditing:1; + unsigned int dontAllowsUndo:1; + unsigned int needsHighlightedText:1; + unsigned int refusesFirstResponder:1; + unsigned int cellReserved2:4; + unsigned int backgroundStyle:3; + unsigned int layoutDirectionRTL:1; + NSTextAlignment alignment:3; + unsigned int importsGraphics:1; + unsigned int allowsEditingTextAttributes:1; + unsigned int hasInvalidObject:1; + unsigned int wasSelectable:1; + // 32 BIT BREAK +#endif + + unsigned int focusRingType:2; + unsigned int showsFirstResponder:1; + unsigned int useUserKeyEquivalent:1; + unsigned int isWhite:1; + unsigned int dontActOnMouseUp:1; + unsigned int truncateLastLine:1; + unsigned int isLoaded:1; + unsigned int actOnMouseDragged:1; + unsigned int singleLineMode:1; + unsigned int allowsAppearanceEffects:1; + unsigned int weakTargetHelperFlag:1; + NSLineBreakMode lineBreakMode:3; + unsigned int invalidFont:1; + unsigned int invalidObjectValue:1; + unsigned int isLeaf:1; + unsigned int actOnMouseDown:1; + unsigned int continuous:1; + unsigned int scrollable:1; + unsigned int selectable:1; + unsigned int bezeled:1; + unsigned int bordered:1; + unsigned int hCentered:1; + unsigned int vCentered:1; + NSCellType type:2; + unsigned int editable:1; + unsigned int disabled:1; + unsigned int highlighted:1; + unsigned int state:1; +#endif +} GSCellFlags; + +typedef union _GSCellFlagsUnion +{ + GSCellFlags flags; + uint32_t value; +} GSCellFlagsUnion; + +typedef struct _GSCellflags2 +{ +#if GS_WORDS_BIGENDIAN == 1 + unsigned int unused1:1; + unsigned int allowsEditingTextAttributes:1; + unsigned int importsGraphics:1; + unsigned int alignment:3; + unsigned int refusesFirstResponder:1; + unsigned int allowsMixedState:1; + unsigned int unused2:1; + unsigned int sendsActionOnEndEditing:1; + unsigned int unused3:2; + unsigned int controlSize:3; + unsigned int unused4:4; + unsigned int doesNotAllowUndo:1; + unsigned int lineBreakMode:3; + unsigned int unused5:1; + unsigned int controlTint:3; + unsigned int unused6:5; +#else + unsigned int unused6:5; + unsigned int controlTint:3; + unsigned int unused5:1; + unsigned int lineBreakMode:3; + unsigned int doesNotAllowUndo:1; + unsigned int unused4:4; + unsigned int controlSize:3; + unsigned int unused3:2; + unsigned int sendsActionOnEndEditing:1; + unsigned int unused2:1; + unsigned int allowsMixedState:1; + unsigned int refusesFirstResponder:1; + unsigned int alignment:3; + unsigned int importsGraphics:1; + unsigned int allowsEditingTextAttributes:1; + unsigned int unused1:1; +#endif +} GSCellFlags2; + +typedef union _GSCellFlags2Union +{ + GSCellFlags2 flags; + uint32_t value; +} GSCellFlags2Union; + @interface NSCell : NSObject { // Attributes @@ -170,7 +348,7 @@ typedef NSInteger NSBackgroundStyle; NSImage *_cell_image; NSFont *_font; id _object_value; - struct GSCellFlagsType { + struct GSCellFlagsType { // total 32 bits. 0 bits left. unsigned contents_is_attributed_string: 1; unsigned is_highlighted: 1; diff --git a/Headers/AppKit/NSMatrix.h b/Headers/AppKit/NSMatrix.h index 942391697..ca1025a5c 100644 --- a/Headers/AppKit/NSMatrix.h +++ b/Headers/AppKit/NSMatrix.h @@ -49,6 +49,46 @@ typedef enum _NSMatrixMode { NSTrackModeMatrix } NSMatrixMode; +typedef struct _GSMatrixFlags { +#if GS_WORDS_BIGENDIAN == 1 + unsigned int isHighlight:1; + unsigned int isRadio:1; + unsigned int isList:1; + unsigned int allowsEmptySelection:1; + unsigned int autoScroll:1; + unsigned int selectionByRect:1; + unsigned int drawCellBackground:1; + unsigned int drawBackground:1; + unsigned int autosizesCells:1; + unsigned int drawingAncestor:1; + unsigned int tabKeyTraversesCells:1; + unsigned int tabKeyTraversesCellsExplicitly:1; + unsigned int canSearchIncrementally:1; + unsigned int unused:19; +#else + unsigned int unused:19; + unsigned int canSearchIncrementally:1; + unsigned int tabKeyTraversesCellsExplicitly:1; + unsigned int tabKeyTraversesCells:1; + unsigned int drawingAncestor:1; + unsigned int autosizesCells:1; + unsigned int drawBackground:1; + unsigned int drawCellBackground:1; + unsigned int selectionByRect:1; + unsigned int autoScroll:1; + unsigned int allowsEmptySelection:1; + unsigned int isList:1; + unsigned int isRadio:1; + unsigned int isHighlight:1; +#endif +} GSMatrixFlags; + +typedef union _GSMatrixFlagsUnion +{ + GSMatrixFlags flags; + unsigned int value; +} GSMatrixFlagsUnion; + @protocol NSMatrixDelegate @end diff --git a/Headers/AppKit/NSSplitView.h b/Headers/AppKit/NSSplitView.h index 6467f0a52..2d718fabf 100644 --- a/Headers/AppKit/NSSplitView.h +++ b/Headers/AppKit/NSSplitView.h @@ -38,6 +38,9 @@ enum { NSSplitViewDividerStyleThick = 1, NSSplitViewDividerStyleThin = 2, +#if OS_API_VERSION(MAC_OS_X_VERSION_10_6, GS_API_LATEST) + NSSplitViewDividerStylePaneSplitter = 3, +#endif }; typedef NSInteger NSSplitViewDividerStyle; #endif diff --git a/Headers/AppKit/NSTabView.h b/Headers/AppKit/NSTabView.h index b569e3e07..d6e2a4306 100644 --- a/Headers/AppKit/NSTabView.h +++ b/Headers/AppKit/NSTabView.h @@ -45,6 +45,31 @@ typedef enum { @class NSFont; @class NSTabViewItem; +typedef struct _GSTabViewTypeFlags +{ +#if GS_WORDS_BIGENDIAN == 1 + unsigned int reserved1:1; + unsigned int controlTint:3; + unsigned int controlSize:2; + unsigned int reserved2:18; + unsigned int tabPosition:5; + unsigned int tabViewBorderType:3; +#else + unsigned int tabViewBorderType:3; + unsigned int tabPosition:5; + unsigned int reserved2:18; + unsigned int controlSize:2; + unsigned int controlTint:3; + unsigned int reserved1:1; +#endif +} GSTabViewTypeFlags; + +typedef union _GSTabViewTypeFlagsUnion +{ + GSTabViewTypeFlags flags; + unsigned int value; +} GSTabViewTypeFlagsUnion; + @interface NSTabView : NSView { NSMutableArray *_items; diff --git a/Headers/AppKit/NSTableView.h b/Headers/AppKit/NSTableView.h index 74a229637..5cdfd427f 100644 --- a/Headers/AppKit/NSTableView.h +++ b/Headers/AppKit/NSTableView.h @@ -90,6 +90,35 @@ typedef enum _NSTableViewAnimationOptions } NSTableViewAnimationOptions; #endif +/* + * Nib compatibility struct. This structure is used to + * pull the attributes out of the nib that we need to fill + * in the flags. + */ +typedef struct _tableViewFlags +{ +#if GS_WORDS_BIGENDIAN == 1 + unsigned int columnOrdering:1; + unsigned int columnResizing:1; + unsigned int drawsGrid:1; + unsigned int emptySelection:1; + unsigned int multipleSelection:1; + unsigned int columnSelection:1; + unsigned int columnAutosave:1; + unsigned int _unused:24; +#else + unsigned int _unused:24; + unsigned int columnAutosave:1; + unsigned int unknown1:1; + unsigned int columnSelection:1; + unsigned int multipleSelection:1; + unsigned int emptySelection:1; + unsigned int drawsGrid:1; + unsigned int columnResizing:1; + unsigned int columnOrdering:1; +#endif +} GSTableViewFlags; + @interface NSTableView : NSControl { diff --git a/Headers/AppKit/NSView.h b/Headers/AppKit/NSView.h index cad2910ed..3ec9f8d94 100644 --- a/Headers/AppKit/NSView.h +++ b/Headers/AppKit/NSView.h @@ -96,6 +96,33 @@ typedef enum _NSFocusRingType { NSFocusRingTypeExterior = 2 } NSFocusRingType; +typedef struct _GSvFlags +{ +#if GS_WORDS_BIGENDIAN == 1 + unsigned int isHidden:1; + unsigned int unused1:3; + unsigned int unused2:4; + unsigned int unused3:4; + unsigned int unused4:4; + unsigned int unused5:4; + unsigned int unused6:3; + unsigned int autoresizesSubviews:1; + unsigned int unused7:2; + unsigned int autoresizingMask:6; +#else + unsigned int autoresizingMask:6; + unsigned int unused7:2; + unsigned int autoresizesSubviews:1; + unsigned int unused6:3; + unsigned int unused5:4; + unsigned int unused4:4; + unsigned int unused3:4; + unsigned int unused2:4; + unsigned int unused1:3; + unsigned int isHidden:1; +#endif +} GSvFlags; + @interface NSView : NSResponder { NSRect _frame; diff --git a/Source/GNUmakefile b/Source/GNUmakefile index 500d99c69..4ee186475 100644 --- a/Source/GNUmakefile +++ b/Source/GNUmakefile @@ -254,6 +254,8 @@ GSNibLoader.m \ GSXibElement.m \ GSXibLoader.m \ GSXibLoading.m \ +GSXibKeyedUnarchiver.m \ +GSXib5KeyedUnarchiver.m \ GSXibObjectContainer.m \ GSXibParser.m \ GSHelpAttachment.m diff --git a/Source/GSNibLoading.m b/Source/GSNibLoading.m index a9e680d2c..a61569b55 100644 --- a/Source/GSNibLoading.m +++ b/Source/GSNibLoading.m @@ -52,6 +52,7 @@ #import "GNUstepGUI/GSNibLoading.h" #import "AppKit/NSApplication.h" +#import "AppKit/NSFontManager.h" #import "AppKit/NSImage.h" #import "AppKit/NSMenuItem.h" #import "AppKit/NSMenuView.h" @@ -339,6 +340,10 @@ static BOOL _isInInterfaceBuilder = NO; { if ([coder allowsKeyedCoding]) { + _windowStyle = 0; + _viewClass = @"NSView"; + _windowClass = @"NSWindow"; + if ([coder containsValueForKey: @"NSViewClass"]) { ASSIGN(_viewClass, [coder decodeObjectForKey: @"NSViewClass"]); @@ -347,6 +352,10 @@ static BOOL _isInInterfaceBuilder = NO; { ASSIGN(_windowClass, [coder decodeObjectForKey: @"NSWindowClass"]); } + else + { + ASSIGN(_windowClass, @"NSWindow"); + } if ([coder containsValueForKey: @"NSWindowStyleMask"]) { _windowStyle = [coder decodeIntForKey: @"NSWindowStyleMask"]; @@ -401,7 +410,7 @@ static BOOL _isInInterfaceBuilder = NO; } if ([coder containsValueForKey: @"NSFrameAutosaveName"]) { - ASSIGN(_autosaveName, [coder decodeObjectForKey: @"NSFrameAutosaveName"]); + ASSIGN(_autosaveName, [coder decodeObjectForKey: @"NSFrameAutosaveName"]); } if ([coder containsValueForKey: @"NSWindowTitle"]) { @@ -467,12 +476,11 @@ static BOOL _isInInterfaceBuilder = NO; format: @"Unable to find class '%@'", _windowClass]; } - _realObject = [[aClass allocWithZone: NSDefaultMallocZone()] - initWithContentRect: _windowRect - styleMask: _windowStyle - backing: _backingStoreType - defer: _flags.isDeferred]; - + _realObject = [[aClass allocWithZone: NSDefaultMallocZone()] initWithContentRect: _windowRect + styleMask: _windowStyle + backing: _backingStoreType + defer: _flags.isDeferred]; + // set flags... [_realObject setHidesOnDeactivate: _flags.isHiddenOnDeactivate]; [_realObject setReleasedWhenClosed: !(_flags.isNotReleasedOnClose)]; @@ -493,10 +501,10 @@ static BOOL _isInInterfaceBuilder = NO; [_realObject setTitle: _title]; if ([_viewClass isKindOfClass: [NSToolbar class]]) - { + { // FIXME: No idea what is going on here - [_realObject setToolbar: (NSToolbar*)_viewClass]; - } + [_realObject setToolbar: (NSToolbar*)_viewClass]; + } [_realObject setContentMinSize: _minSize]; [_realObject setContentMaxSize: _maxSize]; @@ -506,11 +514,11 @@ static BOOL _isInInterfaceBuilder = NO; // FIXME What is the point of calling -setFrame:display: here? It looks // like an effective no op to me. // resize the window... - [_realObject setFrame: [NSWindow frameRectForContentRect: [self windowRect] - styleMask: [self windowStyle]] + [_realObject setFrame: [NSWindow frameRectForContentRect: [self windowRect] + styleMask: [self windowStyle]] display: NO]; [_realObject setFrameAutosaveName: _autosaveName]; - } + } return _realObject; } @@ -914,6 +922,7 @@ static BOOL _isInInterfaceBuilder = NO; @end @implementation NSCustomObject + - (void) setClassName: (NSString *)name { ASSIGNCOPY(_className, name); @@ -999,16 +1008,22 @@ static BOOL _isInInterfaceBuilder = NO; format: @"Unable to find class '%@'", _className]; } - if (GSObjCIsKindOf(aClass, [NSApplication class]) || - [_className isEqual: @"NSApplication"]) - { - _object = RETAIN([aClass sharedApplication]); - } + if ((GSObjCIsKindOf(aClass, [NSApplication class])) || + ([_className isEqual: @"NSApplication"])) + { + _object = RETAIN([aClass sharedApplication]); + } + else if ((GSObjCIsKindOf(aClass, [NSFontManager class])) || + ([_className isEqual: @"NSFontManager"])) + { + _object = RETAIN([aClass sharedFontManager]); + } else - { - _object = [[aClass allocWithZone: NSDefaultMallocZone()] init]; - } + { + _object = [[aClass allocWithZone: NSDefaultMallocZone()] init]; + } } + return _object; } diff --git a/Source/GSXib5KeyedUnarchiver.h b/Source/GSXib5KeyedUnarchiver.h new file mode 100644 index 000000000..f2b80500e --- /dev/null +++ b/Source/GSXib5KeyedUnarchiver.h @@ -0,0 +1,41 @@ +/** GSXibParserDelegate.h + + The XIB 5 keyed unarchiver + + Copyright (C) 1996-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 +#import "GSXibKeyedUnarchiver.h" + +@class GSXib5Element; + +@interface GSXib5KeyedUnarchiver : GSXibKeyedUnarchiver +{ + NSDictionary *_context; +} + +- (NSRange) decodeRangeForKey: (NSString*)key; +- (void) setContext: (NSDictionary*)context; +@end diff --git a/Source/GSXib5KeyedUnarchiver.m b/Source/GSXib5KeyedUnarchiver.m new file mode 100644 index 000000000..62658ed2f --- /dev/null +++ b/Source/GSXib5KeyedUnarchiver.m @@ -0,0 +1,3004 @@ +/** GSXib5KeyedUnarchiver.m + + The XIB 5 keyed unarchiver + + Copyright (C) 1996-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/NSButtonCell.h" +#import "AppKit/NSCell.h" +#import "AppKit/NSClipView.h" +#import "AppKit/NSImage.h" +#import "AppKit/NSMatrix.h" +#import "AppKit/NSMenu.h" +#import "AppKit/NSMenuItem.h" +#import "AppKit/NSNib.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/NSTableView.h" +#import "AppKit/NSTabView.h" +#import "AppKit/NSView.h" + + +//#define DEBUG_XIB5 + +@interface NSString (GSXib5KeyedUnarchiver) + +#pragma mark - NSString method(s)... +- (NSString*) stringByDeletingPrefix: (NSString*) prefix; +@end + +@implementation NSString (GSXib5KeyedUnarchiver) + +- (NSString*) stringByDeletingPrefix: (NSString*) prefix +{ + if ([self length] > [prefix length]) + { + if ([[self substringWithRange:NSMakeRange(0, [prefix length])] isEqualToString:prefix]) + { + NSString *key = [self substringWithRange:NSMakeRange([prefix length], [self length]-[prefix length])]; + return key; + } + } + + return nil; +} + +@end + +@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]) + { + ASSIGN(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; +} +@end + +@implementation NSWindowTemplate5 + +- (id) initWithCoder: (NSCoder *)coder +{ + self = [super initWithCoder: coder]; + if (self) + { + _visibleAtLaunch = YES; + + if ([coder containsValueForKey: @"visibleAtLaunch"]) + _visibleAtLaunch = [coder decodeBoolForKey: @"visibleAtLaunch"]; + } + + return self; +} + +- (id) nibInstantiate +{ + // Instantiate the real object... + id object = [super nibInstantiate]; + + // >= XIB 5 - startup visible windows... + if (_visibleAtLaunch) + { + // bring visible windows to front... + [(NSWindow *)object orderFront: self]; + } + + return object; +} + +@end + +@interface IBOutletConnection5 : IBOutletConnection +@end + +@implementation IBOutletConnection5 + +- (instancetype)initWithCoder:(NSCoder *)coder +{ + self = [super initWithCoder: coder]; + if (self) + { + if ([coder allowsKeyedCoding]) + { + if ([coder containsValueForKey: @"property"]) + { + ASSIGN(label, [coder decodeObjectForKey: @"property"]); + } + } + 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]]; +#if 0 + else if ([@"number" isEqualToString: typeIdentifier]) + [self setValue [coder decodeObjectForKey: @"value"]]; + else if ([@"point" isEqualToString: typeIdentifier]) + [self setValue: [coder decodeObjectForKey: @"point"]]; + else if ([@"size" isEqualToString: typeIdentifier]) + [self setValue: [code decodeObjectForKey: @"size"]]; + else if ([@"rect" isEqualToString: typeIdentifier]) + [self setValue: [coder decodeObjectForKey: @"value"]]; + NSWarnMLog(@"type: %@ value: %@ (%@)", typeIdentifier, value, [value class]); +#endif + } + } + + 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; + ++ (void) initialize +{ + if (self == [GSXib5KeyedUnarchiver class]) + { + @synchronized(self) + { + // Only check one since we're going to load all once... + if (XmltagToObjectClassCrossReference == nil) + { + XmltagToObjectClassCrossReference = @{ @"objects" : @"NSMutableArray", + @"items" : @"NSMutableArray", + @"tabViewItems" : @"NSMutableArray", + @"connections" : @"NSMutableArray", + @"subviews" : @"NSMutableArray", + @"tableColumns" : @"NSMutableArray", + @"cells" : @"NSMutableArray", + @"column" : @"NSMutableArray", + @"tabStops" : @"NSMutableArray", + @"userDefinedRuntimeAttributes" : @"NSMutableArray", + @"customObject" : @"NSCustomObject5", + @"userDefinedRuntimeAttribute" : @"IBUserDefinedRuntimeAttribute5", + //@"outlet" : @"IBOutletConnection5", + //@"action" : @"IBActionConnection", + @"window" : @"NSWindowTemplate5" }; + RETAIN(XmltagToObjectClassCrossReference); + + XmltagsNotStacked = @[ @"document" ]; + RETAIN(XmltagsNotStacked); + + XmltagsToSkip = @[ @"dependencies" ]; + RETAIN(XmltagsToSkip); + + ClassNamePrefixes = @[ @"NS", @"IB" ]; + RETAIN(ClassNamePrefixes); + + XmlReferenceAttributes = @[ @"headerView" ]; + RETAIN(XmlReferenceAttributes); + + XmlKeyMapTable = @{ @"NSIsSeparator" : @"isSeparatorItem", + //@"NSName" : @"systemMenu", + @"NSClassName" : @"customClass", + @"NSCatalogName" : @"catalog", + @"NSColorName" : @"name", + @"NSSelectedIndex" : @"selectedItem", + @"NSNoAutoenable" : @"autoenablesItems", + @"NSPullDown" : @"pullsDown", + @"NSProtoCell" : @"prototype", + @"IBIsSystemFont" : @"metaFont", + //@"NSHeaderClipView" : @"headerView", + @"NSHScroller" : @"horizontalScroller", + @"NSVScroller" : @"verticalScroller", + @"NSKeyEquiv" : @"keyEquivalent", + @"NSKeyEquivModMask" : @"keyEquivalentModifierMask", + @"NSOffsets" : @"contentViewMargins", + @"NSWindowStyleMask" : @"styleMask", + @"NSWindowView" : @"contentView", + @"NSWindowClass" : @"customClass", + @"NSWindowTitle" : @"title", + @"windowPositionMask" : @"initialPositionMask", + @"NSWindowRect" : @"contentRect", + @"NSInsertionColor" : @"insertionPointColor", + @"NSIsVertical" : @"vertical" }; + RETAIN(XmlKeyMapTable); + + XmlKeysDefined = @[ @"NSWTFlags", @"NSvFlags", @"NSBGColor", + @"NSSize", //@"IBIsSystemFont", + @"NSHeaderClipView", @"NSHScroller", @"NSVScroller", @"NSsFlags", + @"NSTvFlags", @"NScvFlags", + @"NSSupport", @"NSName", + @"NSMenuItem", + @"NSDocView", + @"NSSliderType", + @"NSWhite", @"NSRGB", @"NSCYMK", + @"NSContents", @"NSAlternateContents", + @"NSCellFlags", @"NSCellFlags2", + @"NSButtonFlags", @"NSButtonFlags2", + @"NSSelectedIndex", @"NSAltersState", + @"NSNormalImage", @"NSAlternateImage", + @"NSBorderType", @"NSBoxType", @"NSTitlePosition", + @"NSTitleCell", @"NSOffsets", + @"NSMatrixFlags", @"NSNumCols", @"NSNumRows", + @"NSSharedData", @"NSFlags", + @"NSpiFlags" ]; + RETAIN(XmlKeysDefined); + + XmlTagToDecoderSelectorMap = @{ @"tableColumnResizingMask" : @"decodeTableColumnResizingMaskForElement:", + @"autoresizingMask" : @"decodeAutoresizingMaskForElement:", + @"windowStyleMask" : @"decodeWindowStyleMaskForElement:", + @"windowPositionMask" : @"decodeWindowPositionMaskForElement:", + //@"modifierMask" : @"decodeModifierMaskForElement:", + @"tableViewGridLines" : @"decodeTableViewGridLinesForElement" }; + RETAIN(XmlTagToDecoderSelectorMap); + + XmlKeyToDecoderSelectorMap = @{ @"NSIntercellSpacingHeight" : @"decodeIntercellSpacingHeightForElement:", + @"NSIntercellSpacingWidth" : @"decodeIntercellSpacingWidthForElement:", + @"columnAutoresizingStyle" : @"decodeColumnAutoresizingStyleForElement:", + @"NSName" : @"decodeNameForElement:", + @"NSSliderType" : @"decodeSliderCellTypeForElement:", + @"NSTickMarkPosition" : @"decodeSliderCellTickMarkPositionForElement:", + @"NSCells" : @"decodeCellsForElement:", + @"NSNumCols" : @"decodeNumberOfColumnsInMatrixForElement:", + @"NSNumRows" : @"decodeNumberOfRowsInMatrixForElement:", + @"pullsDown" : @"decodePullsDownForElement:", + @"autoenablesItems" : @"decodeAutoenablesItemsForElement:", + @"NSAltersState" : @"decodeAltersStateForElement:", + @"NSMenuItem" : @"decodeMenuItemForElement:", + @"selectedItem" : @"decodeSelectedIndexForElement:", + @"NSTitleCell" : @"decodeTitleCellForElement:", + @"NSBorderType" : @"decodeBorderTypeForElement:", + @"NSBoxType" : @"decodeBoxTypeForElement:", + @"NSTitlePosition" : @"decodeTitlePositionForElement:", + //@"NSSearchButtonCell" : @"decodeSearchButtonForElement:", + //@"NSCancelButtonCell" : @"decodeSearchButtonForElement:", + @"keyEquivalentModifierMask" : @"decodeModifierMaskForElement:", + @"NSState" : @"decodeButtonStateForElement:", + @"NSCell" : @"decodeCellForElement:", + @"NSSize" : @"decodeFontSizeForElement:", + //@"IBIsSystemFont" : @"decodeFontTypeForElement:", + @"NSpiFlags" : @"decodeProgressIndicatorFlagsForElement:", + @"NSFlags" : @"decodeTextViewFlagsForElement:", + @"NSSharedData" : @"decodeSharedDataForElement:", + @"NSMatrixFlags" : @"decodeMatrixFlagsForElement:", + @"NSsFlags" : @"decodeScrollClassFlagsForElement:", + @"NSHeaderClipView" : @"decodeScrollViewHeaderClipViewForElement:", + @"NSBGColor" : @"decodeBackgroundColorForElement:", + @"NScvFlags" : @"decodeClipViewFlagsForElement:", + @"NSTvFlags" : @"decodeTViewFlagsForElement:", + @"NSvFlags" : @"decodeViewFlagsForElement:", + @"NSContents" : @"decodeCellContentsForElement:", + @"NSAlternateContents" : @"decodeCellAlternateContentsForElement:", + @"NSCellFlags" : @"decodeCellFlags1ForElement:", + @"NSCellFlags2" : @"decodeCellFlags2ForElement:", + @"NSButtonFlags" : @"decodeButtonFlags1ForElement:", + @"NSButtonFlags2" : @"decodeButtonFlags2ForElement:", + @"NSNormalImage" : @"decodeCellNormalImageForElement:", + @"NSAlternateImage" : @"decodeCellAlternateImageForElement:", + @"NSWTFlags" : @"decodeWindowTemplateFlagsForElement:", + @"NSDocView" : @"decodeClipViewDocumentViewForElement:", + @"NSWhite" : @"decodeColorWhiteForElement:", + @"NSRGB" : @"decodeColorRGBForElement:", + @"NSColorSpace" : @"decodeColorSpaceForElement:", + @"NSCYMK" : @"decodeColorCYMKForElement:" }; + RETAIN(XmlKeyToDecoderSelectorMap); + } + } + } +} + +#pragma mark - Class level support method(s)... ++ (NSInteger) coderVersion +{ + return 5; +} + ++ (NSString*) classNameForXibTag: (NSString*)xibTag +{ + NSString *className = [XmltagToObjectClassCrossReference objectForKey:xibTag]; + + if (nil == className) + { + NSEnumerator *iter = [ClassNamePrefixes objectEnumerator]; + NSString *prefix = nil; + NSString *baseString = [[xibTag substringToIndex:1] capitalizedString]; + baseString = [baseString stringByAppendingString:[xibTag substringFromIndex:1]]; + + // Try to generate a default name from tag... + while ((prefix = [iter nextObject])) + { + NSString *theClassName = [NSString stringWithFormat:@"%@%@",prefix,baseString]; +#if defined(DEBUG_XIB5) + NSWarnMLog(@"%@ - trying: %@", xibTag, theClassName); +#endif + if (NSClassFromString(theClassName)) + { + className = theClassName; + break; + } + } + } + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"xibTag: %@ className: %@", xibTag, className); +#endif + return className; +} + ++ (Class) classForXibTag: (NSString*)xibTag +{ + return NSClassFromString([self classNameForXibTag:xibTag]); +} + +#pragma mark - Instance level support method(s)... +- (void)setContext:(NSDictionary *)context +{ + ASSIGN(_context, context); +} + +#pragma mark - Instance initialization method(s)... +- (id) initForReadingWithData: (NSData*)data +{ +#if GNUSTEP_BASE_HAVE_LIBXML + NSXMLParser *theParser; + NSData *theData = data; + + if (theData == nil) + { + return nil; + } + + objects = [[NSMutableDictionary alloc] init]; + stack = [[NSMutableArray alloc] init]; + decoded = [[NSMutableDictionary alloc] init]; + + theParser = [[NSXMLParser alloc] initWithData: theData]; + [theParser setDelegate: self]; + + NS_DURING + { + // Parse the XML data + [theParser parse]; + } + NS_HANDLER + { + NSLog(@"Exception occurred while parsing Xib: %@",[localException reason]); + DESTROY(self); + } + NS_ENDHANDLER + + DESTROY(theParser); +#endif + + return self; +} + +#pragma mark - XML decoding method(s)... +- (void) parser: (NSXMLParser*)parser +didStartElement: (NSString*)elementName + namespaceURI: (NSString*)namespaceURI + qualifiedName: (NSString*)qualifiedName + attributes: (NSDictionary*)attributeDict +{ + NSMutableDictionary *attributes = AUTORELEASE([attributeDict mutableCopy]); + NSString *className = nil; + NSString *elementType = elementName; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"elementName: %@ className: %@ namespaceURI: %@ qName: %@ attrs: %@", elementName, className, namespaceURI, qualifiedName, attributes); +#endif + + // Skip certain element names - for now... + if ([XmltagsToSkip containsObject:elementName] == NO) + { + if (([@"window" isEqualToString: elementName] == NO) && + ([@"customView" isEqualToString: elementName] == NO) && + ([@"customObject" isEqualToString: elementName] == NO)) + className = [attributes objectForKey: @"customClass"]; + if (nil == className) + className = [[self class] classNameForXibTag:elementName]; + + if (nil != className) + { + if ([NSClassFromString(className) isSubclassOfClass:[NSArray class]]) + elementType = @"array"; + else if ([@"string" isEqualToString: elementName] == NO) + elementType = @"object"; + } + + // Add the necessary attribute(s)... + if (className) + [attributes setObject: className forKey: @"class"]; + + if ([attributes objectForKey:@"key"] == nil) + { + // Special cases to allow current initWithCoder methods to obtain objects... + if ([@"objects" isEqualToString:elementName]) + { + [attributes setObject:@"IBDocument.RootObjects" forKey:@"key"]; + } + else if (([@"items" isEqualToString:elementName]) && + ([[currentElement attributeForKey: @"class"] isEqualToString:@"NSMenu"])) + { + [attributes setObject: @"NSMenuItems" forKey: @"key"]; + } + else + { + [attributes setObject: elementName forKey: @"key"]; + } + } + + if (([attributes objectForKey: @"customClass"] == nil) || + ([NSClassFromString([attributes objectForKey: @"customClass"]) isSubclassOfClass: [NSApplication class]] == NO)) + if ([[attributes objectForKey: @"userLabel"] isEqualToString: @"Application"]) + [attributes setObject:@"NSApplication" forKey:@"customClass"]; + + // FOR DEBUG...CAN BE REMOVED... + [attributes setObject: elementName forKey: @"key5"]; + + // Generate the XIB element object... + GSXib5Element *element = [[GSXib5Element alloc] initWithType: elementType + andAttributes: attributes]; + NSString *key = [attributes objectForKey: @"key"]; + NSString *ref = [attributes objectForKey: @"id"]; + + // FIXME: We should use proper memory management here + AUTORELEASE(element); + + if ([@"array" isEqualToString: [currentElement type]]) + { + // For arrays + [currentElement addElement: element]; + } + else + { + // For elements... + [currentElement setElement: element forKey: key]; + } + + // Reference(s)... + if (ref != nil) + { + [objects setObject: element forKey: ref]; + } + + if ([XmltagsNotStacked containsObject:elementName] == NO) + { + // Push element onto stack... + [stack addObject: currentElement]; + } + + // Set as current element being processed... + currentElement = element; + } +} + +- (void) parser: (NSXMLParser*)parser + didEndElement: (NSString*)elementName + namespaceURI: (NSString*)namespaceURI + qualifiedName: (NSString*)qName +{ +#if defined(DEBUG_XIB5) + NSWarnMLog(@"%s:elementName: %@ namespaceURI: %@ qName: %@", __PRETTY_FUNCTION__, + elementName, namespaceURI, qName); +#endif + + // Skip certain element names - for now... + if ([XmltagsToSkip containsObject:elementName] == NO) + { + if ([XmltagsNotStacked containsObject:elementName] == NO) + { + // Pop element... + currentElement = [stack lastObject]; + [stack removeLastObject]; + } + } +} + +#pragma mark - Decoding method(s)... +// All this code should eventually move into their respective initWithCoder class +// methods - however note - there are a couple that may be duplicated... +- (id) decodeIntercellSpacingHeightForElement: (GSXib5Element*)element +{ + element = [element elementForKey: @"intercellSpacing"]; + return [element attributeForKey: @"height"]; +} + +- (id) decodeIntercellSpacingWidthForElement: (GSXib5Element*)element +{ + element = [element elementForKey: @"intercellSpacing"]; + return [element attributeForKey: @"width"]; +} + +- (id) decodeColumnAutoresizingStyleForElement: (GSXib5Element*)element +{ + NSString *style = [element elementForKey: @"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; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"value: %lu", value); +#endif + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id) decodeWindowStyleMaskForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + + if (attributes) + { + NSUInteger mask = 0; + + if ([[attributes objectForKey: @"titled"] boolValue]) + mask |= NSTitledWindowMask; + if ([[attributes objectForKey: @"closable"] boolValue]) + mask |= NSClosableWindowMask; + if ([[attributes objectForKey: @"miniaturizable"] boolValue]) + mask |= NSMiniaturizableWindowMask; + if ([[attributes objectForKey: @"resizable"] boolValue]) + mask |= NSResizableWindowMask; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %lu", mask); +#endif + + return [NSNumber numberWithUnsignedInteger: mask]; + } + + return nil; +} + +- (id) decodeTableColumnResizingMaskForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + + if (attributes) + { + NSUInteger mask = NSTableColumnNoResizing; + + if ([[attributes objectForKey: @"resizeWithTable"] boolValue]) + mask |= NSTableColumnAutoresizingMask; + if ([[attributes objectForKey: @"userResizable"] boolValue]) + mask |= NSTableColumnUserResizingMask; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %lu", mask); +#endif + return [NSNumber numberWithUnsignedInteger: mask]; + } + + return nil; +} + +- (id) decodeAutoresizingMaskForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + + if (attributes) + { + NSUInteger mask = NSViewNotSizable; + + if ([[attributes objectForKey: @"flexibleMinX"] boolValue]) + mask |= NSViewMinXMargin; + if ([[attributes objectForKey: @"widthSizable"] boolValue]) + mask |= NSViewWidthSizable; + if ([[attributes objectForKey: @"flexibleMaxX"] boolValue]) + mask |= NSViewMaxXMargin; + if ([[attributes objectForKey: @"flexibleMinY"] boolValue]) + mask |= NSViewMinYMargin; + if ([[attributes objectForKey: @"heightSizable"] boolValue]) + mask |= NSViewHeightSizable; + if ([[attributes objectForKey: @"flexibleMaxY"] boolValue]) + mask |= NSViewMaxYMargin; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"attributes: %@ mask: %p", attributes, mask); +#endif + return [NSNumber numberWithUnsignedInt: mask]; + } + + return nil; +} + +- (id) decodeModifierMaskForElement: (GSXib5Element*)element +{ + id object = nil; + NSDictionary *attributes = [[element elementForKey: @"keyEquivalentModifierMask"] attributes]; + + if (attributes == nil) + { + // Seems that Apple decided to omit this attribute IF Control key alone + // is applied. If this key is present WITH NO setting then that NULL + // value is used for the modifier mask... + object = [NSNumber numberWithUnsignedInteger: NSCommandKeyMask]; + } + else + { + // If the modifier mask element is present then no modifier attributes + // equates to no key modifiers applied... + NSUInteger mask = 0; + + if ([[attributes objectForKey:@"option"] boolValue]) + { + mask |= NSAlternateKeyMask; + } + if ([[attributes objectForKey:@"alternate"] boolValue]) + { + mask |= NSAlternateKeyMask; + } + if ([[attributes objectForKey:@"command"] boolValue]) + { + mask |= NSCommandKeyMask; + } + if ([[attributes objectForKey:@"control"] boolValue]) + { + mask |= NSControlKeyMask; + } + if ([[attributes objectForKey:@"shift"] boolValue]) + { + mask |= NSShiftKeyMask; + } + if ([[attributes objectForKey:@"numeric"] boolValue]) + { + mask |= NSNumericPadKeyMask; + } + if ([[attributes objectForKey:@"help"] boolValue]) + { + mask |= NSHelpKeyMask; + } + if ([[attributes objectForKey:@"function"] boolValue]) + { + mask |= NSFunctionKeyMask; + } + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %lu", mask); +#endif + + object = [NSNumber numberWithUnsignedInteger: mask]; + } + + return object; +} + +- (id) decodeTableViewGridLinesForElement: (GSXib5Element*)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; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %p", mask); +#endif + + return [NSNumber numberWithUnsignedInteger: mask]; +} + +- (id) decodeClipViewDocumentViewForElement: (GSXib5Element*)element +{ + NSArray *subviews = [self decodeObjectForKey: @"subviews"]; + + if ([subviews count] == 0) + NSWarnMLog(@"no clipview document view for element: %@", element); + else + return [subviews objectAtIndex: 0]; + + return nil; +} + +- (id) decodeWindowTemplateFlagsForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + + if (attributes) + { + typedef union _GSWindowTemplateFlagsUnion + { + GSWindowTemplateFlags flags; + uint32_t value; + } GSWindowTemplateFlagsUnion; + + GSWindowTemplateFlagsUnion mask = { 0 }; + GSXib5Element *winPosMaskEleme = [currentElement elementForKey: @"initialPositionMask"]; + NSUInteger winPosMask = [[self decodeWindowPositionMaskForElement:winPosMaskEleme] unsignedIntegerValue]; + + mask.flags.isHiddenOnDeactivate = [[attributes objectForKey: @"hidesOnDeactivate"] boolValue]; + mask.flags.isNotReleasedOnClose = ![[attributes objectForKey: @"releasedWhenClosed"] boolValue]; + mask.flags.isDeferred = ![[attributes objectForKey: @"visibleAtLaunch"] boolValue]; + mask.flags.isOneShot = ([attributes objectForKey: @"oneShot"] ? + [[attributes objectForKey: @"oneShot"] boolValue] : YES); + + mask.flags.isVisible = [[attributes objectForKey: @"visibleAtLaunch"] boolValue]; + mask.flags.wantsToBeColor = 0; //[[attributes objectForKey: @"visibleAtLaunch"] boolValue]; + mask.flags.dynamicDepthLimit = 0; //[[attributes objectForKey: @"visibleAtLaunch"] boolValue]; + mask.flags.autoPositionMask = winPosMask; + mask.flags.savePosition = [attributes objectForKey: @"frameAutosaveName"] != nil; + mask.flags.style = 0; //[[attributes objectForKey: @"visibleAtLaunch"] boolValue]; + + return [NSNumber numberWithUnsignedInteger: mask.value]; + } + + return nil; +} + +- (id) decodeWindowPositionMaskForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + + if (attributes) + { + NSUInteger mask = 0; + + return [NSNumber numberWithUnsignedInteger: mask]; + } + + return nil; +} + +- (id)decodeMatrixFlagsForElement: (GSXib5Element*)element +{ + NSString *mode = [element attributeForKey: @"mode"]; + NSString *allowsEmptySelection = [element attributeForKey: @"allowsEmptySelection"]; + NSString *autosizesCells = [element attributeForKey: @"autosizesCells"]; + NSString *drawsBackground = [element attributeForKey: @"drawsBackground"]; + NSString *selectionByRect = [element attributeForKey: @"selectionByRect"]; + GSMatrixFlagsUnion mask = { 0 }; + + // mode... + if ([@"list" isEqualToString: mode]) + { + mask.flags.isList = 1; + } + else if ([@"highlight" isEqualToString: mode]) + { + mask.flags.isHighlight = 1; + } + else if ([@"radio" isEqualToString: mode]) + { + mask.flags.isRadio = 1; + } + else if ([@"track" isEqualToString: mode]) + { + // What do we do with this type??? + } + else if (mode) + { + NSWarnMLog(@"unknown matrix mode: %@", mode); + } + + // allows empty selection... + if (allowsEmptySelection == nil) + mask.flags.allowsEmptySelection = 1; + else + mask.flags.allowsEmptySelection = [allowsEmptySelection boolValue]; + + // autosizes cells... + if (autosizesCells == nil) + mask.flags.autosizesCells = 1; + else + mask.flags.autosizesCells = [autosizesCells boolValue]; + + // draw background/cell background... + if (drawsBackground) + mask.flags.drawBackground = [drawsBackground boolValue]; + mask.flags.drawCellBackground = mask.flags.drawBackground; + + // selection by rectangle... + if (selectionByRect == nil) + mask.flags.selectionByRect = 1; + else + mask.flags.selectionByRect = [selectionByRect boolValue]; + + return [NSNumber numberWithUnsignedInt: mask.value]; +} + +- (id)decodeNumberOfColumnsInMatrixForElement: (GSXib5Element*)element +{ + id object = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSMatrix class]]) + { + NSArray *cells = [self decodeObjectForKey: @"cells"]; + object = [NSNumber numberWithUnsignedInteger: [cells count]]; + } + + return object; +} + +- (id)decodeNumberOfRowsInMatrixForElement: (GSXib5Element*)element +{ + id object = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSMatrix class]]) + { + NSArray *cells = [self decodeObjectForKey: @"cells"]; + NSArray *column = [cells objectAtIndex: 0]; + object = [NSNumber numberWithUnsignedInteger: [column count]]; + } + + return object; +} + +- (id)decodeFormCellsForElement: (GSXib5Element*)element +{ + id object = [NSMutableArray array]; + NSArray *columns = [self decodeObjectForKey: @"cells"]; + NSInteger numCols = [columns count]; + NSInteger numRows = [[columns objectAtIndex: 0] count]; + NSInteger row = 0; + NSInteger col = 0; + + // NSForm's cells now encoded as two dimensional array but we need + // the cells in a single array by column/row... + for (row = 0; row < numRows; ++row) + { + for (col = 0; col < [columns count]; ++col) + { + // Add the row/column object... + [object addObject: [[columns objectAtIndex: col] objectAtIndex: row]]; + } + } + + return object; +} + +- (id)decodeNameForElement: (GSXib5Element*)element +{ + id object = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSMenu class]]) + { + object = [element attributeForKey: @"systemMenu"]; + + if (([@"main" isEqualToString: object]) && + ([@"MainMenu" isEqualToString: [element attributeForKey: @"userLabel"]])) + object = @"_NSMainMenu"; + } + else if ([element attributeForKey: @"name"]) + { + object = [self decodeObjectForKey: @"name"]; + } + else if ([class isSubclassOfClass: [NSFont class]] == NO) + { + NSWarnMLog(@"no name object for class: %@", [element attributeForKey: @"class"]); + } + + return object; +} + +- (id)decodeSliderCellTickMarkPositionForElement: (GSXib5Element*)element +{ + NSUInteger value = NSTickMarkBelow; // Default... + NSString *tickMarkPosition = [element attributeForKey: @"tickMarkPosition"]; + + if ([@"below" isEqualToString: tickMarkPosition]) + value = NSTickMarkBelow; + else if ([@"above" isEqualToString: tickMarkPosition]) + value = NSTickMarkAbove; + else if ([@"leading" isEqualToString: tickMarkPosition]) + value = NSTickMarkLeft; + else if ([@"trailing" isEqualToString: tickMarkPosition]) + value = NSTickMarkRight; + else if (tickMarkPosition) + NSWarnMLog(@"unknown slider cell tick mark position: %@", tickMarkPosition); + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id)decodeSliderCellTypeForElement: (GSXib5Element*)element +{ + NSUInteger value = NSCircularSlider; // Default... + NSString *sliderType = [element attributeForKey: @"sliderType"]; + + if ([@"linear" isEqualToString: sliderType]) + value = NSLinearSlider; + else if ([@"circular" isEqualToString: sliderType]) + value = NSCircularSlider; + else if (sliderType) + NSWarnMLog(@"unknown slider cell type: %@", sliderType); + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id)decodeCellsForElement: (GSXib5Element*)element +{ + id object = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSMatrix class]]) + object = [self decodeFormCellsForElement: element]; + else + object = [self decodeObjectForKey: @"cells"]; + + return object; +} + +- (id)decodePullsDownForElement: (GSXib5Element*)element +{ + id object = nil; + NSString *pullsDown = [element attributeForKey: @"pullsDown"]; + BOOL value = YES; + + if (pullsDown) + value = [pullsDown boolValue]; + + return [NSNumber numberWithBool: value]; +} + +- (id)decodeAutoenablesItemsForElement: (GSXib5Element*)element +{ + id object = nil; + NSString *autoenablesItems = [element attributeForKey: @"autoenablesItems"]; + BOOL value = YES; + + if (autoenablesItems) + value = [autoenablesItems boolValue]; + + return [NSNumber numberWithBool: value]; +} + +- (id)decodeAltersStateForElement: (GSXib5Element*)element +{ + NSString *altersState = [element attributeForKey: @"altersStateOfSelectedItem"]; + BOOL value = YES; + + if (altersState) + value = [altersState boolValue]; + + return [NSNumber numberWithBool: value]; +} + +- (id)decodeMenuItemForElement: (GSXib5Element*)element +{ + NSString *itemID = [element attributeForKey: @"selectedItem"]; + GSXib5Element *itemElem = [objects objectForKey: itemID]; + id object = [self objectForXib: itemElem]; + + return object; +} + +- (id)decodeSelectedIndexForElement: (GSXib5Element*)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)decodeTitleCellForElement: (GSXib5Element*)element +{ + id object = nil; + NSString *title = [element attributeForKey: @"title"]; + + if (title) + { + NSFont *font = [self decodeObjectForKey: @"titleFont"]; + + // IF no font... + if (font == nil) // default to system-11... + font = [NSFont systemFontOfSize: 11]; + + object = [[NSCell alloc] initTextCell: title]; + [object setAlignment: NSCenterTextAlignment]; + [object setBordered: NO]; + [object setEditable: NO]; + [object setFont: font]; + } + + return object; +} + +- (id)decodeBorderTypeForElement: (GSXib5Element*)element +{ + NSString *borderType = [element attributeForKey: @"borderType"]; + NSBorderType value = NSGrooveBorder; // Cocoa default... + + if (borderType) + { + if ([@"bezel" isEqualToString: borderType]) + value = NSBezelBorder; + else if ([@"line" isEqualToString: borderType]) + value = NSLineBorder; + else if ([@"none" isEqualToString: borderType]) + value = NSNoBorder; + else + NSWarnMLog(@"unknown border type: %@", borderType); + } + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id)decodeBoxTypeForElement: (GSXib5Element*)element +{ + NSString *boxType = [element attributeForKey: @"boxType"]; + NSBoxType value = NSBoxPrimary; // Cocoa default... + + if (boxType) + { + if ([@"secondary" isEqualToString: boxType]) + value = NSBoxSecondary; + else if ([@"oldStyle" isEqualToString: boxType]) + value = NSBoxOldStyle; + else if ([@"custom" isEqualToString: boxType]) + value = NSBoxCustom; + else if ([@"primary" isEqualToString: boxType]) + value = NSBoxPrimary; + else + NSWarnMLog(@"unknown box type: %@", boxType); + } + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id)decodeTitlePositionForElement: (GSXib5Element*)element +{ + NSString *titlePosition = [element attributeForKey: @"titlePosition"]; + NSTitlePosition value = NSAtTop; // Cocoa default... + + if (titlePosition) + { + if ([@"noTitle" isEqualToString: titlePosition]) + value = NSNoTitle; + else if ([@"aboveTop" isEqualToString: titlePosition]) + value = NSAboveTop; + else if ([@"belowTop" isEqualToString: titlePosition]) + value = NSBelowTop; + else if ([@"aboveBottom" isEqualToString: titlePosition]) + value = NSAboveTop; + else if ([@"atBottom" isEqualToString: titlePosition]) + value = NSAtBottom; + else if ([@"belowBottom" isEqualToString: titlePosition]) + value = NSBelowBottom; + else if ([@"atTop" isEqualToString: titlePosition]) + value = NSAtTop; + else + NSWarnMLog(@"unknown title position: %@", titlePosition); + } + + return [NSNumber numberWithUnsignedInteger: value]; +} + +- (id)decodeFontSizeForElement: (GSXib5Element*)element +{ + NSDictionary *attributes = [element attributes]; + CGFloat size = [[attributes objectForKey: @"size"] floatValue]; + + if (size == 0) + { + NSString *metaFont = [[attributes objectForKey: @"metaFont"] lowercaseString]; + + // Default the value... + size = 13; + + if ([@"system" isEqualToString: metaFont]) + size = 13; + else if ([metaFont containsString: @"small"]) + size = 11; + else if ([metaFont containsString: @"mini"]) + size = 9; + else if ([metaFont containsString: @"medium"]) + size = 13; + else if ([metaFont containsString: @"menu"]) + size = 13; + else if (metaFont) + NSWarnMLog(@"unknown meta font value: %@", metaFont); + } + + return [NSNumber numberWithFloat: size]; +} + +- (id)decodeFontTypeForElement: (GSXib5Element*)element +{ + static NSArray *MetaFontSystemNames = nil; + if (MetaFontSystemNames == nil) + { + MetaFontSystemNames = @[ @"system", @"message" ]; + RETAIN(MetaFontSystemNames); + } + + NSDictionary *attributes = [element attributes]; + NSString *metaFont = [[attributes objectForKey: @"metaFont"] lowercaseString]; + BOOL isSystem = [MetaFontSystemNames containsObject: metaFont]; + NSWarnMLog(@"isSystemFont %ld", (long)isSystem); + return [NSNumber numberWithBool: isSystem]; +} + +- (id) decodeDividerStyleForElement: (GSXib5Element*)element +{ + NSString *dividerStyle = [element attributeForKey: @"dividerStyle"]; + NSSplitViewDividerStyle style = NSSplitViewDividerStyleThick; // Default... + + if (dividerStyle) + { + if ([@"thin" isEqualToString: dividerStyle]) + style = NSSplitViewDividerStyleThin; + else if ([@"paneSplitter" isEqualToString: dividerStyle]) + style = NSSplitViewDividerStylePaneSplitter; +#if 0 // DEFAULT - see above... + else if ([@"thick" isEqualToString: dividerStyle]) + style = NSSplitViewDividerStyleThick; +#endif + else + NSWarnMLog(@"unknown divider style: %@", dividerStyle); + } + + return [NSNumber numberWithInteger: dividerStyle]; +} + +- (id) decodeProgressIndicatorFlagsForElement: (GSXib5Element*)element +{ + unsigned int flags = 0; + NSString *bezeled = [element attributeForKey: @"bezeled"]; + NSString *style = [element attributeForKey: @"style"]; + NSString *controlSize = [element attributeForKey: @"controlSize"]; + NSString *indeterminate = [element attributeForKey: @"indeterminate"]; + NSString *displayedWhenStopped = [element attributeForKey: @"displayedWhenStopped"]; + + if ([indeterminate boolValue]) + flags |= 0x02; + if ([@"small" isEqualToString: controlSize]) + flags |= 0x100; + if ([@"spinning" isEqualToString: style]) + flags |= 0x1000; + if ((displayedWhenStopped == nil) || ([displayedWhenStopped boolValue])) + flags |= 0x2000; + + return [NSNumber numberWithInt: flags]; +} + +- (id) decodeTextViewFlagsForElement: (GSXib5Element*)element +{ + unsigned int flags = 0; + NSString *allowsUndo = [element attributeForKey: @"allowsUndo"]; + NSString *importsGraphics = [element attributeForKey: @"importsGraphics"]; + NSString *editable = [element attributeForKey: @"editable"]; + NSString *selectable = [element attributeForKey: @"selectable"]; + NSString *fieldEditor = [element attributeForKey: @"fieldEditor"]; + NSString *findStyle = [element attributeForKey: @"findStyle"]; + NSString *richText = [element attributeForKey: @"richText"]; + NSString *smartInsertDelete = [element attributeForKey: @"smartInsertDelete"]; + NSString *usesFontPanel = [element attributeForKey: @"usesFontPanel"]; + NSString *allowsNonContiguousLayout = [element attributeForKey: @"allowsNonContiguousLayout"]; + NSString *spellingCorrection = [element attributeForKey: @"spellingCorrection"]; + NSString *continuousSpellChecking = [element attributeForKey: @"continuousSpellChecking"]; + NSString *usesRuler = [element attributeForKey: @"usesRuler"]; + NSString *drawsBackground = [element attributeForKey: @"drawsBackground"]; + +#if 0 + // FIXME: if and when these are added to NSTextView... + NSString *allowsImageEditing = [element attributeForKey: @"allowsImageEditing"]; + NSString *allowsDocumentBackgroundColorChange = [element attributeForKey: @"allowsDocumentBackgroundColorChange"]; +#endif + + if ((selectable == nil) || ([selectable boolValue])) + flags |= 0x01; + if ((editable == nil) || ([editable boolValue])) + flags |= 0x02; + if ((richText == nil) || ([richText boolValue])) + flags |= 0x04; + if ([importsGraphics boolValue]) + flags |= 0x08; + if ([fieldEditor boolValue]) + flags |= 0x10; + if ([usesFontPanel boolValue]) + flags |= 0x20; + if ([usesRuler boolValue]) + flags |= 0x40; + if ([continuousSpellChecking boolValue]) + flags |= 0x80; + if ([usesRuler boolValue]) + flags |= 0x100; + if ([smartInsertDelete boolValue]) + flags |= 0x200; + if ([allowsUndo boolValue]) + flags |= 0x400; + if ((drawsBackground == nil) || ([drawsBackground boolValue])) + flags |= 0x800; + if (findStyle) //([@"panel" isEqualToString: findStyle]) + flags |= 0x2000; + +#if 0 + // FIXME: when added to NSTextView... + if ([allowsImageEditing boolValue]) + flags |= 0x00; + if ([allowsDocumentBackgroundColorChange boolValue]) + flags |= 0x00; +#endif + + return [NSNumber numberWithUnsignedInt: flags]; +} + +- (id) decodeSharedDataForElement: (GSXib5Element*)element +{ + id object = [[NSClassFromString(@"NSTextViewSharedData") alloc] initWithCoder: self]; + + return AUTORELEASE(object); +} + +- (id) decodeColorSpaceForElement: (GSXib5Element*)element +{ + // + // + // + // + // + 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 (customSpace) + { + NSWarnMLog(@"unknown custom color space: %@", customSpace); + } + } + else + { + NSWarnMLog(@"unknown color space: %@", colorSpace); + } + + return [NSNumber numberWithUnsignedInteger: value]; + } + + return nil; +} + +- (id) decodeColorCYMKForElement: (GSXib5Element*)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: (GSXib5Element*)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: (GSXib5Element*)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: (GSXib5Element*)element +{ + id object = nil; + + // Return value... + object = [NSColor whiteColor]; + + return object; +} + +- (id) decodeScrollerFlagsForElement: (GSXib5Element*)element +{ + NSUInteger mask = NSBezelBorder; // Default... + NSDictionary *attributes = [element attributes]; + NSString *borderType = [attributes objectForKey: @"borderType"]; + NSString *hasHorizontalScroller = [attributes objectForKey: @"hasHorizontalScroller"]; + NSString *hasVerticalScroller = [attributes objectForKey: @"hasVerticalScroller"]; + NSString *autohidesScrollers = [attributes objectForKey: @"autohidesScrollers"]; + + // borderType - do this one first to avoid or'ing... + 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 if (borderType) + { + NSWarnMLog(@"unknown border type: %@", borderType); + } + + if (hasHorizontalScroller) + mask |= ([hasHorizontalScroller boolValue] ? (1 << 4) : 0); + else // otherwise the default is 'has'... + mask |= (1 << 4); + + if (hasVerticalScroller) + mask |= ([hasVerticalScroller boolValue] ? (1 << 5) : 0); + else // otherwise the default is 'has'... + mask |= (1 << 5); + + if (autohidesScrollers) + mask |= ([autohidesScrollers boolValue] ? (1 << 9) : 0); + else // otherwise the default is 'has'... + mask |= (1 << 9); + + // Return value... + return [NSNumber numberWithUnsignedInt: mask]; +} + +- (id) decodeScrollViewFlagsForElement: (GSXib5Element*)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 if (borderType) + { + 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 ([attributes objectForKey: @"autohidesScrollers"] == nil) + mask |= (1 << 9); + else + mask |= ([[attributes objectForKey: @"autohidesScrollers"] boolValue] ? (1 << 9) : 0); + + // Return value... + return [NSNumber numberWithUnsignedInt: mask]; +} + +- (id) decodeScrollViewHeaderClipViewForElement: (GSXib5Element*)element +{ + Class class = NSClassFromString([element attributeForKey: @"class"]); + id object = nil; + + NSTableHeaderView *headerView = [self decodeObjectForKey: @"headerView"]; + object = [[NSClipView alloc] initWithFrame: [headerView frame]]; +#if 0 + [object setAutoresizesSubviews: YES]; + [object setAutoresizingMask: NSViewWidthSizable | NSViewMaxYMargin]; + [object setDocumentView: headerView]; +#endif + + return object; +} + +- (id) decodeScrollClassFlagsForElement: (GSXib5Element*)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: (GSXib5Element*)element +{ + typedef union _GSTableViewFlagsUnion + { + GSTableViewFlags flags; + uint32_t value; + } GSTableViewFlagsUnion; + + id object = nil; + GSTableViewFlagsUnion mask = { 0 }; + NSDictionary *attributes = [element attributes]; + NSDictionary *gridStyleMask = [[element elementForKey: @"gridStyleMask"] attributes]; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"gridStyleMask: %@", gridStyleMask); +#endif + + mask.flags.columnOrdering = [[attributes objectForKey: @"columnReordering"] boolValue]; + mask.flags.columnResizing = [[attributes objectForKey: @"columnResizing"] boolValue]; + mask.flags.drawsGrid = (gridStyleMask != nil); + mask.flags.emptySelection = YES; // check if present - see below... + mask.flags.multipleSelection = [[attributes objectForKey: @"multipleSelection"] boolValue]; + mask.flags.columnSelection = [[attributes objectForKey: @"columnSelection"] boolValue]; + mask.flags.columnAutosave = [[attributes objectForKey: @"autosaveColumns"] boolValue]; + + if ([attributes objectForKey: @"emptySelection"]) + mask.flags.emptySelection = [[attributes objectForKey: @"emptySelection"] boolValue]; + + // Unknown: typeSelect, + + return [NSNumber numberWithUnsignedInteger: mask.value]; +} + +- (id) decodeTabViewFlagsForElement: (GSXib5Element*)element +{ + id object = nil; + GSTabViewTypeFlagsUnion mask = { 0 }; + NSDictionary *attributes = [element attributes]; + NSString *type = [attributes objectForKey: @"type"]; + NSString *controlSize = [attributes objectForKey: @"controlSize"]; + NSString *controlTint = [attributes objectForKey: @"controlTint"]; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"attributes: %@", attributes); +#endif + + // 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: (GSXib5Element*)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) decodeClipViewFlagsForElement: (GSXib5Element*)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 << 2); + else + mask |= ([[attributes objectForKey: @"copiesOnScroll"] boolValue] ? (1 << 2) : 0); + + // drawsBackground - defaults to ON... + if ([attributes objectForKey: @"drawsBackground"] == nil) + mask |= (1 << 4); + else + mask |= ([[attributes objectForKey: @"drawsBackground"] boolValue] ? (1 << 4) : 0); + + + // Return value... + object = [NSNumber numberWithUnsignedInt: mask]; + } + + return object; +} + +- (id) decodeViewFlagsForElement: (GSXib5Element*)element +{ + Class class = NSClassFromString([element attributeForKey: @"class"]); + id object = nil; + + if ([class isSubclassOfClass: [NSView class]] == NO) + { + NSWarnMLog(@"called for a class that is NOT a sub-class of NSView - class: %@", NSStringFromClass(class)); + } + else + { + typedef union _GSvFlagsUnion + { + GSvFlags flags; + uint32_t value; + } GSvFlagsUnion; + + GSvFlagsUnion mask = { 0 }; + NSDictionary *attributes = [element attributes]; + GSXib5Element *autoresizingMask = [element elementForKey: @"autoresizingMask"]; + + mask.flags.autoresizingMask = [[self decodeAutoresizingMaskForElement: autoresizingMask] unsignedIntegerValue]; + mask.flags.isHidden = [[attributes objectForKey: @"hidden"] boolValue]; + mask.flags.autoresizesSubviews = YES; + + if ([attributes objectForKey: @"autoresizesSubviews"]) + mask.flags.autoresizesSubviews = [[attributes objectForKey: @"autoresizesSubviews"] boolValue]; + + // Return value... + object = [NSNumber numberWithUnsignedInt: mask.value]; + } + + return object; +} + +- (id) decodeCellContentsForElement: (GSXib5Element*)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 + { + // 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]; + } +#endif + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"object: %@", object); +#endif + } + + return object; +} + +- (id) decodeCellAlternateContentsForElement: (GSXib5Element*)element +{ + Class class = NSClassFromString([element attributeForKey: @"class"]); + id object = @""; + + if ([class isSubclassOfClass: [NSCell class]]) + { + if ([element attributeForKey: @"title"]) + { + object = [element attributeForKey: @"title"]; + } + else if ([element attributeForKey: @"image"]) + { + object = [NSImage imageNamed: [element attributeForKey: @"image"]]; + } +#if defined(DEBUG_XIB5) + NSWarnMLog(@"object: %@", object); +#endif + } + + return object; +} + +- (unsigned int) decodeLineBreakModeForAttributes: (NSDictionary*)attributes +{ + unsigned int value = 0; + NSString *lineBreakMode = [attributes objectForKey: @"lineBreakMode"]; + + value = NSLineBreakByWordWrapping; + if ([@"clipping" isEqualToString: lineBreakMode]) + value = NSLineBreakByClipping; + else if ([@"charWrapping" isEqualToString: lineBreakMode]) + value = NSLineBreakByCharWrapping; + else if ([@"wordWrapping" isEqualToString: lineBreakMode]) + value = NSLineBreakByWordWrapping; + else if ([@"truncatingHead" isEqualToString: lineBreakMode]) + value = NSLineBreakByTruncatingHead; + else if ([@"truncatingMiddle" isEqualToString: lineBreakMode]) + value = NSLineBreakByTruncatingMiddle; + else if ([@"truncatingTail" isEqualToString: lineBreakMode]) + value = NSLineBreakByTruncatingTail; +#if 0 + else + NSWarnMLog(@"unknown line break mode: %@", lineBreakMode); +#endif + + return value; +} + +- (id) decodeCellFlags1ForElement: (GSXib5Element*)element +{ + NSNumber *value = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSCell class]]) + { + GSCellFlagsUnion mask = { 0 }; + NSDictionary *attributes = [element attributes]; + NSString *title = [attributes objectForKey: @"title"]; + NSString *imageName = [attributes objectForKey: @"image"]; + NSString *lineBreakMode = [attributes objectForKey: @"lineBreakMode"]; + NSString *focusRingType = [attributes objectForKey: @"focusRingType"]; + NSString *bezelStyle = [attributes objectForKey: @"bezelStyle"]; + NSString *borderStyle = [attributes objectForKey: @"borderStyle"]; +#if defined(DEBUG_XIB5) + NSWarnMLog(@"attributes: %@", attributes); +#endif + + 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 decodeLineBreakModeForAttributes: attributes]; + mask.flags.truncateLastLine = [[attributes objectForKey: @"truncatesLastVisibleLine"] boolValue]; + mask.flags.continuous = [[attributes objectForKey: @"continuous"] boolValue]; + mask.flags.singleLineMode = [[attributes objectForKey: @"usesSingleLineMode"] boolValue]; + + // FIXME: these are unknowns for now... + mask.flags.actOnMouseDown = NO; + mask.flags.isLeaf = NO; + mask.flags.invalidObjectValue = NO; + mask.flags.invalidFont = NO; + mask.flags.weakTargetHelperFlag = NO; + mask.flags.allowsAppearanceEffects = NO; + mask.flags.actOnMouseDragged = NO; + mask.flags.isLoaded = NO; + mask.flags.dontActOnMouseUp = NO; + mask.flags.isWhite = NO; + mask.flags.useUserKeyEquivalent = NO; + mask.flags.showsFirstResponder = NO; + +#if 0 + if ((title == nil) && (imageName == nil)) + mask.flags.type = NSNullCellType; + else if (title == nil) + mask.flags.type = NSImageCellType; + else + mask.flags.type = NSTextCellType; +#else + if (imageName) + mask.flags.type = NSImageCellType; + else + mask.flags.type = NSTextCellType; +#endif + + 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: (GSXib5Element*)element +{ + NSNumber *value = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSCell class]]) + { + GSCellFlags2Union mask = { 0 }; + NSDictionary *attributes = [element attributes]; + NSString *type = [attributes objectForKey: @"type"]; + NSString *alignment = [attributes objectForKey: @"alignment"]; + NSString *controlSize = [attributes objectForKey: @"controlSize"]; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"attributes: %@", attributes); +#endif + + mask.flags.allowsEditingTextAttributes = 0; + mask.flags.importsGraphics = 0; + mask.flags.lineBreakMode = [self decodeLineBreakModeForAttributes: attributes]; + 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) decodeButtonFlags1ForElement: (GSXib5Element*)element +{ + NSNumber *value = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSButtonCell class]]) + { + typedef union _GSButtonCellFlagsUnion + { + GSButtonCellFlags flags; + uint32_t value; + } GSButtonCellFlagsUnion; + + 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]; + } +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %@", value); +#endif + + return value; +} + +- (id) decodeButtonFlags2ForElement: (GSXib5Element*)element +{ + NSNumber *value = nil; + Class class = NSClassFromString([element attributeForKey: @"class"]); + + if ([class isSubclassOfClass: [NSButtonCell class]]) + { + typedef union _GSButtonCellFlags2Union + { + GSButtonCellFlags2 flags; + uint32_t value; + } GSButtonCellFlags2Union; + + 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; + } + + // Return value... + value = [NSNumber numberWithUnsignedInteger: mask.value]; + } +#if defined(DEBUG_XIB5) + NSWarnMLog(@"mask: %@", value); +#endif + + return value; +} + +- (id) decodeCellNormalImageForElement: (GSXib5Element*)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"]; + } + } +#if defined(DEBUG_XIB5) + NSWarnMLog(@"object: %@", object); +#endif + } + + return object; +} + +- (id) decodeCellAlternateImageForElement: (GSXib5Element*)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"]; + } + } + } +#if defined(DEBUG_XIB5) + NSWarnMLog(@"object: %@", object); +#endif + + return object; +} + +- (id) decodeButtonStateForElement: (GSXib5Element*)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: (GSXib5Element*)topElement +{ + // Unfortunately cell classes can be overridden by their encompassing class so + // we need to check for these manually... + GSXib5Element *element = [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; +} + +#pragma mark - Overridden decoding methods from base class... +- (id) objectForXib: (GSXibElement*)element +{ + id object = [super objectForXib: element]; + + if (object == nil) + { + NSString *elementName = [element type]; + + if (([@"outlet" isEqualToString: elementName]) || + ([@"action" isEqualToString: elementName])) + { + // Use the attributes for this result... + object = [element attributes]; + + if ([element attributeForKey: @"id"]) + [decoded setObject: object forKey: [element attributeForKey: @"id"]]; + } + else 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"]]; + } +#if 0 + else if ([[[elementName substringFromIndex:[elementName length]-4] lowercaseString] isEqualToString:@"mask"]) + { + object = AUTORELEASE([[element attributes] copy]); + } +#endif + } + + return object; +} + +- (id) nibInstantiate: (id)object +{ + id theObject = object; + + // Check whether object needs to be instantiated and awaken... + if ([theObject respondsToSelector: @selector(nibInstantiate)]) + { + // If this is the file's owner see if there is a value in the context... + if ([theObject isKindOfClass: [NSCustomObject5 class]]) + { + // Cross reference the file's owner object from the context data... + if ([[(NSCustomObject5*)theObject userLabel] isEqualToString: @"File's Owner"]) + { + if ([_context objectForKey: NSNibOwner]) + { + [(NSCustomObject*)theObject setRealObject: [_context objectForKey: NSNibOwner]]; + } + } + } + + // Instantiate the real object... + theObject = [theObject nibInstantiate]; + } + + // We are going to awaken objects here for now... + if ([theObject respondsToSelector: @selector(awakeFromNib)]) + [theObject awakeFromNib]; + + return theObject; +} + +- (Ivar) getClassVariableForObject: (id)object forName: (NSString*)property +{ + const char *name = [property cString]; + Class class = object_getClass(object); + Ivar ivar = class_getInstanceVariable(class, name); + + // If not found... + if (ivar == 0) + { + // Try other permutations... + if ([property characterAtIndex: 0] == '_') + { + // Try removing the '_' prefix automatically added by Xcode... + ivar = [self getClassVariableForObject: object forName: [property substringFromIndex: 1]]; + } + } + + return ivar; +} + +- (id) decodeObjectForXib: (GSXibElement*)element + forClassName: (NSString*)classname + withID: (NSString*)objID +{ + id object = [super decodeObjectForXib: element forClassName: classname withID: objID]; + id theObject = [self nibInstantiate:object]; + + // XIB 5 now stores connections etc as part of element objects... + // NOTE: This code should follow the normal IBRecord-type processing. However, + // obejcts are no longer referenced within the action/outlets/tooltips/etc + // constructs. The connection constructs are now embedded within the object + // defined constructs so can be cross-referenced and instiated in real-time. + // We can eventually reconstruct the constructs manually to eventually follow + // the XIB loading process that was defined by the previous XIB format, but to + // expedite this code for use by Testplant I've decided to short cut that for now. + // + // Process tooltips... + if ([element attributeForKey: @"toolTip"]) + { + if ([theObject respondsToSelector: @selector(setToolTip:)]) + [theObject setToolTip: [element attributeForKey: @"toolTip"]]; + else if ([object respondsToSelector: @selector(setHeaderToolTip:)]) + [theObject setHeaderToolTip: [element attributeForKey: @"toolTip"]]; +#if defined(DEBUG_XIB5) + NSWarnMLog(@"object: %@ toolTip: %@", theObject, [element attributeForKey: @"toolTip"]); +#endif + } + + // Process actions/outlets... + if ([element elementForKey: @"connections"]) + { + NSArray *connections = [self objectForXib: [element elementForKey: @"connections"]]; + + // Process actions for object... + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"key == 'action'"]; + NSArray *actions = [connections filteredArrayUsingPredicate: predicate]; + + if ([actions count]) + { + NSDictionary *action = [actions objectAtIndex: 0]; + NSString *targetID = [action objectForKey: @"target"]; + id target = [self objectForXib: [objects objectForKey: targetID]]; + NSString *selector = [action objectForKey: @"selector"]; + + // Check whether target needs instantiation and awakening... + target = [self nibInstantiate: target]; + + [theObject setTarget: target]; + [theObject setAction: NSSelectorFromString(selector)]; + } + } + + // Process outlets for object... + { + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"key == 'outlet'"]; + NSArray *outlets = [connections filteredArrayUsingPredicate: predicate]; + NSEnumerator *iter = [outlets objectEnumerator]; + NSDictionary *outlet = nil; + + while ((outlet = [iter nextObject]) != nil) + { +#if defined(DEBUG_XIB5) + NSWarnMLog(@"processing outlet: %@", outlet); +#endif + NSString *property = [outlet objectForKey: @"property"]; + NSString *destID = [outlet objectForKey: @"destination"]; + GSXib5Element *destElem = [objects objectForKey: destID]; + id destination = [self objectForXib: destElem]; + NSString *selectorName = [NSString stringWithFormat: @"set%@%@:", + [[property substringToIndex: 1] uppercaseString], + [property substringFromIndex: 1]]; + SEL selector = NSSelectorFromString(selectorName); + + + // Check whether destination needs instantiation and awakening... + destination = [self nibInstantiate: destination]; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"source: %@ dest: %@ property: %@", theObject, destination, property); +#endif + + if (selector && [theObject respondsToSelector: selector]) + { + [theObject performSelector: selector withObject: destination]; + } + else + { + /* + * We cannot use the KVC mechanism here, as this would always retain _dst + * and it could also affect _setXXX methods and _XXX ivars that aren't + * affected by the Cocoa code. + */ + Ivar ivar = [self getClassVariableForObject: theObject forName: property]; + + if (ivar != 0) + { + // This shouldn't be needed... + RETAIN(destination); + + // Set the iVar... + object_setIvar(theObject, ivar, destination); + } + else + { + NSWarnMLog(@"class '%@' has no instance var named: %@", [theObject className], property); + } + } + } + } + } + + // Process runtime attributes for object... + if ([element elementForKey: @"userDefinedRuntimeAttributes"]) + { + GSXib5Element *ibDefinedRuntimeAttr = [element elementForKey: @"userDefinedRuntimeAttributes"]; + NSArray *runtimeAttributes = [self objectForXib: ibDefinedRuntimeAttr]; + NSEnumerator *iter = [runtimeAttributes objectEnumerator]; + IBUserDefinedRuntimeAttribute5 *runtimeAttribute = nil; + + while ((runtimeAttribute = [iter nextObject]) != nil) + { +#if defined(DEBUG_XIB5) + NSWarnMLog(@"processing object (%@) runtime attr: %@", object, runtimeAttribute); +#endif + [theObject setValue: [runtimeAttribute value] forKeyPath: [runtimeAttribute keyPath]]; + } + } + + return object; +} + +- (id)decodeObjectForKey:(NSString *)key +{ +#if defined(DEBUG_XIB5) + NSWarnMLog(@"STARTING: key: %@ currentElement: %@ id: %@", key, [currentElement type], [currentElement attributeForKey: @"id"]); +#endif + id object = [super decodeObjectForKey:key]; + + // If not object 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 (([@"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; + +#if defined(DEBUG_XIB5) + NSWarnMLog(@"title: %@ bFlags: %u", [object title], bFlags); +#endif + + 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 + object = [self decodeObjectForKey: @"font"]; + } + 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 ([key hasPrefix:@"NS"]) + { + // Try a key minus a (potential) NS prefix... + NSString *newKey = [key stringByDeletingPrefix: @"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + object = [self decodeObjectForKey:newKey]; + } + 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]; + GSXib5Element *element = [objects objectForKey:idString]; + object = [self objectForXib: element]; + } + else + { + // New xib stores values as attributes... + object = [currentElement attributeForKey: key]; + } + } + +#if 0 + if (object == nil) + NSWarnMLog(@"no object for key: %@", key); +#endif +#if defined(DEBUG_XIB5) + NSWarnMLog(@"DONE: key: %@ currentElement: %@ id: %@", key, [currentElement type], [currentElement attributeForKey: @"id"]); +#endif + + return object; +} + +- (BOOL)decodeBoolForKey:(NSString *)key +{ + BOOL flag = NO; + + if ([super containsValueForKey:key]) + { + flag = [super decodeBoolForKey:key]; + } + 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 ([currentElement attributeForKey: key]) + { + flag = [[currentElement attributeForKey: key] boolValue]; + } + else if ([key hasPrefix:@"NS"]) + { + NSString *newKey = [key stringByDeletingPrefix:@"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + flag = [self decodeBoolForKey:newKey]; + } +#if 0 + else + { + NSWarnMLog(@"no BOOL for key: %@", key); + } +#endif + + return flag; +} + +- (double)decodeDoubleForKey:(NSString *)key +{ + double value = 0; + + if ([self containsValueForKey:key]) + { + value = [super decodeDoubleForKey:key]; + } + else if ([XmlKeyMapTable objectForKey: key]) + { + value = [self decodeDoubleForKey: [XmlKeyMapTable objectForKey: key]]; + } + else if ([XmlKeyToDecoderSelectorMap objectForKey: key]) + { + SEL selector = NSSelectorFromString([XmlKeyToDecoderSelectorMap objectForKey: key]); + value = [[self performSelector: selector withObject: currentElement] doubleValue]; + } + else if ([currentElement attributeForKey: key]) + { + value = [[currentElement attributeForKey: key] doubleValue]; + } + else if ([key hasPrefix:@"NS"]) + { + NSString *newKey = [key stringByDeletingPrefix:@"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + value = [self decodeDoubleForKey:newKey]; + } + else + { + NSWarnMLog(@"no DOUBLE for key: %@", key); + } + + return value; +} + +- (float)decodeFloatForKey:(NSString *)key +{ + return (float)[self decodeDoubleForKey: key]; +} + +- (int)decodeIntForKey:(NSString *)key +{ + int value = 0; + + if ([self containsValueForKey:key]) + { + value = [super decodeIntForKey:key]; + } + else if ([XmlKeyMapTable objectForKey: key]) + { + value = [self decodeIntForKey: [XmlKeyMapTable objectForKey: key]]; + } + else if ([XmlKeyToDecoderSelectorMap objectForKey: key]) + { + SEL selector = NSSelectorFromString([XmlKeyToDecoderSelectorMap objectForKey: key]); + value = [[self performSelector: selector withObject: currentElement] intValue]; + } + else if ([currentElement attributeForKey: key]) + { + value = [[currentElement attributeForKey: key] integerValue]; + } + else if ([key hasPrefix:@"NS"]) + { + NSString *newKey = [key stringByDeletingPrefix:@"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + value = [self decodeIntegerForKey:newKey]; + } + else + { + NSWarnMLog(@"no INT for key: %@", key); + } + + return value; +} + +- (NSInteger)decodeIntegerForKey:(NSString *)key +{ + NSInteger value = 0; + + if ([self containsValueForKey:key]) + { + value = [super decodeIntegerForKey:key]; + } + else if ([XmlKeyToDecoderSelectorMap objectForKey: key]) + { + SEL selector = NSSelectorFromString([XmlKeyToDecoderSelectorMap objectForKey: key]); + value = [[self performSelector: selector withObject: currentElement] integerValue]; + } + else if ([currentElement attributeForKey: key]) + { + value = [[currentElement attributeForKey: key] integerValue]; + } + else if ([key hasPrefix:@"NS"]) + { + NSString *newKey = [key stringByDeletingPrefix:@"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + value = [self decodeIntegerForKey:newKey]; + } + else + { + NSWarnMLog(@"no INTEGER for key: %@", key); + } + + return value; +} + +- (NSPoint) decodePointForKey:(NSString *)key +{ + NSPoint point = NSZeroPoint; + + // If the request element exists... + if ([currentElement elementForKey: key]) + { + GSXib5Element *element = [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 = [key stringByDeletingPrefix: @"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString: [newKey substringFromIndex:1]]; + 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]) + { + GSXib5Element *element = [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 hasPrefix:@"NS"]) + { + NSString *newKey = [key stringByDeletingPrefix: @"NS"]; + NSString *prefix = [[newKey substringToIndex:1] lowercaseString]; + newKey = [prefix stringByAppendingString: [newKey substringFromIndex:1]]; + 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 = [key stringByDeletingPrefix: @"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString: [newKey substringFromIndex:1]]; + frame = [self decodeRectForKey: newKey]; + } + else + { + NSWarnMLog(@"no RECT for key: %@", key); + } + + return frame; +} + +- (NSRange) decodeRangeForKey: (NSString*)key +{ + NSRange range = NSMakeRange(0, 0); + GSXib5Element *element = [currentElement elementForKey: key]; + + // If the request element exists... + if (element) + { + range.location = [[element attributeForKey: @"location"] integerValue]; + range.length = [[element attributeForKey: @"length"] integerValue]; + } + else + { + NSWarnMLog(@"no RANGE for key: %@", key); + } + + return range; +} + +- (BOOL)containsValueForKey:(NSString *)key +{ + BOOL hasValue = [super containsValueForKey:key]; + + // Check attributes (for XIB 5 and above) for additional values... + if (hasValue == NO) + { + hasValue = [currentElement attributeForKey: key] != nil; + } + + // 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 ([XmlKeysDefined containsObject: key]) + { + // These are arbitrarily defined through hard-coding... + hasValue = YES; + } + else if ([key hasPrefix:@"NS"]) + { + // Try a key minus a (potential) NS prefix... + NSString *newKey = [key stringByDeletingPrefix:@"NS"]; + newKey = [[[newKey substringToIndex:1] lowercaseString] stringByAppendingString:[newKey substringFromIndex:1]]; + 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"]]; + NSPredicate *predicate = [NSPredicate predicateWithFormat:@"key == 'action'"]; + NSArray *actions = [connections filteredArrayUsingPredicate: predicate]; + hasValue = ([actions count] != 0); + +#if defined(DEBUG_XIB5) + // FOR DEBUG... + if ([actions count] == 0) + { + NSWarnMLog(@"no action available for target request"); + } +#endif + } + } + } + + return hasValue; +} + +@end + +#if 0 +#pragma mark - NSObject (NSKeyedUnarchiverDelegate) Protocol... +@implementation NSObject (NSKeyedUnarchiverDelegate) +/** + */ +- (Class) unarchiver: (NSKeyedUnarchiver*)anUnarchiver +cannotDecodeObjectOfClassName: (NSString*)aName + originalClasses: (NSArray*)classNames +{ + return nil; +} +/** + */ +- (id) unarchiver: (NSKeyedUnarchiver*)anUnarchiver + didDecodeObject: (id)anObject +{ + return anObject; +} +/** + */ +- (void) unarchiverDidFinish: (NSKeyedUnarchiver*)anUnarchiver +{ +} +/** + */ +- (void) unarchiverWillFinish: (NSKeyedUnarchiver*)anUnarchiver +{ +} +/** + */ +- (void) unarchiver: (NSKeyedUnarchiver*)anUnarchiver + willReplaceObject: (id)anObject + withObject: (id)newObject +{ +} +@end + +@implementation NSObject (NSKeyedUnarchiverObjectSubstitution) ++ (Class) classForKeyedUnarchiver +{ + return self; +} +@end +#endif diff --git a/Source/GSXibElement.m b/Source/GSXibElement.m index 4d1c89e59..853336dde 100644 --- a/Source/GSXibElement.m +++ b/Source/GSXibElement.m @@ -34,11 +34,11 @@ @implementation GSXibElement -- (GSXibElement*) initWithType: (NSString*)typeName - andAttributes: (NSDictionary*)attribs +- (GSXibElement*) initWithType: (NSString*)typeName + andAttributes: (NSDictionary*)attribs { ASSIGN(type, typeName); - ASSIGN(attributes, attribs); + ASSIGN(attributes, AUTORELEASE([attribs mutableCopy])); elements = [[NSMutableDictionary alloc] init]; values = [[NSMutableArray alloc] init]; @@ -114,3 +114,11 @@ @end +@implementation GSXib5Element + +- (void) setAttribute: (id)attribute forKey: (NSString*)key +{ + [attributes setObject:attribute forKey:key]; +} + +@end \ No newline at end of file diff --git a/Source/GSXibKeyedUnarchiver.h b/Source/GSXibKeyedUnarchiver.h new file mode 100644 index 000000000..c5bf8c5cd --- /dev/null +++ b/Source/GSXibKeyedUnarchiver.h @@ -0,0 +1,52 @@ +/** GSXibKeyedUnarchiver.h + + + These are templates for use with OSX XIB 5 files. These classes are the + templates and other things which are needed for reading XIB 5 files. + + + Copyright (C) 1997, 1999 Free Software Foundation, Inc. + + File created by Marcian Lytwyn on 12/30/16 from original code by: + + Author: Gregory John Casamento + Date: 2003, 2005 + + 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 + +@class GSXibElement; + +@interface GSXibKeyedUnarchiver : NSKeyedUnarchiver +{ + NSMutableDictionary *objects; + NSMutableArray *stack; + GSXibElement *currentElement; + NSMutableDictionary *decoded; +} + +- (id) decodeObjectForXib: (GSXibElement*)element + forClassName: (NSString*)classname + withID: (NSString*)objID; +- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element; +- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element; +- (id) objectForXib: (GSXibElement*)element; +@end diff --git a/Source/GSXibKeyedUnarchiver.m b/Source/GSXibKeyedUnarchiver.m new file mode 100644 index 000000000..ad82e9929 --- /dev/null +++ b/Source/GSXibKeyedUnarchiver.m @@ -0,0 +1,1003 @@ +/** GSXibKeyedUnarchiver.m + + + These are templates for use with OSX XIB 5 files. These classes are the + templates and other things which are needed for reading XIB 5 files. + + + Copyright (C) 1997, 1999 Free Software Foundation, Inc. + + File created by Marcian Lytwyn on 12/30/16 from original code by: + + Author: Gregory John Casamento + Date: 2003, 2005 + + 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 "GSXibKeyedUnarchiver.h" +#import "GNUstepGUI/GSXibElement.h" +#import "GNUstepGUI/GSNibLoading.h" + +@implementation GSXibKeyedUnarchiver + +- (NSData *) _preProcessXib: (NSData *)data +{ + NSData *result = nil; + +#if GNUSTEP_BASE_HAVE_LIBXML + NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data + options:0 + error:NULL]; + result = data; + if (document == nil) + { + NSLog(@"%s:DOCUMENT IS NIL: %@\n", __PRETTY_FUNCTION__, document); + } + else + { + // Test to see if this is an Xcode 5 XIB... + NSArray *documentNodes = [document nodesForXPath:@"/document" + error:NULL]; + if ([documentNodes count] > 0) + { + NSLog(@"This is an XCode 5 XIB file."); + return nil; + } + else + { + NSArray *customClassNodes = [document nodesForXPath:@"//dictionary[@key=\"flattenedProperties\"]/" + @"string[contains(@key,\"CustomClassName\")]" + error:NULL]; + NSMutableDictionary *customClassDict = [NSMutableDictionary dictionary]; + if (customClassNodes) + { + NSDebugLLog(@"PREXIB", @"%s:customClassNodes: %@\n", __PRETTY_FUNCTION__, customClassNodes); + + // Replace the NSXMLNodes with a dictionary... + NSInteger index = 0; + for (index = 0; index < [customClassNodes count]; ++index) + { + id node = [customClassNodes objectAtIndex:index]; + if ([node isMemberOfClass:[NSXMLElement class]]) + { + NSString *key = [[node attributeForName:@"key"] stringValue]; + if ([key rangeOfString:@"CustomClassName"].location != NSNotFound) + { + [customClassDict setObject:[node stringValue] forKey:key]; + } + } + } + } + else + { + NSArray *flatProps = [document nodesForXPath:@"//object[@key=\"flattenedProperties\"]" error:NULL]; + if ([flatProps count] == 1) + { + NSInteger index = 0; + NSArray *xmlKeys = [[flatProps objectAtIndex:0] nodesForXPath:@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.sortedKeys\"]/*" error:NULL]; + NSArray *xmlObjs = [[flatProps objectAtIndex:0] nodesForXPath:@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.values\"]/*" error:NULL]; + if ([xmlKeys count] != [xmlObjs count]) + { + NSLog(@"%s:keys to objs count mismatch - keys: %d objs: %d\n", __PRETTY_FUNCTION__, + (int)[xmlKeys count], (int)[xmlObjs count]); + } + else + { + for (index = 0; index < [xmlKeys count]; ++index) + { + id key = [[xmlKeys objectAtIndex:index] stringValue]; + if ([key rangeOfString:@"CustomClassName"].location != NSNotFound) + { + // NSString *obj = [[xmlObjs objectAtIndex:index] stringValue]; + [customClassDict setObject:[[xmlObjs objectAtIndex:index] stringValue] forKey:key]; + } + } + } + } + } + + NSDebugLLog(@"PREXIB", @"%s:customClassDict: %@\n", __PRETTY_FUNCTION__, customClassDict); + + if ([customClassDict count] > 0) + { + NSArray *objectRecords = nil; + NSEnumerator *en = [[customClassDict allKeys] objectEnumerator]; + NSString *key = nil; + + while ((key = [en nextObject]) != nil) + { + NSString *keyValue = [key stringByReplacingOccurrencesOfString:@".CustomClassName" withString:@""]; + NSString *className = [customClassDict objectForKey:key]; + NSString *objectRecordXpath = nil; + + objectRecordXpath = [NSString stringWithFormat:@"//object[@class=\"IBObjectRecord\"]/" + @"int[@key=\"objectID\"][text()=\"%@\"]/../reference", + keyValue]; + + objectRecords = [document nodesForXPath:objectRecordXpath error:NULL]; + + if (objectRecords == nil) + { + // If that didn't work then it could be a 4.6+ XIB... + objectRecordXpath = [NSString stringWithFormat:@"//object[@class=\"IBObjectRecord\"]/" + @"string[@key=\"id\"][text()=\"%@\"]/../reference", + keyValue]; + objectRecords = [document nodesForXPath:objectRecordXpath error:NULL]; + } + + NSString *refId = nil; + if ([objectRecords count] > 0) + { + id record = nil; + NSEnumerator *oen = [objectRecords objectEnumerator]; + while ((record = [oen nextObject]) != nil) + { + if ([record isMemberOfClass:[NSXMLElement class]]) + { + if([[[record attributeForName:@"key"] stringValue] isEqualToString:@"object"]) + { + NSArray *classNodes = nil; + id classNode = nil; + NSString *refXpath = nil; + + refId = [[record attributeForName:@"ref"] stringValue]; + refXpath = [NSString stringWithFormat:@"//object[@id=\"%@\"]",refId]; + classNodes = [document nodesForXPath:refXpath + error:NULL]; + if([classNodes count] > 0) + { + id classAttr = nil; + Class cls = NSClassFromString(className); + + classNode = [classNodes objectAtIndex:0]; + classAttr = [classNode attributeForName:@"class"]; + [classAttr setStringValue:className]; + + if (cls != nil) + { + if ([cls respondsToSelector:@selector(cellClass)]) + { + NSArray *cellNodes = nil; + id cellNode = nil; + id cellClass = [cls cellClass]; + NSString *cellXpath = [NSString stringWithFormat:@"//object[@id=\"%@\"]/object[@key=\"NSCell\"]",refId]; + cellNodes = [document nodesForXPath:cellXpath + error:NULL]; + if ([cellNodes count] > 0) + { + NSString *cellClassString = NSStringFromClass(cellClass); + id cellAttr = nil; + cellNode = [cellNodes objectAtIndex:0]; + cellAttr = [cellNode attributeForName:@"class"]; + [cellAttr setStringValue:cellClassString]; + } + } + } + } + } + } + } + } + } + } + result = [document XMLData]; + RELEASE(document); + } + } +#endif + return result; +} + +- (id) initForReadingWithData: (NSData*)data +{ +#if GNUSTEP_BASE_HAVE_LIBXML + NSXMLParser *theParser; + NSData *theData = data; + + // If we are in the interface builder app, do not replace + // the existing classes with their custom subclasses. + if([NSClassSwapper isInInterfaceBuilder] == NO) + { + theData = [self _preProcessXib: data]; + } + + if (theData == nil) + { + return nil; + } + + objects = [[NSMutableDictionary alloc] init]; + stack = [[NSMutableArray alloc] init]; + decoded = [[NSMutableDictionary alloc] init]; + + theParser = [[NSXMLParser alloc] initWithData: theData]; + [theParser setDelegate: self]; + + NS_DURING + { + // Parse the XML data + [theParser parse]; + } + NS_HANDLER + { + NSLog(@"Exception occurred while parsing Xib: %@",[localException reason]); + DESTROY(self); + } + NS_ENDHANDLER + + DESTROY(theParser); +#endif + return self; +} + +- (void) dealloc +{ + DESTROY(objects); + DESTROY(stack); + DESTROY(decoded); + + [super dealloc]; +} + +- (void) parser: (NSXMLParser*)parser +foundCharacters: (NSString*)string +{ +#if defined(DEBUG_XIB) + NSWarnMLog(@"elementName: %@ string: %@", [currentElement type], string); +#endif + [currentElement setValue: string]; +} + +- (void) parser: (NSXMLParser*)parser +didStartElement: (NSString*)elementName + namespaceURI: (NSString*)namespaceURI + qualifiedName: (NSString*)qualifiedName + attributes: (NSDictionary*)attributeDict +{ + GSXibElement *element = [[GSXibElement alloc] initWithType: elementName + andAttributes: attributeDict]; + NSString *key = [attributeDict objectForKey: @"key"]; + NSString *ref = [attributeDict objectForKey: @"id"]; +#if defined(DEBUG_XIB) + NSWarnMLog(@"elementName: %@ namespaceURI: %@ qName: %@ attrs: %@", elementName, namespaceURI, qualifiedName, attributeDict); +#endif + + // FIXME: We should use proper memory management here + AUTORELEASE(element); + + if (key != nil) + { + [currentElement setElement: element forKey: key]; + } + else + { + // For Arrays + [currentElement addElement: element]; + } + if (ref != nil) + { + [objects setObject: element forKey: ref]; + } + + if (![@"archive" isEqualToString: elementName] && + ![@"data" isEqualToString: elementName]) + { + // only used for the root element + // push + [stack addObject: currentElement]; + } + + if (![@"archive" isEqualToString: elementName]) + { + currentElement = element; + } +} + +- (void) parser: (NSXMLParser*)parser + didEndElement: (NSString*)elementName + namespaceURI: (NSString*)namespaceURI + qualifiedName: (NSString*)qName +{ +#if defined(DEBUG_XIB) + NSWarnMLog(@"%s:elementName: %@ namespaceURI: %@ qName: %@", __PRETTY_FUNCTION__, + elementName, namespaceURI, qName); +#endif + if (![@"archive" isEqualToString: elementName] && + ![@"data" isEqualToString: elementName]) + { + // pop + currentElement = [stack lastObject]; + [stack removeLastObject]; + } +} + +- (id) allocObjectForClassName: (NSString*)classname +{ + Class c = nil; + id delegate = [self delegate]; + + c = [self classForClassName: classname]; + + if (c == nil) + { + c = [[self class] classForClassName: classname]; + if (c == nil) + { + c = NSClassFromString(classname); + if (c == nil) + { + c = [delegate unarchiver: self + cannotDecodeObjectOfClassName: classname + originalClasses: nil]; + if (c == nil) + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ -%@]: no class for name '%@'", + NSStringFromClass([self class]), + NSStringFromSelector(_cmd), + classname]; + } + } + } + } + + // Create instance. + return [c allocWithZone: [self zone]]; +} + +- (BOOL) replaceObject: (id)oldObj withObject: (id)newObj +{ + NSEnumerator *keyEnumerator = [decoded keyEnumerator]; + id key; + BOOL found = NO; + + while ((key = [keyEnumerator nextObject]) != nil) + { + id obj = [decoded objectForKey: key]; + if (obj == oldObj) + { + found = YES; + break; + } + } + + if (found) + { + [decoded setObject: newObj forKey: key]; + } + + return found; +} + +- (id) decodeObjectForXib: (GSXibElement*)element + forClassName: (NSString*)classname + withID: (NSString*)objID +{ + GSXibElement *last; + id o, r; + id delegate = [self delegate]; + + // Create instance. + o = [self allocObjectForClassName: classname]; + // Make sure the object stays around, even when replaced. + RETAIN(o); + if (objID != nil) + [decoded setObject: o forKey: objID]; + + // push + last = currentElement; + currentElement = element; + + r = [o initWithCoder: self]; + + // pop + currentElement = last; + + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + + r = [o awakeAfterUsingCoder: self]; + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + + if (delegate != nil) + { + r = [delegate unarchiver: self didDecodeObject: o]; + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + } + + // Balance the retain above + RELEASE(o); + + if (objID != nil) + { + NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID); + } + + return AUTORELEASE(o); +} + +/* + This method is a copy of decodeObjectForXib:forClassName:withKey: + The only difference being in the way we decode the object and the + missing context switch. + */ +- (id) decodeDictionaryForXib: (GSXibElement*)element + forClassName: (NSString*)classname + withID: (NSString*)objID +{ + id o, r; + id delegate = [self delegate]; + + // Create instance. + o = [self allocObjectForClassName: classname]; + // Make sure the object stays around, even when replaced. + RETAIN(o); + if (objID != nil) + [decoded setObject: o forKey: objID]; + + r = [o initWithDictionary: [self _decodeDictionaryOfObjectsForElement: element]]; + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + + r = [o awakeAfterUsingCoder: self]; + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + + if (delegate != nil) + { + r = [delegate unarchiver: self didDecodeObject: o]; + if (r != o) + { + [delegate unarchiver: self + willReplaceObject: o + withObject: r]; + ASSIGN(o, r); + if (objID != nil) + [decoded setObject: o forKey: objID]; + } + } + // Balance the retain above + RELEASE(o); + + if (objID != nil) + { + NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID); + } + + return AUTORELEASE(o); +} + +- (id) objectForXib: (GSXibElement*)element +{ + NSString *elementName; + NSString *objID; + + if (element == nil) + return nil; + + NSDebugLLog(@"XIB", @"decoding element %@", element); +#if defined(DEBUG_XIB) + NSWarnMLog(@"decoding element type: %@ id: %@ key: %@", [element type], [element attributeForKey: @"id"], [element attributeForKey: @"key"]); +#endif + objID = [element attributeForKey: @"id"]; + if (objID) + { + id new = [decoded objectForKey: objID]; + if (new != nil) + { + // The object was already decoded as a reference + return new; + } + } + + elementName = [element type]; + if ([@"object" isEqualToString: elementName]) + { + NSString *classname = [element attributeForKey: @"class"]; + return [self decodeObjectForXib: element + forClassName: classname + withID: objID]; + } + else if ([@"string" isEqualToString: elementName]) + { + NSString *type = [element attributeForKey: @"type"]; + id new = [element value]; + + if ([type isEqualToString: @"base64-UTF8"]) + { + NSData *d = [[NSData alloc] initWithBase64EncodedString: new + options: 0]; + new = AUTORELEASE([[NSString alloc] initWithData: d + encoding: NSUTF8StringEncoding]); + RELEASE(d); + } + + // empty strings are not nil! + if (new == nil) + new = @""; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"int" isEqualToString: elementName]) + { + id new = [NSNumber numberWithInt: [[element value] intValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"double" isEqualToString: elementName]) + { + id new = [NSNumber numberWithDouble: [[element value] doubleValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"bool" isEqualToString: elementName]) + { + id new = [NSNumber numberWithBool: [[element value] boolValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"integer" isEqualToString: elementName]) + { + NSString *value = [element attributeForKey: @"value"]; + id new = [NSNumber numberWithInteger: [value integerValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"real" isEqualToString: elementName]) + { + NSString *value = [element attributeForKey: @"value"]; + id new = [NSNumber numberWithFloat: [value floatValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"boolean" isEqualToString: elementName]) + { + NSString *value = [element attributeForKey: @"value"]; + id new = [NSNumber numberWithBool: [value boolValue]]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"point" isEqualToString: elementName]) + { + NSPoint point = [self decodePointForKey: [element attributeForKey: @"key"]]; + id new = [NSValue valueWithPoint: point]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"size" isEqualToString: elementName]) + { + NSSize size = [self decodeSizeForKey: [element attributeForKey: @"key"]]; + id new = [NSValue valueWithSize: size]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"rect" isEqualToString: elementName]) + { + NSRect rect = [self decodeRectForKey: [element attributeForKey: @"key"]]; + id new = [NSValue valueWithRect: rect]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"reference" isEqualToString: elementName]) + { + NSString *ref = [element attributeForKey: @"ref"]; + + if (ref == nil) + { + return nil; + } + else + { + id new = [decoded objectForKey: ref]; + + // FIXME: We need a marker for nil + if (new == nil) + { + //NSLog(@"Decoding reference %@", ref); + element = [objects objectForKey: ref]; + if (element != nil) + { + // Decode the real object + new = [self objectForXib: element]; + } + } + + return new; + } + } + else if ([@"nil" isEqualToString: elementName]) + { + return nil; + } + else if ([@"characters" isEqualToString: elementName]) + { + id new = [element value]; + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"bytes" isEqualToString: elementName]) + { + id new = AUTORELEASE([[NSData alloc] initWithBase64EncodedString: [element value] + options: NSDataBase64DecodingIgnoreUnknownCharacters]); + + if (objID != nil) + [decoded setObject: new forKey: objID]; + + return new; + } + else if ([@"array" isEqualToString: elementName]) + { + NSString *classname = [element attributeForKey: @"class"]; + + if (classname == nil) + { + classname = @"NSArray"; + } + return [self decodeObjectForXib: element + forClassName: classname + withID: objID]; + } + else if ([@"dictionary" isEqualToString: elementName]) + { + NSString *classname = [element attributeForKey: @"class"]; + + if (classname == nil) + { + classname = @"NSDictionary"; + } + + return [self decodeDictionaryForXib: element + forClassName: classname + withID: objID]; + } +#if 0 + else + { + NSLog(@"Unknown element type %@", elementName); + } +#endif + + return nil; +} + +- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey +{ + // FIXME: This is wrong but the only way to keep the code for + // [NSArray-initWithCoder:] working + return [self _decodeArrayOfObjectsForElement: currentElement]; +} + +- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element +{ + NSArray *values = [element values]; + int max = [values count]; + id list[max]; + int i; + + for (i = 0; i < max; i++) + { + list[i] = [self objectForXib: [values objectAtIndex: i]]; + if (list[i] == nil) + NSLog(@"No object for %@ at index %d", [values objectAtIndex: i], i); + } + + return [NSArray arrayWithObjects: list count: max]; +} + +- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element +{ + NSDictionary *elements = [element elements]; + NSEnumerator *en; + NSString *key; + NSMutableDictionary *dict; + + dict = [[NSMutableDictionary alloc] init]; + en = [elements keyEnumerator]; + while ((key = [en nextObject]) != nil) + { + id obj = [self objectForXib: [elements objectForKey: key]]; + if (obj == nil) + NSLog(@"No object for %@ at key %@", [elements objectForKey: key], key); + else + [dict setObject: obj forKey: key]; + } + + return AUTORELEASE(dict); +} + +/* + Extension method to decode the object id of an object referenced by its key. + */ +- (NSString *) decodeReferenceForKey: (NSString*)aKey +{ + GSXibElement *element = [currentElement elementForKey: aKey]; + NSString *objID; + + if (element == nil) + return nil; + + objID = [element attributeForKey: @"id"]; + if (objID) + { + return objID; + } + + objID = [element attributeForKey: @"ref"]; + if (objID) + { + return objID; + } + + return nil; +} + +- (BOOL) containsValueForKey: (NSString*)aKey +{ + GSXibElement *element = [currentElement elementForKey: aKey]; + + return (element != nil); +} + +- (id) decodeObjectForKey: (NSString*)aKey +{ + GSXibElement *element = [currentElement elementForKey: aKey]; +#if defined(DEBUG_XIB) + NSWarnMLog(@"aKey: %@ currentElement: %@ element: %@", aKey, [currentElement type], element); +#endif + + if (element == nil) + return nil; + + return [self objectForXib: element]; +} + +- (BOOL) decodeBoolForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + return [o boolValue]; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return NO; +} + +- (const uint8_t*) decodeBytesForKey: (NSString*)aKey + returnedLength: (NSUInteger*)length +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if ([o isKindOfClass: [NSData class]] == YES) + { + *length = [o length]; + return [o bytes]; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + *length = 0; + return 0; +} + +- (double) decodeDoubleForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + return [o doubleValue]; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return 0.0; +} + +- (float) decodeFloatForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + return [o floatValue]; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return 0.0; +} + +- (int) decodeIntForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + long long l = [o longLongValue]; + + return l; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return 0; +} + +- (int32_t) decodeInt32ForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + long long l = [o longLongValue]; + + return l; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return 0; +} + +- (int64_t) decodeInt64ForKey: (NSString*)aKey +{ + id o = [self decodeObjectForKey: aKey]; + + if (o != nil) + { + if (([o isKindOfClass: [NSNumber class]] == YES) || + ([o isKindOfClass: [NSString class]] == YES)) + { + long long l = [o longLongValue]; + + return l; + } + else + { + [NSException raise: NSInvalidUnarchiveOperationException + format: @"[%@ +%@]: value for key(%@) is '%@'", + NSStringFromClass([self class]), NSStringFromSelector(_cmd), + aKey, o]; + } + } + return 0; +} + +@end diff --git a/Source/GSXibLoader.m b/Source/GSXibLoader.m index bc263a386..3cb641abc 100644 --- a/Source/GSXibLoader.m +++ b/Source/GSXibLoader.m @@ -48,6 +48,7 @@ #import "GNUstepGUI/GSXibParser.h" #import "GNUstepGUI/GSXibObjectContainer.h" #import "GNUstepGUI/GSXibElement.h" +#import "GSXib5KeyedUnarchiver.h" @interface NSApplication (NibCompatibility) - (void) _setMainMenu: (NSMenu*)aMenu; @@ -72,7 +73,10 @@ - (BOOL) _isMainMenu { if (_name) - return [_name isEqualToString:@"_NSMainMenu"]; + { + BOOL isMainMenu = [_name isEqualToString:@"_NSMainMenu"]; + return isMainMenu; + } return NO; } @@ -314,36 +318,36 @@ NS_DURING { if (source != nil) - { - NSString *selName; - SEL sel; - - selName = [NSString stringWithFormat: @"set%@%@:", - [[label substringToIndex: 1] uppercaseString], - [label substringFromIndex: 1]]; - sel = NSSelectorFromString(selName); - - if (sel && [source respondsToSelector: sel]) - { - [source performSelector: sel withObject: destination]; - } - else - { - /* - * We cannot use the KVC mechanism here, as this would always retain _dst - * and it could also affect _setXXX methods and _XXX ivars that aren't - * affected by the Cocoa code. - */ - const char *name = [label cString]; - Class class = object_getClass(source); - Ivar ivar = class_getInstanceVariable(class, name); - - if (ivar != 0) - { - object_setIvar(source, ivar, destination); - } - } - } + { + NSString *selName; + SEL sel; + + selName = [NSString stringWithFormat: @"set%@%@:", + [[label substringToIndex: 1] uppercaseString], + [label substringFromIndex: 1]]; + sel = NSSelectorFromString(selName); + + if (sel && [source respondsToSelector: sel]) + { + [source performSelector: sel withObject: destination]; + } + else + { + /* + * We cannot use the KVC mechanism here, as this would always retain _dst + * and it could also affect _setXXX methods and _XXX ivars that aren't + * affected by the Cocoa code. + */ + const char *name = [label cString]; + Class class = object_getClass(source); + Ivar ivar = class_getInstanceVariable(class, name); + + if (ivar != 0) + { + object_setIvar(source, ivar, destination); + } + } + } } NS_HANDLER { @@ -909,8 +913,7 @@ return 4.0; } -- (void) awake: (NSArray *)rootObjects - inContainer: (id)objects +- (void) awake: (NSArray *)rootObjects withContext: (NSDictionary *)context { NSEnumerator *en; @@ -922,58 +925,71 @@ NSCustomObject *object; NSString *className; + if ([rootObjects count] == 0) + { + NSWarnMLog(@"no root objects!!!"); + return; + } + // Get the file's owner and NSApplication object references... object = (NSCustomObject*)[rootObjects objectAtIndex: 1]; if ([[object className] isEqualToString: @"FirstResponder"]) - { - first = [object realObject]; - } + { + first = [object realObject]; + } else - { + { NSLog(@"%s:first responder missing\n", __PRETTY_FUNCTION__); - } - + } + object = (NSCustomObject*)[rootObjects objectAtIndex: 2]; className = [object className]; if ([className isEqualToString: @"NSApplication"] || [NSClassFromString(className) isSubclassOfClass:[NSApplication class]]) - { - app = [object realObject]; - } + { + app = [object realObject]; + } else - { - NSLog(@"%s:NSApplication missing '%@'\n", __PRETTY_FUNCTION__, className); - } + { + NSLog(@"%s:NSApplication missing '%@'\n", __PRETTY_FUNCTION__, className); + } // Use the owner as first root object [(NSCustomObject*)[rootObjects objectAtIndex: 0] setRealObject: owner]; - + en = [rootObjects objectEnumerator]; while ((obj = [en nextObject]) != nil) + { + if ([obj respondsToSelector: @selector(nibInstantiate)]) { - if ([obj respondsToSelector: @selector(nibInstantiate)]) - { - obj = [obj nibInstantiate]; - } - - // IGNORE file's owner, first responder and NSApplication instances... - if ((obj != nil) && (obj != owner) && (obj != first) && (obj != app)) - { - [topLevelObjects addObject: obj]; - // All top level objects must be released by the caller to avoid - // leaking, unless they are going to be released by other nib - // objects on behalf of the owner. - RETAIN(obj); - } - - if (([obj isKindOfClass: [NSMenu class]]) && - ([obj _isMainMenu])) - { - // add the menu... - [NSApp _setMainMenu: obj]; - } + obj = [obj nibInstantiate]; } + // IGNORE file's owner, first responder and NSApplication instances... + if ((obj != nil) && (obj != owner) && (obj != first) && (obj != app)) + { + [topLevelObjects addObject: obj]; + // All top level objects must be released by the caller to avoid + // leaking, unless they are going to be released by other nib + // objects on behalf of the owner. + RETAIN(obj); + } + + if (([obj isKindOfClass: [NSMenu class]]) && + ([obj _isMainMenu])) + { + // add the menu... + [NSApp _setMainMenu: obj]; + } + } +} + +- (void) awake: (NSArray *)rootObjects + inContainer: (id)objects + withContext: (NSDictionary *)context +{ + [self awake:rootObjects withContext:context]; + // Load connections and awaken objects if ([objects respondsToSelector:@selector(nibInstantiate)]) { @@ -987,63 +1003,63 @@ { BOOL loaded = NO; NSKeyedUnarchiver *unarchiver = nil; - + NS_DURING - { - if (data != nil) - { - unarchiver = [[GSXibKeyedUnarchiver alloc] initForReadingWithData: data]; - if (unarchiver != nil) - { - NSArray *rootObjects; - IBObjectContainer *objects; + { + if (data != nil) + { + unarchiver = [[GSXibKeyedUnarchiver alloc] initForReadingWithData: data]; - NSDebugLLog(@"XIB", @"Invoking unarchiver"); - [unarchiver setObjectZone: zone]; - rootObjects = [unarchiver decodeObjectForKey: @"IBDocument.RootObjects"]; - objects = [unarchiver decodeObjectForKey: @"IBDocument.Objects"]; - NSDebugLLog(@"XIB", @"rootObjects %@", rootObjects); - [self awake: rootObjects - inContainer: objects - withContext: context]; - loaded = YES; - RELEASE(unarchiver); - } - else - { - GSXibParser *parser = [[GSXibParser alloc] initWithData: data]; - NSDictionary *result = [parser parse]; - if (result != nil) - { - NSArray *rootObjects = [result objectForKey: @"IBDocument.RootObjects"]; - GSXibObjectContainer *objects = [result objectForKey: @"IBDocument.Objects"]; - [self awake: rootObjects - inContainer: objects - withContext: context]; - } - else - { - NSLog(@"Could not instantiate Xib unarchiver/Unable to parse Xib."); - } - } - } - else - { - NSLog(@"Data passed to Xib loading method is nil."); - } - } + // If that doesn't work try the XIB 5 loader... + if (unarchiver == nil) + { + unarchiver = [[GSXib5KeyedUnarchiver alloc] initForReadingWithData: data]; + + // XIB 5 loading makes connections, etc - during loading as it's all embedded + // now within the object's XML definition...so the unarchiver needs the context + // information for at least the file's owner... + if (unarchiver) + { + [(GSXib5KeyedUnarchiver*)unarchiver setContext: context]; + } + } + + if (unarchiver != nil) + { + NSArray *rootObjects; + IBObjectContainer *objects; + + NSDebugLLog(@"XIB", @"Invoking unarchiver"); + [unarchiver setObjectZone: zone]; + rootObjects = [unarchiver decodeObjectForKey: @"IBDocument.RootObjects"]; + objects = [unarchiver decodeObjectForKey: @"IBDocument.Objects"]; + NSDebugLLog(@"XIB", @"rootObjects %@", rootObjects); + [self awake: rootObjects inContainer: objects withContext: context]; + loaded = YES; + RELEASE(unarchiver); + } + else + { + NSLog(@"Could not instantiate Xib unarchiver/Unable to parse Xib."); + } + } + else + { + NSLog(@"Data passed to Xib loading method is nil."); + } + } NS_HANDLER - { - NSLog(@"Exception occurred while loading model: %@",[localException reason]); - // TEST_RELEASE(unarchiver); - } + { + NSLog(@"Exception occurred while loading model: %@",[localException reason]); + // TEST_RELEASE(unarchiver); + } NS_ENDHANDLER - + if (loaded == NO) { NSLog(@"Failed to load Xib\n"); } - + return loaded; } @@ -1072,916 +1088,3 @@ } @end - -@implementation GSXibKeyedUnarchiver - -- (NSData *) _preProcessXib: (NSData *)data -{ - NSData *result = nil; - -#if GNUSTEP_BASE_HAVE_LIBXML - NSXMLDocument *document = [[NSXMLDocument alloc] initWithData:data - options:0 - error:NULL]; - result = data; - if (document == nil) - { - NSLog(@"%s:DOCUMENT IS NIL: %@\n", __PRETTY_FUNCTION__, document); - } - else - { - // Test to see if this is an Xcode 5 XIB... - NSArray *documentNodes = [document nodesForXPath:@"/document" - error:NULL]; - if ([documentNodes count] > 0) - { - NSLog(@"Unsupported... This is an XCode 5 XIB file."); - return nil; - } - else - { - NSArray *customClassNodes = [document nodesForXPath:@"//dictionary[@key=\"flattenedProperties\"]/" - @"string[contains(@key,\"CustomClassName\")]" - error:NULL]; - NSMutableDictionary *customClassDict = [NSMutableDictionary dictionary]; - if (customClassNodes) - { - NSDebugLLog(@"PREXIB", @"%s:customClassNodes: %@\n", __PRETTY_FUNCTION__, customClassNodes); - - // Replace the NSXMLNodes with a dictionary... - NSInteger index = 0; - for (index = 0; index < [customClassNodes count]; ++index) - { - id node = [customClassNodes objectAtIndex:index]; - if ([node isMemberOfClass:[NSXMLElement class]]) - { - NSString *key = [[node attributeForName:@"key"] stringValue]; - if ([key rangeOfString:@"CustomClassName"].location != NSNotFound) - { - [customClassDict setObject:[node stringValue] forKey:key]; - } - } - } - } - else - { - NSArray *flatProps = [document nodesForXPath:@"//object[@key=\"flattenedProperties\"]" error:NULL]; - if ([flatProps count] == 1) - { - NSInteger index = 0; - NSArray *xmlKeys = [[flatProps objectAtIndex:0] nodesForXPath:@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.sortedKeys\"]/*" error:NULL]; - NSArray *xmlObjs = [[flatProps objectAtIndex:0] nodesForXPath:@"//object[@key=\"flattenedProperties\"]/object[@key=\"dict.values\"]/*" error:NULL]; - if ([xmlKeys count] != [xmlObjs count]) - { - NSLog(@"%s:keys to objs count mismatch - keys: %d objs: %d\n", __PRETTY_FUNCTION__, - (int)[xmlKeys count], (int)[xmlObjs count]); - } - else - { - for (index = 0; index < [xmlKeys count]; ++index) - { - id key = [[xmlKeys objectAtIndex:index] stringValue]; - if ([key rangeOfString:@"CustomClassName"].location != NSNotFound) - { - // NSString *obj = [[xmlObjs objectAtIndex:index] stringValue]; - [customClassDict setObject:[[xmlObjs objectAtIndex:index] stringValue] forKey:key]; - } - } - } - } - } - - NSDebugLLog(@"PREXIB", @"%s:customClassDict: %@\n", __PRETTY_FUNCTION__, customClassDict); - - if ([customClassDict count] > 0) - { - NSArray *objectRecords = nil; - NSEnumerator *en = [[customClassDict allKeys] objectEnumerator]; - NSString *key = nil; - - while ((key = [en nextObject]) != nil) - { - NSString *keyValue = [key stringByReplacingOccurrencesOfString:@".CustomClassName" withString:@""]; - NSString *className = [customClassDict objectForKey:key]; - NSString *objectRecordXpath = nil; - - objectRecordXpath = [NSString stringWithFormat:@"//object[@class=\"IBObjectRecord\"]/" - @"int[@key=\"objectID\"][text()=\"%@\"]/../reference", - keyValue]; - - objectRecords = [document nodesForXPath:objectRecordXpath error:NULL]; - - if (objectRecords == nil) - { - // If that didn't work then it could be a 4.6+ XIB... - objectRecordXpath = [NSString stringWithFormat:@"//object[@class=\"IBObjectRecord\"]/" - @"string[@key=\"id\"][text()=\"%@\"]/../reference", - keyValue]; - objectRecords = [document nodesForXPath:objectRecordXpath error:NULL]; - } - - NSString *refId = nil; - if ([objectRecords count] > 0) - { - id record = nil; - NSEnumerator *oen = [objectRecords objectEnumerator]; - while ((record = [oen nextObject]) != nil) - { - if ([record isMemberOfClass:[NSXMLElement class]]) - { - if([[[record attributeForName:@"key"] stringValue] isEqualToString:@"object"]) - { - NSArray *classNodes = nil; - id classNode = nil; - NSString *refXpath = nil; - - refId = [[record attributeForName:@"ref"] stringValue]; - refXpath = [NSString stringWithFormat:@"//object[@id=\"%@\"]",refId]; - classNodes = [document nodesForXPath:refXpath - error:NULL]; - if([classNodes count] > 0) - { - id classAttr = nil; - Class cls = NSClassFromString(className); - - classNode = [classNodes objectAtIndex:0]; - classAttr = [classNode attributeForName:@"class"]; - [classAttr setStringValue:className]; - - if (cls != nil) - { - if ([cls respondsToSelector:@selector(cellClass)]) - { - NSArray *cellNodes = nil; - id cellNode = nil; - id cellClass = [cls cellClass]; - NSString *cellXpath = [NSString stringWithFormat:@"//object[@id=\"%@\"]/object[@key=\"NSCell\"]",refId]; - cellNodes = [document nodesForXPath:cellXpath - error:NULL]; - if ([cellNodes count] > 0) - { - NSString *cellClassString = NSStringFromClass(cellClass); - id cellAttr = nil; - cellNode = [cellNodes objectAtIndex:0]; - cellAttr = [cellNode attributeForName:@"class"]; - [cellAttr setStringValue:cellClassString]; - } - } - } - } - } - } - } - } - } - } - result = [document XMLData]; - RELEASE(document); - } - } -#endif - return result; -} - -- (id) initForReadingWithData: (NSData*)data -{ -#if GNUSTEP_BASE_HAVE_LIBXML - NSXMLParser *theParser; - NSData *theData = data; - - // If we are in the interface builder app, do not replace - // the existing classes with their custom subclasses. - if([NSClassSwapper isInInterfaceBuilder] == NO) - { - theData = [self _preProcessXib: data]; - } - - if (theData == nil) - { - return nil; - } - - objects = [[NSMutableDictionary alloc] init]; - stack = [[NSMutableArray alloc] init]; - decoded = [[NSMutableDictionary alloc] init]; - - theParser = [[NSXMLParser alloc] initWithData: theData]; - [theParser setDelegate: self]; - - NS_DURING - { - // Parse the XML data - [theParser parse]; - } - NS_HANDLER - { - NSLog(@"Exception occurred while parsing Xib: %@",[localException reason]); - DESTROY(self); - } - NS_ENDHANDLER - - DESTROY(theParser); -#endif - return self; -} - -- (void) dealloc -{ - DESTROY(objects); - DESTROY(stack); - DESTROY(decoded); - - [super dealloc]; -} - -- (void) parser: (NSXMLParser*)parser -foundCharacters: (NSString*)string -{ - [currentElement setValue: string]; -} - -- (void) parser: (NSXMLParser*)parser -didStartElement: (NSString*)elementName - namespaceURI: (NSString*)namespaceURI - qualifiedName: (NSString*)qualifiedName - attributes: (NSDictionary*)attributeDict -{ - GSXibElement *element = [[GSXibElement alloc] initWithType: elementName - andAttributes: attributeDict]; - NSString *key = [attributeDict objectForKey: @"key"]; - NSString *ref = [attributeDict objectForKey: @"id"]; - - // FIXME: We should use proper memory management here - AUTORELEASE(element); - - if (key != nil) - { - [currentElement setElement: element forKey: key]; - } - else - { - // For Arrays - [currentElement addElement: element]; - } - if (ref != nil) - { - [objects setObject: element forKey: ref]; - } - - if (![@"archive" isEqualToString: elementName] && - ![@"data" isEqualToString: elementName]) - { - // only used for the root element - // push - [stack addObject: currentElement]; - } - - if (![@"archive" isEqualToString: elementName]) - { - currentElement = element; - } -} - -- (void) parser: (NSXMLParser*)parser - didEndElement: (NSString*)elementName - namespaceURI: (NSString*)namespaceURI - qualifiedName: (NSString*)qName -{ - if (![@"archive" isEqualToString: elementName] && - ![@"data" isEqualToString: elementName]) - { - // pop - currentElement = [stack lastObject]; - [stack removeLastObject]; - } -} - -- (id) allocObjectForClassName: (NSString*)classname -{ - Class c = nil; - id delegate = [self delegate]; - - c = [self classForClassName: classname]; - - if (c == nil) - { - c = [[self class] classForClassName: classname]; - if (c == nil) - { - c = NSClassFromString(classname); - if (c == nil) - { - c = [delegate unarchiver: self - cannotDecodeObjectOfClassName: classname - originalClasses: nil]; - if (c == nil) - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ -%@]: no class for name '%@'", - NSStringFromClass([self class]), - NSStringFromSelector(_cmd), - classname]; - } - } - } - } - - // Create instance. - return [c allocWithZone: [self zone]]; - } - -- (BOOL) replaceObject: (id)oldObj withObject: (id)newObj -{ - NSEnumerator *keyEnumerator = [decoded keyEnumerator]; - id key; - BOOL found = NO; - - while ((key = [keyEnumerator nextObject]) != nil) - { - id obj = [decoded objectForKey: key]; - if (obj == oldObj) - { - found = YES; - break; - } - } - - if (found) - { - [decoded setObject: newObj forKey: key]; - } - - return found; -} - -- (id) decodeObjectForXib: (GSXibElement*)element - forClassName: (NSString*)classname - withID: (NSString*)objID -{ - GSXibElement *last; - id o, r; - id delegate = [self delegate]; - - // Create instance. - o = [self allocObjectForClassName: classname]; - // Make sure the object stays around, even when replaced. - RETAIN(o); - if (objID != nil) - [decoded setObject: o forKey: objID]; - - // push - last = currentElement; - currentElement = element; - - r = [o initWithCoder: self]; - - // pop - currentElement = last; - - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - - r = [o awakeAfterUsingCoder: self]; - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - - if (delegate != nil) - { - r = [delegate unarchiver: self didDecodeObject: o]; - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - } - - // Balance the retain above - RELEASE(o); - - if (objID != nil) - { - NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID); - } - - return AUTORELEASE(o); -} - -/* - This method is a copy of decodeObjectForXib:forClassName:withKey: - The only difference being in the way we decode the object and the - missing context switch. - */ -- (id) decodeDictionaryForXib: (GSXibElement*)element - forClassName: (NSString*)classname - withID: (NSString*)objID -{ - id o, r; - id delegate = [self delegate]; - - // Create instance. - o = [self allocObjectForClassName: classname]; - // Make sure the object stays around, even when replaced. - RETAIN(o); - if (objID != nil) - [decoded setObject: o forKey: objID]; - - r = [o initWithDictionary: [self _decodeDictionaryOfObjectsForElement: element]]; - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - - r = [o awakeAfterUsingCoder: self]; - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - - if (delegate != nil) - { - r = [delegate unarchiver: self didDecodeObject: o]; - if (r != o) - { - [delegate unarchiver: self - willReplaceObject: o - withObject: r]; - ASSIGN(o, r); - if (objID != nil) - [decoded setObject: o forKey: objID]; - } - } - // Balance the retain above - RELEASE(o); - - if (objID != nil) - { - NSDebugLLog(@"XIB", @"decoded object %@ for id %@", o, objID); - } - - return AUTORELEASE(o); -} - -- (id) objectForXib: (GSXibElement*)element -{ - NSString *elementName; - NSString *objID; - - if (element == nil) - return nil; - - NSDebugLLog(@"XIB", @"decoding element %@", element); - objID = [element attributeForKey: @"id"]; - if (objID) - { - id new = [decoded objectForKey: objID]; - if (new != nil) - { - // The object was already decoded as a reference - return new; - } - } - - elementName = [element type]; - if ([@"object" isEqualToString: elementName]) - { - NSString *classname = [element attributeForKey: @"class"]; - return [self decodeObjectForXib: element - forClassName: classname - withID: objID]; - } - else if ([@"string" isEqualToString: elementName]) - { - NSString *type = [element attributeForKey: @"type"]; - id new = [element value]; - - if ([type isEqualToString: @"base64-UTF8"]) - { - NSData *d = [[NSData alloc] initWithBase64EncodedString: new - options: 0]; - new = AUTORELEASE([[NSString alloc] initWithData: d - encoding: NSUTF8StringEncoding]); - RELEASE(d); - } - - // empty strings are not nil! - if (new == nil) - new = @""; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"int" isEqualToString: elementName]) - { - id new = [NSNumber numberWithInt: [[element value] intValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"double" isEqualToString: elementName]) - { - id new = [NSNumber numberWithDouble: [[element value] doubleValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"bool" isEqualToString: elementName]) - { - id new = [NSNumber numberWithBool: [[element value] boolValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"integer" isEqualToString: elementName]) - { - NSString *value = [element attributeForKey: @"value"]; - id new = [NSNumber numberWithInteger: [value integerValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"real" isEqualToString: elementName]) - { - NSString *value = [element attributeForKey: @"value"]; - id new = [NSNumber numberWithFloat: [value floatValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"boolean" isEqualToString: elementName]) - { - NSString *value = [element attributeForKey: @"value"]; - id new = [NSNumber numberWithBool: [value boolValue]]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"reference" isEqualToString: elementName]) - { - NSString *ref = [element attributeForKey: @"ref"]; - - if (ref == nil) - { - return nil; - } - else - { - id new = [decoded objectForKey: ref]; - - // FIXME: We need a marker for nil - if (new == nil) - { - //NSLog(@"Decoding reference %@", ref); - element = [objects objectForKey: ref]; - if (element != nil) - { - // Decode the real object - new = [self objectForXib: element]; - } - } - - return new; - } - } - else if ([@"nil" isEqualToString: elementName]) - { - return nil; - } - else if ([@"characters" isEqualToString: elementName]) - { - id new = [element value]; - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"bytes" isEqualToString: elementName]) - { - id new = AUTORELEASE([[NSData alloc] initWithBase64EncodedString: [element value] - options: NSDataBase64DecodingIgnoreUnknownCharacters]); - - if (objID != nil) - [decoded setObject: new forKey: objID]; - - return new; - } - else if ([@"array" isEqualToString: elementName]) - { - NSString *classname = [element attributeForKey: @"class"]; - - if (classname == nil) - { - classname = @"NSArray"; - } - return [self decodeObjectForXib: element - forClassName: classname - withID: objID]; - } - else if ([@"dictionary" isEqualToString: elementName]) - { - NSString *classname = [element attributeForKey: @"class"]; - - if (classname == nil) - { - classname = @"NSDictionary"; - } - - return [self decodeDictionaryForXib: element - forClassName: classname - withID: objID]; - } - else - { - NSLog(@"Unknown element type %@", elementName); - } - - return nil; -} - -- (id) _decodeArrayOfObjectsForKey: (NSString*)aKey -{ - // FIXME: This is wrong but the only way to keep the code for - // [NSArray-initWithCoder:] working - return [self _decodeArrayOfObjectsForElement: currentElement]; -} - -- (id) _decodeArrayOfObjectsForElement: (GSXibElement*)element -{ - NSArray *values = [element values]; - int max = [values count]; - id list[max]; - int i; - - for (i = 0; i < max; i++) - { - list[i] = [self objectForXib: [values objectAtIndex: i]]; - if (list[i] == nil) - NSLog(@"No object for %@ at index %d", [values objectAtIndex: i], i); - } - - return [NSArray arrayWithObjects: list count: max]; -} - -- (id) _decodeDictionaryOfObjectsForElement: (GSXibElement*)element -{ - NSDictionary *elements = [element elements]; - NSEnumerator *en; - NSString *key; - NSMutableDictionary *dict; - - dict = [[NSMutableDictionary alloc] init]; - en = [elements keyEnumerator]; - while ((key = [en nextObject]) != nil) - { - id obj = [self objectForXib: [elements objectForKey: key]]; - if (obj == nil) - NSLog(@"No object for %@ at key %@", [elements objectForKey: key], key); - else - [dict setObject: obj forKey: key]; - } - - return AUTORELEASE(dict); -} - -/* - Extension method to decode the object id of an object referenced by its key. - */ -- (NSString *) decodeReferenceForKey: (NSString*)aKey -{ - GSXibElement *element = [currentElement elementForKey: aKey]; - NSString *objID; - - if (element == nil) - return nil; - - objID = [element attributeForKey: @"id"]; - if (objID) - { - return objID; - } - - objID = [element attributeForKey: @"ref"]; - if (objID) - { - return objID; - } - - return nil; -} - -- (BOOL) containsValueForKey: (NSString*)aKey -{ - GSXibElement *element = [currentElement elementForKey: aKey]; - - return (element != nil); -} - -- (id) decodeObjectForKey: (NSString*)aKey -{ - GSXibElement *element = [currentElement elementForKey: aKey]; - - if (element == nil) - return nil; - - return [self objectForXib: element]; -} - -- (BOOL) decodeBoolForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - return [o boolValue]; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return NO; -} - -- (const uint8_t*) decodeBytesForKey: (NSString*)aKey - returnedLength: (NSUInteger*)length -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSData class]] == YES) - { - *length = [o length]; - return [o bytes]; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - *length = 0; - return 0; -} - -- (double) decodeDoubleForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - return [o doubleValue]; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return 0.0; -} - -- (float) decodeFloatForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - return [o floatValue]; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return 0.0; -} - -- (int) decodeIntForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - long long l = [o longLongValue]; - - return l; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return 0; -} - -- (int32_t) decodeInt32ForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - long long l = [o longLongValue]; - - return l; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return 0; -} - -- (int64_t) decodeInt64ForKey: (NSString*)aKey -{ - id o = [self decodeObjectForKey: aKey]; - - if (o != nil) - { - if ([o isKindOfClass: [NSNumber class]] == YES) - { - long long l = [o longLongValue]; - - return l; - } - else - { - [NSException raise: NSInvalidUnarchiveOperationException - format: @"[%@ +%@]: value for key(%@) is '%@'", - NSStringFromClass([self class]), NSStringFromSelector(_cmd), - aKey, o]; - } - } - return 0; -} - -@end diff --git a/Source/GSXibLoading.m b/Source/GSXibLoading.m index df0da4719..14adacde1 100644 --- a/Source/GSXibLoading.m +++ b/Source/GSXibLoading.m @@ -65,6 +65,17 @@ return value; } +- (NSString*) description +{ + NSMutableString *description = [[super description] mutableCopy]; + [description appendString: @" <"]; + [description appendFormat: @" type: %@", typeIdentifier]; + [description appendFormat: @" keyPath: %@", keyPath]; + [description appendFormat: @" value: %@", value]; + [description appendString: @">"]; + return AUTORELEASE(description); +} + @end @implementation IBUserDefinedRuntimeAttributesPlaceholder diff --git a/Source/NSButtonCell.m b/Source/NSButtonCell.m index a35bd2b0e..b22979f05 100644 --- a/Source/NSButtonCell.m +++ b/Source/NSButtonCell.m @@ -59,59 +59,6 @@ #include -typedef struct _GSButtonCellFlags -{ -#if GS_WORDS_BIGENDIAN == 1 - unsigned int isPushin:1; - unsigned int changeContents:1; - unsigned int changeBackground:1; - unsigned int changeGray:1; - unsigned int highlightByContents:1; - unsigned int highlightByBackground:1; - unsigned int highlightByGray:1; - unsigned int drawing:1; - unsigned int isBordered:1; - unsigned int imageDoesOverlap:1; - unsigned int isHorizontal:1; - unsigned int isBottomOrLeft:1; - unsigned int isImageAndText:1; - unsigned int isImageSizeDiff:1; - unsigned int hasKeyEquiv:1; - unsigned int lastState:1; - unsigned int isTransparent:1; - unsigned int unused1:6; // inset:2 doesn't dim:1 gradient:3 - unsigned int useButtonImageSource:1; - unsigned int unused2:8; // alt mnemonic loc. -#else - unsigned int unused2:8; // alt mnemonic loc. - unsigned int useButtonImageSource:1; - unsigned int unused1:6; // inset:2 doesn't dim:1 gradient:3 - unsigned int isTransparent:1; - unsigned int lastState:1; - unsigned int hasKeyEquiv:1; - unsigned int isImageSizeDiff:1; - unsigned int isImageAndText:1; - unsigned int isBottomOrLeft:1; - unsigned int isHorizontal:1; - unsigned int imageDoesOverlap:1; - unsigned int isBordered:1; - unsigned int drawing:1; - unsigned int highlightByGray:1; - unsigned int highlightByBackground:1; - unsigned int highlightByContents:1; - unsigned int changeGray:1; - unsigned int changeBackground:1; - unsigned int changeContents:1; - unsigned int isPushin:1; -#endif -} GSButtonCellFlags; - -@interface NSCell (Private) -- (NSSize) _scaleImageWithSize: (NSSize)imageSize - toFitInSize: (NSSize)canvasSize - scalingType: (NSImageScaling)scalingType; -@end - /**

TODO Description

*/ @implementation NSButtonCell @@ -1105,10 +1052,11 @@ typedef struct _GSButtonCellFlags // The inside check could also be done via a track rect, but then this would // only work with specially prepared controls. Therefore we dont use // _mouse_inside here. - if ((_cell.is_bordered) && (_bezel_style != NSCircularBezelStyle) - && (!_shows_border_only_while_mouse_inside - || [controlView mouse: [[controlView window] mouseLocationOutsideOfEventStream] - inRect: cellFrame])) + if ((_cell.is_bordered) && + (_bezel_style != NSCircularBezelStyle) && + (!_shows_border_only_while_mouse_inside + || [controlView mouse: [[controlView window] mouseLocationOutsideOfEventStream] + inRect: cellFrame])) { // Testplat-MAL-2015-08-22: Turns out this draw is using the full // frame as opposed to the reduced frame. This ends up drawing @@ -1698,7 +1646,9 @@ typedef struct _GSButtonCellFlags buttonCellFlags.changeGray = [self cellAttribute: NSChangeGrayCell]; // set these to zero... - buttonCellFlags.unused1 = 0; // 32; + buttonCellFlags.inset = 0; // 32; + buttonCellFlags.doesNotDimImage = 0; // 32; + buttonCellFlags.gradient = 0; // 32; buttonCellFlags.unused2 = 0; // 255; buttonCellFlags.lastState = 0; buttonCellFlags.isImageSizeDiff = 0; @@ -1836,8 +1786,9 @@ typedef struct _GSButtonCellFlags } if ([aDecoder containsValueForKey: @"NSButtonFlags"]) { - unsigned int bFlags = [aDecoder decodeIntForKey: @"NSButtonFlags"]; + unsigned int bFlags = [aDecoder decodeIntForKey: @"NSButtonFlags"]; GSButtonCellFlags buttonCellFlags; + memcpy((void *)&buttonCellFlags,(void *)&bFlags,sizeof(struct _GSButtonCellFlags)); [self setTransparent: buttonCellFlags.isTransparent]; @@ -1859,50 +1810,62 @@ typedef struct _GSButtonCellFlags to: buttonCellFlags.changeGray]; if (buttonCellFlags.imageDoesOverlap) + { if (buttonCellFlags.isImageAndText) [self setImagePosition: NSImageOverlaps]; else [self setImagePosition: NSImageOnly]; + } else if (buttonCellFlags.isImageAndText) + { if (buttonCellFlags.isHorizontal) + { if (buttonCellFlags.isBottomOrLeft) [self setImagePosition: NSImageLeft]; else [self setImagePosition: NSImageRight]; + } else + { if (buttonCellFlags.isBottomOrLeft) [self setImagePosition: NSImageBelow]; else [self setImagePosition: NSImageAbove]; + } + } else + { [self setImagePosition: NSNoImage]; + } } if ([aDecoder containsValueForKey: @"NSButtonFlags2"]) { - NSUInteger imageScale; - int bFlags2; - - bFlags2 = [aDecoder decodeIntForKey: @"NSButtonFlags2"]; - [self setShowsBorderOnlyWhileMouseInside: (bFlags2 & 0x8)]; - [self setBezelStyle: (bFlags2 & 0x7) | ((bFlags2 & 0x20) >> 2)]; - [self setKeyEquivalentModifierMask: ((bFlags2 >> 8) & + unsigned int bFlags2 = [aDecoder decodeIntForKey: @"NSButtonFlags2"]; + GSButtonCellFlags2 buttonCellFlags2; + NSUInteger imageScale; + + memcpy((void *)&buttonCellFlags2, (void *)&bFlags2, sizeof(struct _GSButtonCellFlags2)); + + [self setShowsBorderOnlyWhileMouseInside: buttonCellFlags2.showsBorderOnlyWhileMouseInside]; + [self setBezelStyle: buttonCellFlags2.bezelStyle | (buttonCellFlags2.bezelStyle2 << 3)]; + [self setKeyEquivalentModifierMask: (buttonCellFlags2.keyEquivalentModifierMask & NSDeviceIndependentModifierFlagsMask)]; - switch ((bFlags2 >> 6) & 3) + switch (buttonCellFlags2.imageScaling) { + case 3: + imageScale = NSImageScaleAxesIndependently; + break; case 2: imageScale = NSImageScaleProportionallyDown; break; - case 3: - imageScale = NSImageScaleAxesIndependently; + case 1: + imageScale = NSImageScaleProportionallyUpOrDown; break; case 0: default: imageScale = NSImageScaleNone; break; - case 1: - imageScale = NSImageScaleProportionallyUpOrDown; - break; } [self setImageScaling: imageScale]; } diff --git a/Source/NSCell.m b/Source/NSCell.m index 65f57ae2a..e69ea659f 100644 --- a/Source/NSCell.m +++ b/Source/NSCell.m @@ -2668,52 +2668,55 @@ static NSColor *dtxtCol; if ([aDecoder containsValueForKey: @"NSCellFlags"]) { - unsigned long cFlags; - NSUInteger mask = 0; - cFlags = [aDecoder decodeIntForKey: @"NSCellFlags"]; - - [self setFocusRingType: (cFlags & 0x3)]; - [self setShowsFirstResponder: ((cFlags & 0x4) == 0x4)]; + NSUInteger mask = 0; + GSCellFlags cellFLags; + unsigned long flagValue = [aDecoder decodeIntForKey: @"NSCellFlags"]; + memcpy(&cellFLags, &flagValue, sizeof(cellFLags)); + //NSWarnMLog(@"title: %@ cellFLags: %u", [self title], flagValue); + + [self setFocusRingType: cellFLags.focusRingType]; + [self setShowsFirstResponder: cellFLags.showsFirstResponder]; // This bit flag is the other way around! - if ((cFlags & 0x20) != 0x20) + if (cellFLags.dontActOnMouseUp == NO) mask |= NSLeftMouseUpMask; // This bit flag is the other way around! - [self setWraps: ((cFlags & 0x40) != 0x40)]; - if ((cFlags & 0x100) == 0x100) + [self setWraps: cellFLags.truncateLastLine == NO]; + if (cellFLags.actOnMouseDragged) mask |= NSLeftMouseDraggedMask; - if ((cFlags & 0x40000) == 0x40000) + if (cellFLags.actOnMouseDown) mask |= NSLeftMouseDownMask; - if ((cFlags & 0x80000) == 0x80000) + if (cellFLags.continuous) mask |= NSPeriodicMask; [self sendActionOn: mask]; - [self setScrollable: ((cFlags & 0x100000) == 0x100000)]; - [self setSelectable: ((cFlags & 0x200000) == 0x200000)]; - [self setBezeled: ((cFlags & 0x400000) == 0x400000)]; - [self setBordered: ((cFlags & 0x800000) == 0x800000)]; - [self setType: ((cFlags & 0xC000000) >> 26)]; - [self setEditable: ((cFlags & 0x10000000) == 0x10000000)]; + [self setScrollable: cellFLags.scrollable]; + [self setSelectable: cellFLags.selectable]; + [self setBezeled: cellFLags.bezeled]; + [self setBordered: cellFLags.bordered]; + [self setType: cellFLags.type]; + [self setEditable: cellFLags.editable]; // This bit flag is the other way around! - [self setEnabled: ((cFlags & 0x20000000) != 0x20000000)]; - [self setHighlighted: ((cFlags & 0x40000000) == 0x40000000)]; - [self setState: ((cFlags & 0x80000000) == 0x80000000) ? NSOnState : NSOffState]; + [self setEnabled: (cellFLags.disabled == NO)]; + [self setHighlighted: cellFLags.highlighted]; + [self setState: cellFLags.state ? NSOnState : NSOffState]; } if ([aDecoder containsValueForKey: @"NSCellFlags2"]) { - int cFlags2; - - cFlags2 = [aDecoder decodeIntForKey: @"NSCellFlags2"]; - [self setControlTint: ((cFlags2 & 0xE0) >> 5)]; - [self setLineBreakMode: ((cFlags2 & 0xE00) >> 9)]; - [self setControlSize: ((cFlags2 & 0xE0000) >> 17)]; - [self setSendsActionOnEndEditing: ((cFlags2 & 0x400000) == 0x400000)]; - [self setAllowsMixedState: ((cFlags2 & 0x1000000) == 0x1000000)]; - [self setRefusesFirstResponder: ((cFlags2 & 0x2000000) == 0x2000000)]; - [self setAlignment: ((cFlags2 & 0x1C000000) >> 26)]; - [self setImportsGraphics: ((cFlags2 & 0x20000000) == 0x20000000)]; - [self setAllowsEditingTextAttributes: ((cFlags2 & 0x40000000) == 0x40000000)]; + GSCellFlags2Union cFlags2; + cFlags2.value = [aDecoder decodeIntForKey: @"NSCellFlags2"]; + //NSWarnMLog(@"title: %@ cFlags2: %u", [self title], cFlags2.value); + [self setControlTint: cFlags2.flags.controlTint]; + [self setLineBreakMode: cFlags2.flags.lineBreakMode]; + [self setControlSize: cFlags2.flags.controlSize]; + [self setSendsActionOnEndEditing: cFlags2.flags.sendsActionOnEndEditing]; + [self setAllowsMixedState: cFlags2.flags.allowsMixedState]; + [self setRefusesFirstResponder: cFlags2.flags.refusesFirstResponder]; + [self setAlignment: cFlags2.flags.alignment]; + [self setImportsGraphics: cFlags2.flags.importsGraphics]; + [self setAllowsEditingTextAttributes: cFlags2.flags.allowsEditingTextAttributes]; + // These bit flags are the other way around! - [self setAllowsUndo: ((cFlags2 & 0x1000) != 0x1000)]; + [self setAllowsUndo: (cFlags2.flags.doesNotAllowUndo ? NO : YES)]; } if ([aDecoder containsValueForKey: @"NSSupport"]) { diff --git a/Source/NSColor.m b/Source/NSColor.m index 1d5ab696e..815d88d5d 100644 --- a/Source/NSColor.m +++ b/Source/NSColor.m @@ -1471,7 +1471,7 @@ systemColorWithName(NSString *name) RELEASE(scanner); RELEASE(str); } - + if (colorSpace == 1) { self = RETAIN([NSColor colorWithCalibratedRed: red @@ -1873,7 +1873,7 @@ systemColorWithName(NSString *name) } NSAssert([[list name] isEqual: @"System"], NSInvalidArgumentException); [NSColorList _setThemeSystemColorList: list]; - + /* We always update the system dictionary and send a notification, since * the theme may have given us a pre-existing color list, but have changed * one or more of the colors in it. @@ -1909,7 +1909,7 @@ static NSRecursiveLock *namedColorLock = nil; } - (NSColor*) initWithCatalogName: (NSString *)listName - colorName: (NSString *)colorName + colorName: (NSString *)colorName { NSMutableDictionary *d; NSColor *c; @@ -2018,7 +2018,7 @@ static NSRecursiveLock *namedColorLock = nil; } - (NSColor*) colorUsingColorSpaceName: (NSString *)colorSpace - device: (NSDictionary *)deviceDescription + device: (NSDictionary *)deviceDescription { NSColorList *list; NSColor *real; @@ -2096,13 +2096,22 @@ static NSRecursiveLock *namedColorLock = nil; { listName = [aDecoder decodeObjectForKey: @"NSCatalogName"]; colorName = [aDecoder decodeObjectForKey: @"NSColorName"]; - NSColor *color = (NSColor*)[aDecoder decodeObjectForKey: @"NSColor"]; NSColorList *colorList = [NSColorList colorListNamed: listName]; - - if ([colorList colorWithKey:colorName] == nil) + NSColor *color = (NSColor*)[colorList colorWithKey:colorName]; + + if (color == nil) { - NSWarnMLog(@"adding to color list: %@ name: %@ color: %@", listName, colorName, color); - [[NSColorList colorListNamed:listName] setColor:color forKey:colorName]; + color = [aDecoder decodeObjectForKey: @"NSColor"]; + if (color == nil) + { + NSWarnMLog(@"no color available for listName: %@ colorName: %@ color: %@", listName, colorName, color); + [[NSColorList colorListNamed: listName] setColor: [NSColor redColor] forKey: colorName]; + } + else + { + NSWarnMLog(@"adding to color list: %@ colorName: %@ color: %@", listName, colorName, color); + [[NSColorList colorListNamed: listName] setColor: color forKey: colorName]; + } } } else diff --git a/Source/NSMatrix.m b/Source/NSMatrix.m index 38c225e48..ab85b7703 100644 --- a/Source/NSMatrix.m +++ b/Source/NSMatrix.m @@ -129,40 +129,6 @@ typedef struct { NSInteger height; } MRect; -typedef struct _GSMatrixFlags { -#if GS_WORDS_BIGENDIAN == 1 - unsigned int isHighlight:1; - unsigned int isRadio:1; - unsigned int isList:1; - unsigned int allowsEmptySelection:1; - unsigned int autoScroll:1; - unsigned int selectionByRect:1; - unsigned int drawCellBackground:1; - unsigned int drawBackground:1; - unsigned int autosizesCells:1; - unsigned int drawingAncestor:1; - unsigned int tabKeyTraversesCells:1; - unsigned int tabKeyTraversesCellsExplicitly:1; - unsigned int canSearchIncrementally:1; - unsigned int unused:19; -#else - unsigned int unused:19; - unsigned int canSearchIncrementally:1; - unsigned int tabKeyTraversesCellsExplicitly:1; - unsigned int tabKeyTraversesCells:1; - unsigned int drawingAncestor:1; - unsigned int autosizesCells:1; - unsigned int drawBackground:1; - unsigned int drawCellBackground:1; - unsigned int selectionByRect:1; - unsigned int autoScroll:1; - unsigned int allowsEmptySelection:1; - unsigned int isList:1; - unsigned int isRadio:1; - unsigned int isHighlight:1; -#endif -} GSMatrixFlags; - static inline MPoint MakePoint (NSInteger x, NSInteger y) { MPoint point = { x, y }; diff --git a/Source/NSScrollView.m b/Source/NSScrollView.m index 368013758..696b4b067 100644 --- a/Source/NSScrollView.m +++ b/Source/NSScrollView.m @@ -267,6 +267,11 @@ static CGFloat scrollerWidth; [_contentView removeFromSuperview]; [self addSubview: aView]; + + // Testplant-MAL-01092017: XIB 5 support... + if (_headerClipView && ([[self subviews] count] == 1)) + [self addSubview: _headerClipView]; + // This must be done after adding it as a subview, // otherwise it will get unset again. _contentView = aView; @@ -1686,13 +1691,13 @@ GSOppositeEdge(NSRectEdge edge) if (hScroller != nil && _hasHorizScroller) { [self setHorizontalScroller: hScroller]; - [hScroller setHidden: NO]; + [hScroller setHidden: NO]; } if (vScroller != nil && _hasVertScroller) { [self setVerticalScroller: vScroller]; - [vScroller setHidden: NO]; + [vScroller setHidden: NO]; } if ([aDecoder containsValueForKey: @"NSHeaderClipView"]) @@ -1700,7 +1705,7 @@ GSOppositeEdge(NSRectEdge edge) _hasHeaderView = YES; _headerClipView = [aDecoder decodeObjectForKey: @"NSHeaderClipView"]; } - + // set the document view into the content. [self setContentView: content]; [self tile]; diff --git a/Source/NSScroller.m b/Source/NSScroller.m index 6a902d41f..8e6750a1d 100644 --- a/Source/NSScroller.m +++ b/Source/NSScroller.m @@ -249,23 +249,26 @@ static float buttonsOffset = 1.0; // buttonsWidth = sw - 2*buttonsOffset if ([aDecoder allowsKeyedCoding]) { + // Set default action - overridden by XIB when changed... + [self setAction: NSSelectorFromString(@"_doScroller:")]; + if (_frame.size.width > _frame.size.height) { - _scFlags.isHorizontal = YES; - } + _scFlags.isHorizontal = YES; + } else { - _scFlags.isHorizontal = NO; - } + _scFlags.isHorizontal = NO; + } if (_scFlags.isHorizontal) { - _doubleValue = 0.0; - } + _doubleValue = 0.0; + } else { - _doubleValue = 1.0; - } + _doubleValue = 1.0; + } if ([aDecoder containsValueForKey: @"NSAction"]) { @@ -282,32 +285,32 @@ static float buttonsOffset = 1.0; // buttonsWidth = sw - 2*buttonsOffset } if ([aDecoder containsValueForKey: @"NSCurValue"]) { - float value = [aDecoder decodeFloatForKey: @"NSCurValue"]; + float value = [aDecoder decodeFloatForKey: @"NSCurValue"]; [self setFloatValue: value]; - } + } if ([aDecoder containsValueForKey: @"NSPercent"]) { - float percent = [aDecoder decodeFloatForKey: @"NSPercent"]; + float percent = [aDecoder decodeFloatForKey: @"NSPercent"]; [self setKnobProportion: percent / 100.0]; - } + } if ([aDecoder containsValueForKey: @"NSsFlags"]) { int flags; - - flags = [aDecoder decodeIntForKey: @"NSsFlags"]; - // is horiz is set above... + + flags = [aDecoder decodeIntForKey: @"NSsFlags"]; + // is horiz is set above... [self setControlTint: ((flags >> 16) & 7)]; [self setArrowsPosition: ((flags >> 29) & 3)]; _usableParts = ((flags >> 27) & 3); - } + } if ([aDecoder containsValueForKey: @"NSsFlags2"]) { int flags2; - - flags2 = [aDecoder decodeIntForKey: @"NSsFlags2"]; + + flags2 = [aDecoder decodeIntForKey: @"NSsFlags2"]; [self setControlSize: ((flags2 >> 26) & 3)]; - } + } // setup... _hitPart = NSScrollerNoPart; diff --git a/Source/NSSearchFieldCell.m b/Source/NSSearchFieldCell.m index 87dfaedeb..99c099144 100644 --- a/Source/NSSearchFieldCell.m +++ b/Source/NSSearchFieldCell.m @@ -295,6 +295,7 @@ static inline NSRect textCellFrameFromRect(NSRect cellRect) [c setTarget: self]; [c setKeyEquivalent: @"\e"]; [c setKeyEquivalentModifierMask: 0]; + [self setTitle: @""]; } - (void) resetSearchButtonCell @@ -520,24 +521,30 @@ static inline NSRect textCellFrameFromRect(NSRect cellRect) if (self != nil) { if ([aDecoder allowsKeyedCoding]) - { - [self setSearchButtonCell: [aDecoder decodeObjectForKey: @"NSSearchButtonCell"]]; - [self setCancelButtonCell: [aDecoder decodeObjectForKey: @"NSCancelButtonCell"]]; - [self setRecentsAutosaveName: [aDecoder decodeObjectForKey: @"NSRecentsAutosaveName"]]; - [self setSendsWholeSearchString: [aDecoder decodeBoolForKey: @"NSSendsWholeSearchString"]]; - [self setMaximumRecents: [aDecoder decodeIntForKey: @"NSMaximumRecents"]]; - } + { + [self setSearchButtonCell: [aDecoder decodeObjectForKey: @"NSSearchButtonCell"]]; + [self setCancelButtonCell: [aDecoder decodeObjectForKey: @"NSCancelButtonCell"]]; + [self setRecentsAutosaveName: [aDecoder decodeObjectForKey: @"NSRecentsAutosaveName"]]; + [self setSendsWholeSearchString: [aDecoder decodeBoolForKey: @"NSSendsWholeSearchString"]]; + [self setMaximumRecents: [aDecoder decodeIntForKey: @"NSMaximumRecents"]]; + + if ([[aDecoder class] coderVersion] > 0) + { + [self setDrawsBackground: YES]; + [self setBackgroundColor: [NSColor whiteColor]]; + } + } else - { - NSInteger max; + { + NSInteger max; - [self setSearchButtonCell: [aDecoder decodeObject]]; - [self setCancelButtonCell: [aDecoder decodeObject]]; - [self setRecentsAutosaveName: [aDecoder decodeObject]]; - [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_sends_whole_search_string]; - [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &max]; - [self setMaximumRecents: max]; - } + [self setSearchButtonCell: [aDecoder decodeObject]]; + [self setCancelButtonCell: [aDecoder decodeObject]]; + [self setRecentsAutosaveName: [aDecoder decodeObject]]; + [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_sends_whole_search_string]; + [aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &max]; + [self setMaximumRecents: max]; + } [self resetCancelButtonCell]; [self resetSearchButtonCell]; diff --git a/Source/NSTabView.m b/Source/NSTabView.m index 54fdacf8d..ae5af877b 100644 --- a/Source/NSTabView.m +++ b/Source/NSTabView.m @@ -555,11 +555,12 @@ } if ([aDecoder containsValueForKey: @"NSTvFlags"]) { - int vFlags = [aDecoder decodeIntForKey: @"NSTvFlags"]; + GSTabViewTypeFlagsUnion mask; + mask.value = [aDecoder decodeIntForKey: @"NSTvFlags"]; - [self setControlTint: ((vFlags & 0x70000000) >> 28)]; - [self setControlSize: ((vFlags & 0x0c000000) >> 26)]; - [self setTabViewType: (vFlags & 0x00000007)]; + [self setControlTint: mask.flags.controlTint]; + [self setControlSize: mask.flags.controlSize]; + [self setTabViewType: mask.flags.tabViewBorderType]; } if ([aDecoder containsValueForKey: @"NSTabViewItems"]) { diff --git a/Source/NSTableColumn.m b/Source/NSTableColumn.m index 4042f893a..59d325d8f 100644 --- a/Source/NSTableColumn.m +++ b/Source/NSTableColumn.m @@ -311,7 +311,11 @@ Return the resizing mask that describes whether the column is resizable and how it resizes. */ - (void) setResizingMask: (NSUInteger)resizingMask { - _resizing_mask = resizingMask; + if (_resizing_mask != resizingMask) + { + _resizing_mask = resizingMask; + [self setResizable:(_resizing_mask & NSTableColumnUserResizingMask ? YES : NO)]; + } } /** diff --git a/Source/NSTableView.m b/Source/NSTableView.m index 19abbe02c..e3e3eca1b 100644 --- a/Source/NSTableView.m +++ b/Source/NSTableView.m @@ -84,35 +84,6 @@ static NSInteger currentDropRow; static NSInteger lastQuarterPosition; static NSDragOperation currentDragOperation; -/* - * Nib compatibility struct. This structure is used to - * pull the attributes out of the nib that we need to fill - * in the flags. - */ -typedef struct _tableViewFlags -{ -#if GS_WORDS_BIGENDIAN == 1 - unsigned int columnOrdering:1; - unsigned int columnResizing:1; - unsigned int drawsGrid:1; - unsigned int emptySelection:1; - unsigned int multipleSelection:1; - unsigned int columnSelection:1; - unsigned int columnAutosave:1; - unsigned int _unused:24; -#else - unsigned int _unused:24; - unsigned int columnAutosave:1; - unsigned int unknown1:1; - unsigned int columnSelection:1; - unsigned int multipleSelection:1; - unsigned int emptySelection:1; - unsigned int drawsGrid:1; - unsigned int columnResizing:1; - unsigned int columnOrdering:1; -#endif -} GSTableViewFlags; - #define ALLOWS_MULTIPLE (1) #define ALLOWS_EMPTY (1 << 1) #define SHIFT_DOWN (1 << 2) @@ -2019,7 +1990,7 @@ static void computeNewSelection { _isValidating = NO; _drawsGrid = YES; - _rowHeight = 16.0; + _rowHeight = 17.0; // XIB 5 defaults to 17 so we'll use that... _intercellSpacing = NSMakeSize (5.0, 2.0); ASSIGN(_selectedColumns, [NSMutableIndexSet indexSet]); ASSIGN(_selectedRows, [NSMutableIndexSet indexSet]); diff --git a/Source/NSView.m b/Source/NSView.m index 800dcfd3a..2e8430227 100644 --- a/Source/NSView.m +++ b/Source/NSView.m @@ -4726,13 +4726,15 @@ static NSView* findByTag(NSView *view, NSInteger aTag, NSUInteger *level) } if ([aDecoder containsValueForKey: @"NSvFlags"]) { + GSvFlags VFlags; NSUInteger vFlags = [aDecoder decodeIntForKey: @"NSvFlags"]; - + memcpy(&VFlags, &vFlags, sizeof(VFlags)); + // We are lucky here, Apple use the same constants // in the lower bits of the flags - [self setAutoresizingMask: vFlags & 0x3F]; - [self setAutoresizesSubviews: ((vFlags & 0x100) == 0x100)]; - [self setHidden: ((vFlags & 0x80000000) == 0x80000000)]; + [self setAutoresizingMask: VFlags.autoresizingMask]; + [self setAutoresizesSubviews: VFlags.autoresizesSubviews]; + [self setHidden: VFlags.isHidden]; } // iterate over subviews and put them into the view...