Merge pull request #277 from gnustep/NSTreeController_branch2

Updated PR for NSOutlineView/NSTreeController due to refactor
This commit is contained in:
Gregory Casamento 2024-08-23 03:25:51 -04:00 committed by GitHub
commit b003db284e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
22 changed files with 2490 additions and 1357 deletions

View file

@ -1,3 +1,21 @@
2024-08-18 Gregory John Casamento <greg.casamento@gmail.com>
* Headers/AppKit/NSTreeController.h: Update header
* Headers/AppKit/NSTreeNode.h: Update header
* Source/GNUmakefile: Add new class
* Source/GSBindingHelpers.h: Add NSString category
* Source/GSControllerTreeProxy.[hm]: Proxy class
for NSTreeController, a subclass of NSTreeNode.
* Source/GSThemeDrawing.m: Optimize code a bit and
add changes for bindings.
* Source/GSXib5KeyedUnarchiver.m: Add support for new keys
on NSTreeController.
* Source/NSKeyValueBinding.m: Add new methods to
GSKeyValueBinding
* Source/NSOutlineView.m: Add support for bindings.
* Source/NSTreeController.m: Implement new methods.
* Source/NSTreeNode.m: Implement new methods
2024-07-25 Gregory John Casamento <greg.casamento@gmail.com>
* Headers/AppKit/NSBrowser.h: Add declarations for
@ -16,7 +34,7 @@
* Source/GNUmakefile: Add class to build
* Source/NSMenuToolbarItem.m: Implementation of
NSMenuToolbarItem.
2024-06-07 Gregory John Casamento <greg.casamento@gmail.com>
* Headers/Additions/GNUstepGUI/GSTheme.h: Add new methods

View file

@ -126,6 +126,7 @@ APPKIT_EXPORT NSString *NSSelectedObjectBinding;
APPKIT_EXPORT NSString *NSSelectedTagBinding;
APPKIT_EXPORT NSString *NSSelectedValueBinding;
APPKIT_EXPORT NSString *NSSelectionIndexesBinding;
APPKIT_EXPORT NSString *NSSelectionIndexPathsBinding;
APPKIT_EXPORT NSString *NSSortDescriptorsBinding;
APPKIT_EXPORT NSString *NSTextColorBinding;
APPKIT_EXPORT NSString *NSTitleBinding;

View file

@ -44,7 +44,6 @@ APPKIT_EXPORT_CLASS
NSMapTable *_itemDict;
NSMutableArray *_items;
NSMutableArray *_expandedItems;
NSMutableArray *_selectedItems; /* No longer in use */
NSMapTable *_levelOfItems;
BOOL _autoResizesOutlineColumn;
BOOL _indentationMarkerFollowsCell;

View file

@ -1,4 +1,4 @@
/*
/*
NSTreeController.h
The tree controller class.
@ -7,7 +7,7 @@
Author: Gregory Casamento <greg.casamento@gmail.com>
Date: 2012
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
@ -22,10 +22,10 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
*/
#ifndef _GNUstep_H_NSTreeController
#define _GNUstep_H_NSTreeController
@ -34,6 +34,7 @@
#if OS_API_VERSION(MAC_OS_X_VERSION_10_4, GS_API_LATEST)
#import <AppKit/NSObjectController.h>
#import <AppKit/NSNibDeclarations.h>
@class NSString;
@class NSArray;
@ -47,61 +48,306 @@ APPKIT_EXPORT_CLASS
NSString *_countKeyPath;
NSString *_leafKeyPath;
NSArray *_sortDescriptors;
NSTreeNode *_arranged_objects;
NSMutableArray *_selection_index_paths;
BOOL _alwaysUsesMultipleValuesMarker;
BOOL _avoidsEmptySelection;
BOOL _preservesSelection;
BOOL _selectsInsertedObjects;
BOOL _canAddChild;
BOOL _canInsert;
BOOL _canInsertChild;
}
- (BOOL) addSelectionIndexPaths: (NSArray*)indexPaths;
/**
* Adds the objects in the indexPaths array to the current selection.
*/
- (BOOL) addSelectionIndexPaths: (NSArray *)indexPaths;
/**
* BOOL that indicates if the controller returns the multiple values marker when
* multiple objects have been selected.
*/
- (BOOL) alwaysUsesMultipleValuesMarker;
/**
* If YES, requires the content array to maintain a selection.
*/
- (BOOL) avoidsEmptySelection;
- (BOOL) canAddChid;
/**
* If YES, a child can be added.
*/
- (BOOL) canAddChild;
/**
* If YES, an object can be inserted.
*/
- (BOOL) canInsert;
/**
* If YES, a child can be inserted.
*/
- (BOOL) canInsertChild;
- (BOOL) preservesSelection;
/**
* If YES, then preserve the current selection when the content changes.
*/
- (BOOL) preservesSelection;
/**
* If YES, then when an object is inserted it is added to the selection.
*/
- (BOOL) selectsInsertedObjects;
- (BOOL) setSelectionIndexPath: (NSIndexPath*)indexPath;
- (BOOL) setSelectionIndexPaths: (NSArray*)indexPaths;
- (id) arrangedObjects;
- (id) content;
- (NSArray*) selectedObjects;
- (NSIndexPath*) selectionIndexPath;
- (NSArray*) selectionIndexPaths;
- (NSArray*) sortDescriptors;
- (NSString*) childrenKeyPath;
- (NSString*) countKeyPath;
- (NSString*) leafKeyPath;
- (void) addChild: (id)sender;
- (void) add: (id)sender;
- (void) insertChild: (id)sender;
- (void) insertObject: (id)object atArrangedObjectIndexPath: (NSIndexPath*)indexPath;
- (void) insertObjects: (NSArray*)objects atArrangedObjectIndexPaths: (NSArray*)indexPaths;
- (void) insert: (id)sender;
/**
* Makes indexPath the current selection.
*/
- (BOOL) setSelectionIndexPath: (NSIndexPath *)indexPath;
/**
* Makes the array indexPaths the current selections.
*/
- (BOOL) setSelectionIndexPaths: (NSArray *)indexPaths;
/**
* All objects managed by this tree controller.
*/
- (NSTreeNode *) arrangedObjects;
/**
* An NSArray containing all selected objects.
*/
- (NSArray *) selectedObjects;
/**
* The index path of the first selected object.
*/
- (NSIndexPath *) selectionIndexPath;
/**
* An array containing all of the currently selected objects.
*/
- (NSArray *) selectionIndexPaths;
/**
* An array containing sort descriptors used to arrange content.
*/
- (NSArray *) sortDescriptors;
/**
* Key path for children of the node. This key must be key value
* compliant.
*/
- (NSString *) childrenKeyPath;
/**
* Key value path for the flag which gives the count for the children
* of this node. The path indicated here must be key-value compliant.
* If count is enabled, then add:, addChild:, remove:, removeChild:
* and insert: are disabled. This key path is option since it can
* be determined by the array of children retuned by the
* childKeyPath. The mode the tree controller is in when this is
* not specified is called "object" mode.
*/
- (NSString *) countKeyPath;
/**
* Key value path for the flag which determins that this is a leaf.
* The path indicated here must be key-value compliant. This
* key path is optional as it can be determined by the children
* returned by the childrenKeyPath.
*/
- (NSString *) leafKeyPath;
/**
* Adds a child to the current selection using the newObject method.
* If the tree controller is in "object" mode, then newObject is called
* to add a new node.
*/
- (IBAction) addChild: (id)sender;
/**
* Adds a new objeect to the tree usin the newObject method.
* If the tree controller is in "object" mode, then newObject is called
* to add a new node.
*/
- (IBAction) add: (id)sender;
/**
* Inserts a child using the newObject method. This method
* will fail if canInsertChild returns NO.
* If the tree controller is in "object" mode, then newObject is called
* to add a new node.
*/
- (IBAction) insertChild: (id)sender;
/**
* Inserts and object using the newObject method at the specified indexPath.
* If the tree controller is in "object" mode, then newObject is called
* to add a new node.
*/
- (void) insertObject: (id)object atArrangedObjectIndexPath: (NSIndexPath *)indexPath;
/**
* Inserts objects into arranged objects at the specified indexPaths. These arrays are
* expected to be parallel and have the same number of objects.
* This method will only function if the tree controller is in
* "object" mode.
*/
- (void) insertObjects: (NSArray *)objects atArrangedObjectIndexPaths: (NSArray *)indexPaths;
/**
* Insert an object created by newObject into arranged objects.
* This method will only function if the tree controller is in
* "object" mode.
*/
- (void) insertObject: (id)object atArrangedObjectIndexPath: (NSIndexPath *)indexPath;
/**
* Inserts objects into arranged objects at the specified indexPaths. These arrays are
* expected to be parallel and have the same number of objects.
* This method will only function if the tree controller is in
* "object" mode.
*/
- (void) insertObjects: (NSArray *)objects atArrangedObjectIndexPaths: (NSArray *)indexPaths;
/**
* Insert an object created by newObject into arranged objects.
* This method will only function if the tree controller is in
* "object" mode.
*/
- (IBAction) insert: (id)sender;
/**
* Causes the controller to re-sort and rearrange the objects. This method
* should be called if anything has been done that affects the list of objects
* in the controller.
*/
- (void) rearrangeObjects;
- (void) removeObjectAtArrangedObjectIndexPath: (NSIndexPath*)indexPath;
- (void) removeObjectsAtArrangedObjectIndexPaths: (NSArray*)indexPaths;
- (void) removeSelectionIndexPaths: (NSArray*)indexPaths;
- (void) remove: (id)sender;
/**
* Removes object at the specified indexPath.
* This method will only function if the tree controller is in
* "object" mode.
*/
- (void) removeObjectAtArrangedObjectIndexPath: (NSIndexPath *)indexPath;
/**
* Removes objects at the specified indexPaths.
*/
- (void) removeObjectsAtArrangedObjectIndexPaths: (NSArray *)indexPaths;
/**
* Removes selection of objects at the specified indexPaths.
*/
- (void) removeSelectionIndexPaths: (NSArray *)indexPaths;
/**
* Remove the currently selected object
*/
- (void) removeObjectAtArrangedObjectIndexPath: (NSIndexPath *)indexPath;
/**
* Removes objects at the specified indexPaths.
*/
- (void) removeObjectsAtArrangedObjectIndexPaths: (NSArray *)indexPaths;
/**
* Removes selection of objects at the specified indexPaths.
*/
- (void) removeSelectionIndexPaths: (NSArray *)indexPaths;
/**
* Remove the currently selected object. This method will only
* function if the tree controller is in "object" mode.
*/
- (IBAction) remove: (id)sender;
/**
* Sets the flag to always use multiple values marker.
*/
- (void) setAlwaysUsesMultipleValuesMarker: (BOOL)flag;
/**
* Sets the flag to avoid empty selection.
*/
- (void) setAvoidsEmptySelection: (BOOL)flag;
- (void) setChildrenKeyPath: (NSString*)path;
- (void) setContent: (id)content;
- (void) setCountKeyPath: (NSString*)path;
- (void) setLeafPathKey: (NSString*)key;
/**
* Sets the children key path. This needs to be key-value compliant.
*/
- (void) setChildrenKeyPath: (NSString *)path;
/**
* Sets the count key path. This needs to be key-value compliant.
* Setting this key path will disable add:, addChild:, remove:,
* removeChild:, and insert: methods. If this is not specified,
* the tree controller is in "object" mode.
*/
- (void) setCountKeyPath: (NSString *)path;
/**
* Sets leaf key path. This value needs to be key-value compliant.
*/
- (void) setLeafKeyPath: (NSString *)key;
/**
* Sets the preserves selection flag.
*/
- (void) setPreservesSelection: (BOOL)flag;
/**
* Sets the flag that determines if objects inserted are automatically
* selected.
*/
- (void) setSelectsInsertedObjects: (BOOL)flag;
- (void) setSortDescriptors: (NSArray*)descriptors;
/**
* Sets the array of sort descriptors used when building arrangedObjects.
*/
- (void) setSortDescriptors: (NSArray *)descriptors;
#if OS_API_VERSION(MAC_OS_X_VERSION_10_5, GS_API_LATEST)
- (NSString*) childrenKeyPathForNode: (NSTreeNode*)node;
- (NSString*) countKeyPathForNode: (NSTreeNode*)node;
- (NSString*) leafKeyPathForNode: (NSTreeNode*)node;
- (void) moveNode: (NSTreeNode*)node toIndexPath: (NSIndexPath*)indexPath;
- (void) moveNodes: (NSArray*)nodes toIndexPath: (NSIndexPath*)startingIndexPath;
- (NSArray*) selectedNodes;
#endif
/**
* children key path for the given NSTreeNode.
*/
- (NSString *) childrenKeyPathForNode: (NSTreeNode *)node;
/**
* count key path for the given NSTreeNode.
*/
- (NSString *) countKeyPathForNode: (NSTreeNode *)node;
/**
* leaf key path for the given NSTreeNode.
*/
- (NSString *) leafKeyPathForNode: (NSTreeNode *)node;
/**
* Moves node to given indexPath
*/
- (void) moveNode: (NSTreeNode *)node toIndexPath: (NSIndexPath *)indexPath;
/**
* Move nodes to position at startingIndexPath
*/
- (void) moveNodes: (NSArray *)nodes toIndexPath: (NSIndexPath *)startingIndexPath;
/**
* Set the descriptors by which the content of this tree controller
* is sorted.
*/
- (void) setSortDescriptors: (NSArray *)descriptors;
/**
* Array containing all selected nodes
*/
- (NSArray *) selectedNodes;
#endif // 10_5
@end
#endif
#endif // if OS_API_VERSION...
#endif /* _GNUstep_H_NSTreeController */

View file

@ -26,6 +26,7 @@
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#ifndef _GNUstep_H_NSTreeNode
#define _GNUstep_H_NSTreeNode

View file

@ -304,6 +304,7 @@ NSWindowController.m \
NSWorkspace.m \
GSAnimator.m \
GSAutocompleteWindow.m \
GSControllerTreeProxy.m \
GSDisplayServer.m \
GSHelpManagerPanel.m \
GSInfoPanel.m \

View file

@ -64,6 +64,7 @@
- (void) reverseSetValueFor: (NSString *)binding;
- (id) destinationValue;
- (id) sourceValueFor: (NSString *)binding;
- (id) observedObject;
/* Transforms the value with a value transformer, if specified and available,
* and takes care of any placeholders

View file

@ -0,0 +1,59 @@
/* Definition of class GSControllerTreeProxy
Copyright (C) 2024 Free Software Foundation, Inc.
By: Gregory John Casamento
Date: 24-06-2024
This file is part of the GNUstep 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.1 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; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
#ifndef _GSControllerTreeProxy_h_GNUSTEP_GUI_INCLUDE
#define _GSControllerTreeProxy_h_GNUSTEP_GUI_INCLUDE
#import "AppKit/NSTreeNode.h"
@class NSTreeController;
#if defined(__cplusplus)
extern "C" {
#endif
@interface GSControllerTreeProxy : NSTreeNode
{
NSTreeController *_controller;
}
- (instancetype) initWithContent: (id)content
withController: (id)controller;
- (NSUInteger) count;
- (NSMutableArray *) children;
- (id) value;
- (void) setValue: (id)value;
@end
#if defined(__cplusplus)
}
#endif
#endif /* _GSControllerTreeProxy_h_GNUSTEP_GUI_INCLUDE */

View file

@ -0,0 +1,90 @@
/* Implementation of class GSControllerTreeProxy
Copyright (C) 2024 Free Software Foundation, Inc.
By: Gregory John Casamento
Date: 24-06-2024
This file is part of the GNUstep 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.1 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; if not, write to the Free
Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110 USA.
*/
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSString.h>
#import "AppKit/NSTreeController.h"
#import "GSControllerTreeProxy.h"
#import "GSBindingHelpers.h"
@implementation GSControllerTreeProxy
- (instancetype) initWithContent: (id)content
withController: (id)controller
{
NSMutableDictionary *dict =
[NSMutableDictionary dictionaryWithObject:
[NSMutableArray arrayWithArray: content]
forKey: @"children"];
self = [super initWithRepresentedObject: dict];
if (self != nil)
{
ASSIGN(_controller, controller);
}
return self;
}
- (NSUInteger) count
{
NSArray *children = [[self representedObject] objectForKey: @"children"];
return [children count];
}
// This is here so that when the path is specified as "children" it responds
- (NSMutableArray *) children
{
NSDictionary *ro = [self representedObject];
NSMutableArray *children = [ro objectForKey: @"children"];
return children;
}
- (id) value
{
return [_representedObject objectForKey: @"value"];
}
- (void) setValue: (id)value
{
[_representedObject setObject: value
forKey: @"value"];
}
// These return the value in the cases where the parent class method is called...
- (NSArray *) childNodes
{
return [self children];
}
- (NSMutableArray *) mutableChildNodes
{
return [self children];
}
@end

View file

@ -44,6 +44,7 @@
#import "AppKit/NSGraphics.h"
#import "AppKit/NSImage.h"
#import "AppKit/NSImageView.h"
#import "AppKit/NSKeyValueBinding.h"
#import "AppKit/NSMenuView.h"
#import "AppKit/NSMenuItemCell.h"
#import "AppKit/NSOutlineView.h"
@ -70,6 +71,7 @@
#import "GNUstepGUI/GSToolbarView.h"
#import "GNUstepGUI/GSTitleView.h"
#import "GSBindingHelpers.h"
/* a border width of 5 gives a reasonable compromise between Cocoa metrics and looking good */
/* 7.0 gives us the NeXT Look (which is 8 pix wide including the shadow) */
@ -3284,7 +3286,7 @@ static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
{
endingRow = numberOfRows - 1;
}
// NSLog(@"drawRect : %d-%d", startingRow, endingRow);
// NSLog(@"drawRect : %ld-%ld", startingRow, endingRow);
{
SEL sel = @selector(drawRow:clipRect:);
void (*imp)(id, SEL, NSInteger, NSRect);
@ -3408,6 +3410,7 @@ static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
{
const BOOL columnSelected = [tableView isColumnSelected: i];
const BOOL cellSelected = (rowSelected || columnSelected);
tb = [tableColumns objectAtIndex: i];
cell = [tb dataCellForRow: rowIndex];
[tableView _willDisplayCell: cell
@ -3529,14 +3532,8 @@ static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
NSInteger endingColumn;
NSRect drawingRect;
NSInteger i;
id dataSource = [outlineView dataSource];
NSTableColumn *outlineTableColumn = [outlineView outlineTableColumn];
if (dataSource == nil)
{
return;
}
/* Using columnAtPoint: here would make it called twice per row per drawn
rect - so we avoid it and do it natively */
@ -3566,9 +3563,9 @@ static NSDictionary *titleTextAttributes[3] = {nil, nil, nil};
}
else
{
[cell setObjectValue: [dataSource outlineView: outlineView
objectValueForTableColumn: tb
byItem: item]];
id value = [outlineView _objectValueForTableColumn: tb
row: rowIndex];
[cell setObjectValue: value];
}
drawingRect = [outlineView frameOfCellAtColumn: i

View file

@ -330,6 +330,9 @@ static NSArray *XmlBoolDefaultYes = nil;
@"shadow", @"NSViewShadow",
@"blurRadius", @"NSShadowBlurRadius",
@"color", @"NSShadowColor",
@"childrenKeyPath", @"NSTreeContentChildrenKey", // NSTreeController
@"countKeyPath", @"NSTreeContentCountKey",
@"leafKeyPath", @"NSTreeContentLeafKey",
nil];
RETAIN(XmlKeyMapTable);

View file

@ -147,6 +147,7 @@
if (self == [NSArrayController class])
{
[self exposeBinding: NSContentArrayBinding];
[self exposeBinding: NSSelectionIndexesBinding];
[self setKeys: [NSArray arrayWithObjects: NSContentBinding, NSContentObjectBinding, nil]
triggerChangeNotificationsForDependentKey: @"arrangedObjects"];
}

View file

@ -66,6 +66,7 @@
#import "GNUstepGUI/GSTheme.h"
#import "GSGuiPrivate.h"
#import "GSBindingHelpers.h"
/* Cache */
static CGFloat scrollerWidth; // == [NSScroller scrollerWidth];
@ -2569,6 +2570,8 @@ static BOOL browserUseBezels;
- (void) dealloc
{
[GSKeyValueBinding unbindAllForObject: self];
[[NSNotificationCenter defaultCenter] removeObserver: self];
if ([titleCell controlView] == self)

View file

@ -122,6 +122,7 @@ static NSNotificationCenter *nc;
- (void) dealloc
{
[GSKeyValueBinding unbindAllForObject: self];
RELEASE(_cell);
[super dealloc];
}

View file

@ -192,8 +192,11 @@ void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
if (!objectTable)
return nil;
NSDebugLLog(@"NSBinding", @"+++ called with %@, %@", binding, anObject);
[bindingLock lock];
bindings = (NSMutableDictionary *)NSMapGet(objectTable, (void *)anObject);
NSDebugLLog(@"NSBinding", @"+++ Bindings found for %@ => %@", anObject, bindings);
if (bindings != nil)
{
theBinding = (GSKeyValueBinding*)[bindings objectForKey: binding];
@ -321,6 +324,11 @@ void GSBindingInvokeAction(NSString *targetKey, NSString *argumentKey,
[super dealloc];
}
- (id) observedObject
{
return [info objectForKey: NSObservedObjectKey];
}
- (id) destinationValue
{
id newValue;

View file

@ -99,6 +99,8 @@
{
if (self == [NSObjectController class])
{
[self setVersion: 1];
[self exposeBinding: NSContentObjectBinding];
[self setKeys: [NSArray arrayWithObject: @"editable"]
triggerChangeNotificationsForDependentKey: @"canAdd"];
@ -155,11 +157,13 @@
[coder encodeValueOfObjCType: @encode(BOOL) at: &_is_editable];
[coder encodeValueOfObjCType: @encode(BOOL) at: &_automatically_prepares_content];
[coder encodeConditionalObject: _managed_proxy];
[coder encodeObject: NSStringFromClass([self objectClass])];
}
}
- (id) initWithCoder: (NSCoder *)coder
{
{
int version = [coder versionForClassName: @"NSObjectController"];
if ((self = [super initWithCoder: coder]) != nil)
{
if ([self automaticallyPreparesContent])
@ -190,6 +194,11 @@
[coder decodeValueOfObjCType: @encode(BOOL) at: &_is_editable];
[coder decodeValueOfObjCType: @encode(BOOL) at: &_automatically_prepares_content];
ASSIGN(_managed_proxy, [coder decodeObject]);
if (version > 0)
{
NSString *className = [coder decodeObject];
[self setObjectClass: NSClassFromString(className)];
}
}
}

View file

@ -54,16 +54,22 @@
#import "AppKit/NSEvent.h"
#import "AppKit/NSGraphics.h"
#import "AppKit/NSImage.h"
#import "AppKit/NSKeyValueBinding.h"
#import "AppKit/NSOutlineView.h"
#import "AppKit/NSScroller.h"
#import "AppKit/NSTableColumn.h"
#import "AppKit/NSTableHeaderView.h"
#import "AppKit/NSText.h"
#import "AppKit/NSTextFieldCell.h"
#import "AppKit/NSTreeController.h"
#import "AppKit/NSTreeNode.h"
#import "AppKit/NSWindow.h"
#import "GNUstepGUI/GSTheme.h"
#import "GSBindingHelpers.h"
#import "GSFastEnumeration.h"
#import "GSGuiPrivate.h"
#include <math.h>
static NSMapTableKeyCallBacks keyCallBacks;
@ -143,6 +149,24 @@ static NSImage *unexpandable = nil;
clipRect: (NSRect)clipRect;
@end
@interface NSTableColumn (Private)
- (void) _applyBindingsToCell: (NSCell *)cell
atRow: (NSInteger)index;
- (NSString *) _keyPathForValueBinding;
@end
@interface NSTreeNode (Private_NSOutlineView)
- (void) _setParentNode: (NSTreeNode*)parentNode;
@end
@implementation NSTreeNode (Private_NSOutlineView)
- (void) _setParentNode: (NSTreeNode*)parentNode
{
_parentNode = parentNode;
}
@end
@implementation NSOutlineView
// Initialize the class when it is loaded
@ -170,6 +194,12 @@ static NSImage *unexpandable = nil;
unexpandable = [[NSImage alloc] initWithSize: [expanded size]];
#endif
autoExpanded = [NSMutableSet new];
// Bindings..
[self exposeBinding: NSContentBinding];
[self exposeBinding: NSContentArrayBinding];
[self exposeBinding: NSSelectionIndexesBinding];
[self exposeBinding: NSSortDescriptorsBinding];
}
}
@ -440,11 +470,57 @@ static NSImage *unexpandable = nil;
*/
- (BOOL) isExpandable: (id)item
{
if (item == nil)
BOOL result = NO;
GSKeyValueBinding *theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];
if (theBinding != nil)
{
return NO;
BOOL leaf = YES;
id observedObject = [theBinding observedObject];
NSTreeController *tc = (NSTreeController *)observedObject;
NSString *leafKeyPath = [tc leafKeyPathForNode: item];
if (leafKeyPath == nil)
{
NSString *countKeyPath = [tc countKeyPathForNode: item];
if (countKeyPath == nil)
{
NSString *childrenKeyPath = [tc childrenKeyPathForNode: item];
if (childrenKeyPath == nil)
{
result = NO;
}
else
{
id children = [item valueForKeyPath: childrenKeyPath];
leaf = ([children count] > 0);
}
}
else
{
NSNumber *countValue = [item valueForKeyPath: countKeyPath];
leaf = ([countValue integerValue] > 0);
}
}
else
{
NSNumber *leafValue = [item valueForKeyPath: leafKeyPath];
leaf = [leafValue boolValue];
}
result = !leaf; // if item is a leaf, it's not expandable...
}
return [_dataSource outlineView: self isItemExpandable: item];
else if (item != nil)
{
result = [_dataSource outlineView: self isItemExpandable: item];
}
return result;
}
/**
@ -700,21 +776,34 @@ static NSImage *unexpandable = nil;
*/
- (void) setDataSource: (id)anObject
{
GSKeyValueBinding *theBinding;
#define CHECK_REQUIRED_METHOD(selector_name) \
if (anObject && ![anObject respondsToSelector: @selector(selector_name)]) \
[NSException raise: NSInternalInconsistencyException \
format: @"data source does not respond to %@", @#selector_name]
CHECK_REQUIRED_METHOD(outlineView:child:ofItem:);
CHECK_REQUIRED_METHOD(outlineView:isItemExpandable:);
CHECK_REQUIRED_METHOD(outlineView:numberOfChildrenOfItem:);
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];
if (theBinding == nil)
{
CHECK_REQUIRED_METHOD(outlineView:child:ofItem:);
CHECK_REQUIRED_METHOD(outlineView:isItemExpandable:);
CHECK_REQUIRED_METHOD(outlineView:numberOfChildrenOfItem:);
// This method is @optional in NSOutlineViewDataSource as of macOS10.0
// CHECK_REQUIRED_METHOD(outlineView:objectValueForTableColumn:byItem:);
// This method is @optional in NSOutlineViewDataSource as of macOS10.0
// CHECK_REQUIRED_METHOD(outlineView:objectValueForTableColumn:byItem:);
// Is the data source editable?
_dataSource_editable = [anObject respondsToSelector:
@selector(outlineView:setObjectValue:forTableColumn:byItem:)];
// Is the data source editable?
_dataSource_editable = [anObject respondsToSelector:
@selector(outlineView:setObjectValue:forTableColumn:byItem:)];
}
else
{
/* Based on testing on macOS, this should default to YES if there is a binding...
*/
_dataSource_editable = YES;
}
/* We do *not* retain the dataSource, it's like a delegate */
_dataSource = anObject;
@ -956,6 +1045,16 @@ static NSImage *unexpandable = nil;
*/
- (void) drawRow: (NSInteger)rowIndex clipRect: (NSRect)aRect
{
GSKeyValueBinding *theBinding = nil;
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];
if (_dataSource == nil && theBinding == nil)
{
return;
}
if (_viewBased)
{
[self _drawCellViewRow: rowIndex
@ -1642,6 +1741,85 @@ Also returns the child index relative to this parent. */
@end /* implementation of NSOutlineView */
@implementation NSOutlineView (NotificationRequestMethods)
- (NSIndexPath *) _findIndexPathForItem: (id)item
parentItem: (id)pItem
{
id parentItem = (pItem == nil) ? (id)[NSNull null] : (id)pItem;
NSArray *children = NSMapGet(_itemDict, parentItem);
NSInteger childCount = [children count];
NSInteger index = 0;
for (index = 0; index < childCount; index++)
{
id childItem = [children objectAtIndex: index];
if (childItem == item)
{
return [NSIndexPath indexPathWithIndex: index];
}
else
{
NSIndexPath *foundPath = [self _findIndexPathForItem: item
parentItem: childItem];
if (foundPath != nil)
{
NSIndexPath *newPath = [NSIndexPath indexPathWithIndex: index];
NSUInteger length = [foundPath length];
NSUInteger indexes[length + 1];
NSUInteger i = 0;
[foundPath getIndexes: indexes];
// Iterate over existing indexes...
for (i = 0; i < length; i++)
{
newPath = [newPath indexPathByAddingIndex: indexes[i]];
}
return newPath;
}
}
}
return nil;
}
- (NSIndexPath *) _indexPathForItem: (id)item
{
return [self _findIndexPathForItem: item
parentItem: nil];
}
- (NSArray *) _indexPathsFromSelectedRows
{
NSUInteger index = [_selectedRows firstIndex];
NSMutableArray *result = [[NSMutableArray alloc] init];
// Regenerate the array...
while (index != NSNotFound)
{
id item = [_items objectAtIndex: index];
NSIndexPath *path = nil;
if ([item respondsToSelector: @selector(indexPath)])
{
path = [item indexPath];
}
else
{
path = [self _indexPathForItem: item];
}
[result addObject: path];
index = [_selectedRows indexGreaterThanIndex: index];
}
return result;
}
/*
* (NotificationRequestMethods)
*/
@ -1651,12 +1829,38 @@ Also returns the child index relative to this parent. */
NSOutlineViewSelectionIsChangingNotification
object: self];
}
- (void) _postSelectionDidChangeNotification
{
[nc postNotificationName:
NSOutlineViewSelectionDidChangeNotification
object: self];
NSTableColumn *tb = [_tableColumns objectAtIndex: 0];
GSKeyValueBinding *theBinding;
theBinding = [GSKeyValueBinding getBinding: NSValueBinding
forObject: tb];
// If there is a binding, send the indexes back
if (theBinding != nil)
{
id observedObject = [theBinding observedObject];
// Set the selection indexes on the controller...
theBinding = [GSKeyValueBinding getBinding: NSSelectionIndexPathsBinding
forObject: observedObject];
if (theBinding != nil)
{
NSArray *paths = [self _indexPathsFromSelectedRows];
if ([observedObject respondsToSelector: @selector(setSelectionIndexPaths:)])
{
[observedObject setSelectionIndexPaths: paths];
}
[theBinding reverseSetValue: paths];
}
}
[nc postNotificationName: NSOutlineViewSelectionDidChangeNotification
object: self];
}
- (void) _postColumnDidMoveNotificationWithOldIndex: (NSInteger) oldIndex
newIndex: (NSInteger) newIndex
{
@ -1766,10 +1970,13 @@ Also returns the child index relative to this parent. */
return YES;
}
- (void) _willDisplayCell: (NSCell*)cell
- (void) _willDisplayCell: (NSCell *)cell
forTableColumn: (NSTableColumn *)tb
row: (NSInteger)index
{
[tb _applyBindingsToCell: cell
atRow: index];
if (_del_responds)
{
id item = [self itemAtRow: index];
@ -1814,15 +2021,24 @@ Also returns the child index relative to this parent. */
row: (NSInteger) index
{
id result = nil;
NSString *keyPath = [tb _keyPathForValueBinding];
if ([_dataSource respondsToSelector:
@selector(outlineView:objectValueForTableColumn:byItem:)])
if (keyPath != nil)
{
id item = [self itemAtRow: index];
id theItem = [_items objectAtIndex: index];
result = [theItem valueForKeyPath: keyPath];
}
else
{
if ([_dataSource respondsToSelector:
@selector(outlineView:objectValueForTableColumn:byItem:)])
{
id item = [self itemAtRow: index];
result = [_dataSource outlineView: self
objectValueForTableColumn: tb
byItem: item];
result = [_dataSource outlineView: self
objectValueForTableColumn: tb
byItem: item];
}
}
return result;
@ -1832,15 +2048,30 @@ Also returns the child index relative to this parent. */
forTableColumn: (NSTableColumn *)tb
row: (NSInteger) index
{
if ([_dataSource respondsToSelector:
@selector(outlineView:setObjectValue:forTableColumn:byItem:)])
{
id item = [self itemAtRow: index];
NSString *keyPath = [tb _keyPathForValueBinding];
[_dataSource outlineView: self
setObjectValue: value
forTableColumn: tb
byItem: item];
// If we have content binding the data source is used only
// like a delegate
if (keyPath != nil)
{
id theItem = [_items objectAtIndex: index];
// Set the value on the keyPath.
[theItem setValue: value
forKeyPath: keyPath];
}
else
{
if ([_dataSource respondsToSelector:
@selector(outlineView:setObjectValue:forTableColumn:byItem:)])
{
id item = [self itemAtRow: index];
[_dataSource outlineView: self
setObjectValue: value
forTableColumn: tb
byItem: item];
}
}
}
@ -1953,43 +2184,131 @@ Also returns the child index relative to this parent. */
- (void) _loadDictionaryStartingWith: (id) startitem
atLevel: (NSInteger) level
{
GSKeyValueBinding *theBinding = nil;
NSInteger num = 0;
NSInteger i = 0;
id sitem = (startitem == nil) ? (id)[NSNull null] : (id)startitem;
NSMutableArray *anarray = nil;
/* Check to see if item is expandable and expanded before getting the number
* of items. For macos compatibility the topmost item (startitem==nil)
* is always considered expandable and must not be checked.
* We must load the item only if expanded, otherwise an outline view is not
* usable with a big tree structure. For example, an outline view to browse
* file system would try to traverse every file/directory on -reloadData.
*/
if ((startitem == nil
|| [_dataSource outlineView: self isItemExpandable: startitem])
&& [self isItemExpanded: startitem])
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
forObject: self];
if (theBinding != nil)
{
num = [_dataSource outlineView: self
numberOfChildrenOfItem: startitem];
id observedObject = [theBinding observedObject];
NSTreeController *tc = (NSTreeController *)observedObject;
NSArray *children = nil;
/* If there is a binding present, then allow it to be editable
* by default as editability of cells is determined in the
* NSTableColumn class based on the binding there for the
* editable property as defined in IB.
*/
_dataSource_editable = YES;
/* Implement logic to build the internal data structure here using
* bindings...
*/
if ([observedObject isKindOfClass: [NSTreeController class]])
{
if (startitem == nil)
{
NSTreeNode *node = (NSTreeNode *)[theBinding destinationValue];
/* Per the documentation 10.4/5+ uses NSTreeNode as the return value for
* the contents of this tree node consists of a dictionary with a single
* key of "children". This is per the tests for this at
* https://github.com/gcasa/NSTreeController_test. Specifically it returns
* _NSControllerTreeProxy. The equivalent of that class in GNUstep is
* GSControllerTreeProxy.
*/
children = [node mutableChildNodes];
num = [children count];
}
else
{
/* Per the documentation in NSTreeController, we can determine everything
* from whether there are children present on a given node. See
* the documentation for NSTreeController for more info.
*/
if ([self isExpandable: startitem]
&& [self isItemExpanded: startitem])
{
NSString *childrenKeyPath = [tc childrenKeyPathForNode: startitem];
if (childrenKeyPath != nil)
{
NSString *countKeyPath = [tc countKeyPathForNode: startitem];
children = [sitem valueForKeyPath: childrenKeyPath];
if (countKeyPath == nil)
{
num = [children count]; // get the count directly...
}
else
{
NSNumber *countValue = [sitem valueForKeyPath: countKeyPath];
num = [countValue integerValue];
}
}
}
}
if (num > 0)
{
anarray = [NSMutableArray arrayWithCapacity: num];
NSMapInsert(_itemDict, sitem, anarray);
}
NSMapInsert(_levelOfItems, sitem, [NSNumber numberWithInteger: level]);
for (i = 0; i < num; i++)
{
id anitem = [children objectAtIndex: i];
if ([anitem respondsToSelector: @selector(_setParentNode:)])
{
[anitem _setParentNode: startitem];
}
[anarray addObject: anitem];
[self _loadDictionaryStartingWith: anitem
atLevel: level + 1];
}
}
}
if (num > 0)
else
{
anarray = [NSMutableArray array];
NSMapInsert(_itemDict, sitem, anarray);
}
/* Check to see if item is expandable and expanded before getting the number
* of items. For macos compatibility the topmost item (startitem==nil)
* is always considered expandable and must not be checked.
* We must load the item only if expanded, otherwise an outline view is not
* usable with a big tree structure. For example, an outline view to browse
* file system would try to traverse every file/directory on -reloadData.
*/
if (startitem == nil
|| ([self isExpandable: startitem]
&& [self isItemExpanded: startitem]))
{
num = [_dataSource outlineView: self
numberOfChildrenOfItem: startitem];
}
NSMapInsert(_levelOfItems, sitem, [NSNumber numberWithInteger: level]);
if (num > 0)
{
anarray = [NSMutableArray arrayWithCapacity: num];
NSMapInsert(_itemDict, sitem, anarray);
}
for (i = 0; i < num; i++)
{
id anitem = [_dataSource outlineView: self
child: i
ofItem: startitem];
NSMapInsert(_levelOfItems, sitem, [NSNumber numberWithInteger: level]);
[anarray addObject: anitem];
[self _loadDictionaryStartingWith: anitem
atLevel: level + 1];
for (i = 0; i < num; i++)
{
id anitem = [_dataSource outlineView: self
child: i
ofItem: startitem];
[anarray addObject: anitem];
[self _loadDictionaryStartingWith: anitem
atLevel: level + 1];
}
}
}
@ -2226,7 +2545,7 @@ Also returns the child index relative to this parent. */
drawingRect: drawingRect
rowIndex: row];
}
if (view == nil
&& flag == YES)
{

View file

@ -97,6 +97,10 @@
[self setVersion: 4];
[self exposeBinding: NSValueBinding];
[self exposeBinding: NSEnabledBinding];
[self exposeBinding: NSEditableBinding];
[self exposeBinding: NSFontBinding];
[self exposeBinding: NSFontNameBinding];
[self exposeBinding: NSFontSizeBinding];
}
}
@ -650,6 +654,89 @@ to YES. */
return [_headerCell stringValue];
}
- (NSString *) _keyPathForValueBinding
{
NSString *keyPath = nil;
NSDictionary *info = [GSKeyValueBinding infoForBinding: NSValueBinding
forObject: self];
if (info != nil)
{
NSString *ikp = [info objectForKey: NSObservedKeyPathKey];
NSUInteger location = [ikp rangeOfString: @"."].location;
keyPath = (location == NSNotFound ? ikp : [ikp substringFromIndex: location + 1]);
}
return keyPath;
}
- (void) _applyBindingsToCell: (NSCell *)cell
atRow: (NSInteger)index
{
GSKeyValueBinding *theBinding = nil;
NSFont *font = nil;
[cell setEditable: _is_editable];
theBinding = [GSKeyValueBinding getBinding: NSEnabledBinding
forObject: self];
if (theBinding != nil)
{
id result = nil;
BOOL flag = NO;
result = [(NSArray *)[theBinding destinationValue]
objectAtIndex: index];
flag = [result boolValue];
[cell setEnabled: flag];
}
/* Font bindings... According to Apple documentation, if the
* font binding is available, then name, size, and other
* font related bindings are ignored. Otherwise they are
* used
*/
theBinding = [GSKeyValueBinding getBinding: NSFontBinding
forObject: self];
if (theBinding != nil)
{
font = [(NSArray *)[theBinding destinationValue]
objectAtIndex: index];
}
else
{
NSString *fontName = nil;
CGFloat fontSize = 0.0;
theBinding = [GSKeyValueBinding getBinding: NSFontNameBinding
forObject: self];
if (theBinding != nil)
{
fontName = [(NSArray *)[theBinding destinationValue]
objectAtIndex: index];
}
if (fontName != nil)
{
theBinding = [GSKeyValueBinding getBinding: NSFontSizeBinding
forObject: self];
if (theBinding != nil)
{
id num = [(NSArray *)[theBinding destinationValue]
objectAtIndex: index];
fontSize = [num doubleValue];
}
font = [NSFont fontWithName: fontName
size: fontSize];
}
}
if (font != nil)
{
[cell setFont: font];
}
}
- (void) setValue: (id)anObject forKey: (NSString*)aKey
{
if ([aKey isEqual: NSValueBinding])
@ -661,6 +748,13 @@ to YES. */
{
// FIXME
}
else if ([aKey isEqual: NSEditableBinding])
{
if ([anObject isKindOfClass: [NSNumber class]])
{
_is_editable = [anObject boolValue];
}
}
else
{
[super setValue: anObject forKey: aKey];
@ -678,6 +772,10 @@ to YES. */
// FIXME
return [NSNumber numberWithBool: YES];
}
else if ([aKey isEqual: NSEditableBinding])
{
return [NSNumber numberWithBool: _is_editable];
}
else
{
return [super valueForKey: aKey];

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,13 @@
/*
NSTreeController.h
/*
NSTreeController.m
The tree controller class.
Copyright (C) 2012 Free Software Foundation, Inc.
Copyright (C) 2012, 2024 Free Software Foundation, Inc.
Author: Gregory Casamento <greg.casamento@gmail.com>
Date: 2012
Date: 2012, 2024
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
@ -22,55 +22,77 @@
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
/*
NSTreeController.h
The tree controller class.
Copyright (C) 2012 Free Software Foundation, Inc.
Author: Gregory Casamento <greg.casamento@gmail.com>
Date: 2012
This file is part of the GNUstep GUI Library.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; see the file COPYING.LIB.
If not, see <http://www.gnu.org/licenses/> or write to the
Free Software Foundation, 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
*/
#import <Foundation/NSArchiver.h>
#import <Foundation/NSArray.h>
#import <Foundation/NSDictionary.h>
#import <Foundation/NSIndexPath.h>
#import <Foundation/NSKeyedArchiver.h>
#import <Foundation/NSKeyValueCoding.h>
#import <Foundation/NSKeyValueObserving.h>
#import <Foundation/NSNotification.h>
#import <Foundation/NSString.h>
#import <Foundation/NSSortDescriptor.h>
#import <AppKit/NSTreeController.h>
#import <AppKit/NSTreeNode.h>
#import "AppKit/NSOutlineView.h"
#import "AppKit/NSKeyValueBinding.h"
#import "AppKit/NSTreeController.h"
#import "AppKit/NSTreeNode.h"
#import "GSBindingHelpers.h"
#import "GSFastEnumeration.h"
#import "GSControllerTreeProxy.h"
@implementation NSTreeController
+ (void) initialize
{
if (self == [NSTreeController class])
{
[self exposeBinding: NSContentArrayBinding];
[self exposeBinding: NSContentBinding];
[self exposeBinding: NSSelectionIndexPathsBinding];
[self setKeys: [NSArray arrayWithObjects: NSContentBinding, NSContentObjectBinding, nil]
triggerChangeNotificationsForDependentKey: @"arrangedObjects"];
}
}
- (void) _initDefaults
{
_childrenKeyPath = nil;
_countKeyPath = nil;
_leafKeyPath = nil;
_sortDescriptors = nil;
_selection_index_paths = [[NSMutableArray alloc] init];
_canInsert = YES;
_canInsertChild = YES;
_canAddChild = YES;
[self setObjectClass: [NSMutableDictionary class]];
}
- (id) initWithContent: (id)content
{
if ((self = [super initWithContent: content]) != nil)
{
[self _initDefaults];
}
return self;
}
- (id) init
{
NSMutableArray *array = [[NSMutableArray alloc] init];
self = [self initWithContent: array];
RELEASE(array);
return self;
}
@ -80,13 +102,22 @@
RELEASE(_countKeyPath);
RELEASE(_leafKeyPath);
RELEASE(_sortDescriptors);
RELEASE(_arranged_objects);
RELEASE(_selection_index_paths);
[super dealloc];
}
- (BOOL) addSelectionIndexPaths: (NSArray*)indexPaths
- (BOOL) addSelectionIndexPaths: (NSArray *)indexPaths
{
// FIXME
return NO;
BOOL f = [self commitEditing];
if (YES == f)
{
[_selection_index_paths addObjectsFromArray: indexPaths];
}
return f;
}
- (BOOL) alwaysUsesMultipleValuesMarker
@ -99,22 +130,19 @@
return _avoidsEmptySelection;
}
- (BOOL) canAddChid
- (BOOL) canAddChild
{
// FIXME
return NO;
return _canAddChild;
}
- (BOOL) canInsert
{
// FIXME
return NO;
return _canInsert;
}
- (BOOL) canInsertChild
{
// FIXME
return NO;
return _canInsertChild;
}
- (BOOL) preservesSelection
@ -127,123 +155,248 @@
return _selectsInsertedObjects;
}
- (BOOL) setSelectionIndexPath: (NSIndexPath*)indexPath
- (BOOL) setSelectionIndexPath: (NSIndexPath *)indexPath
{
// FIXME
return NO;
BOOL f = [self commitEditing];
if (YES == f)
{
NSMutableArray *mutable_index_paths = [NSMutableArray arrayWithObject: indexPath];
ASSIGN(_selection_index_paths, mutable_index_paths);
}
return f;
}
- (BOOL) setSelectionIndexPaths: (NSArray*)indexPaths
- (BOOL) setSelectionIndexPaths: (NSArray *)indexPaths
{
// FIXME
return NO;
BOOL f = [self commitEditing];
if (YES == f)
{
NSMutableArray *mutable_index_paths = [NSMutableArray arrayWithArray: indexPaths];
ASSIGN(_selection_index_paths, mutable_index_paths);
}
return f;
}
- (id) arrangedObjects
- (NSTreeNode *) arrangedObjects
{
// FIXME
return nil;
}
- (id) content
{
// FIXME
return [super content];
}
- (NSArray*) selectedObjects
{
// FIXME
return [super selectedObjects];
}
- (NSIndexPath*) selectionIndexPath
{
// FIXME
return nil;
}
- (NSArray*) selectionIndexPaths
{
// FIXME
return nil;
}
- (NSArray*) sortDescriptors
{
return _sortDescriptors;
}
- (NSString*) childrenKeyPath
{
return _childrenKeyPath;
}
- (NSString*) countKeyPath
{
return _countKeyPath;;
}
- (NSString*) leafKeyPath
{
return _leafKeyPath;
}
- (void) addChild: (id)sender
{
// FIXME
}
- (void) add: (id)sender
{
// FIXME
[super add: sender];
}
- (void) insertChild: (id)sender
{
// FIXME
}
- (void) insertObject: (id)object atArrangedObjectIndexPath: (NSIndexPath*)indexPath
{
// FIXME
}
- (void) insertObjects: (NSArray*)objects atArrangedObjectIndexPaths: (NSArray*)indexPaths
{
// FIXME
}
- (void) insert: (id)sender
{
// FIXME
if (_arranged_objects == nil)
{
[self rearrangeObjects];
}
return _arranged_objects;
}
- (void) rearrangeObjects
{
// FIXME
[self willChangeValueForKey: @"arrangedObjects"];
DESTROY(_arranged_objects);
if ([_content isKindOfClass: [NSArray class]])
{
_arranged_objects = [[GSControllerTreeProxy alloc] initWithContent: _content
withController: self];
}
[self didChangeValueForKey: @"arrangedObjects"];
}
- (void) removeObjectAtArrangedObjectIndexPath: (NSIndexPath*)indexPath
- (id) _objectAtIndexPath: (NSIndexPath *)indexPath
{
// FIXME
NSUInteger length = [indexPath length];
NSUInteger pos = 0;
NSMutableArray *children = [_arranged_objects mutableChildNodes];
NSUInteger lastIndex = 0;
id obj = nil;
for (pos = 0; pos < length - 1; pos++)
{
NSUInteger i = [indexPath indexAtPosition: pos];
id node = [children objectAtIndex: i];
children = [node valueForKeyPath: _childrenKeyPath];
}
lastIndex = [indexPath indexAtPosition: length - 1];
obj = [children objectAtIndex: lastIndex];
return obj;
}
- (void) removeObjectsAtArrangedObjectIndexPaths: (NSArray*)indexPaths
- (NSArray *) selectedObjects
{
// FIXME
NSMutableArray *selectedObjects = [NSMutableArray array];
FOR_IN(NSIndexPath*, path, _selection_index_paths)
{
id obj = [self _objectAtIndexPath: path];
[selectedObjects addObject: obj];
}
END_FOR_IN(_selection_index_paths);
return selectedObjects;
}
- (void) removeSelectionIndexPaths: (NSArray*)indexPaths
- (NSIndexPath *) selectionIndexPath
{
// FIXME
if ([_selection_index_paths count] > 0)
{
return [_selection_index_paths objectAtIndex: 0];
}
return nil;
}
- (void) remove: (id)sender
- (NSArray *) selectionIndexPaths
{
// FIXME
[super remove: sender];
return [_selection_index_paths copy];
}
- (NSArray *) sortDescriptors
{
return [_sortDescriptors copy];
}
- (NSString *) childrenKeyPath
{
return _childrenKeyPath;
}
- (NSString *) countKeyPath
{
return _countKeyPath;
}
- (NSString *) leafKeyPath
{
return _leafKeyPath;
}
- (IBAction) add: (id)sender
{
NSIndexPath *p = [NSIndexPath indexPathWithIndex: 0];
id newObject = [self newObject];
[self insertObject: newObject atArrangedObjectIndexPath: p];
RELEASE(newObject);
}
- (IBAction) addChild: (id)sender
{
NSIndexPath *p = [self selectionIndexPath];
id newObject = [self newObject];
if (p != nil)
{
[self insertObject: newObject atArrangedObjectIndexPath: p];
RELEASE(newObject);
}
}
- (IBAction) remove: (id)sender
{
if ([self canRemove]
&& [self countKeyPath] == nil)
{
if ([_selection_index_paths count] > 0)
{
NSIndexPath *p = [self selectionIndexPath];
[self removeObjectAtArrangedObjectIndexPath: p];
}
}
}
- (IBAction) insertChild: (id)sender
{
[self addChild: sender];
}
- (void) insertObject: (id)object atArrangedObjectIndexPath: (NSIndexPath *)indexPath
{
if ([self canAddChild]
&& [self countKeyPath] == nil)
{
NSUInteger length = [indexPath length];
NSUInteger pos = 0;
NSMutableArray *children = [_arranged_objects mutableChildNodes];
for (pos = 0; pos < length - 1; pos++)
{
NSUInteger i = [indexPath indexAtPosition: pos];
id node = [children objectAtIndex: i];
children = [node valueForKeyPath: _childrenKeyPath];
}
[children addObject: object];
[self rearrangeObjects];
}
}
- (void) insertObjects: (NSArray *)objects atArrangedObjectIndexPaths: (NSArray *)indexPaths
{
if ([self canAddChild]
&& [self countKeyPath] == nil)
{
if ([objects count] != [indexPaths count])
{
return;
}
else
{
NSUInteger i = 0;
FOR_IN(id, object, objects)
{
NSIndexPath *indexPath = [indexPaths objectAtIndex: i];
[self insertObject: object atArrangedObjectIndexPath: indexPath];
i++;
}
END_FOR_IN(objects);
}
}
}
- (IBAction) insert: (id)sender
{
[self addChild: sender];
}
- (void) removeObjectAtArrangedObjectIndexPath: (NSIndexPath *)indexPath
{
NSUInteger length = [indexPath length];
NSUInteger pos = 0;
NSMutableArray *children = [_arranged_objects mutableChildNodes];
NSUInteger lastIndex = 0;
for (pos = 0; pos < length - 1; pos++)
{
NSUInteger i = [indexPath indexAtPosition: pos];
id node = [children objectAtIndex: i];
children = [node valueForKeyPath: _childrenKeyPath];
}
lastIndex = [indexPath indexAtPosition: length - 1];
[children removeObjectAtIndex: lastIndex];
[self rearrangeObjects];
}
- (void) removeObjectsAtArrangedObjectIndexPaths: (NSArray *)indexPaths
{
FOR_IN(NSIndexPath*, indexPath, indexPaths)
{
[self removeObjectAtArrangedObjectIndexPath: indexPath];
}
END_FOR_IN(indexPaths);
}
- (void) removeSelectionIndexPaths: (NSArray *)indexPaths
{
[self removeObjectsAtArrangedObjectIndexPaths: indexPaths];
}
- (void) setAlwaysUsesMultipleValuesMarker: (BOOL)flag
@ -256,23 +409,23 @@
_avoidsEmptySelection = flag;
}
- (void) setChildrenKeyPath: (NSString*)path
- (void) setChildrenKeyPath: (NSString *)path
{
ASSIGN(_childrenKeyPath, path);
}
- (void) setContent: (id)content
{
// FIXME
[super setContent: content];
[self rearrangeObjects];
}
- (void) setCountKeyPath: (NSString*)path
- (void) setCountKeyPath: (NSString *)path
{
ASSIGN(_countKeyPath, path);
}
- (void) setLeafPathKey: (NSString*)key
- (void) setLeafKeyPath: (NSString *)key
{
ASSIGN(_leafKeyPath, key);
}
@ -287,58 +440,204 @@
_selectsInsertedObjects = flag;
}
- (void) setSortDescriptors: (NSArray*)descriptors
- (void) setSortDescriptors: (NSArray *)descriptors
{
ASSIGN(_sortDescriptors, descriptors);
}
- (NSString*) childrenKeyPathForNode: (NSTreeNode*)node
- (NSString *) childrenKeyPathForNode: (NSTreeNode *)node
{
// FIXME
return nil;
return _childrenKeyPath;
}
- (NSString*) countKeyPathForNode: (NSTreeNode*)node
- (NSString *) countKeyPathForNode: (NSTreeNode *)node
{
// FIXME
return nil;
return _countKeyPath;
}
- (NSString*) leafKeyPathForNode: (NSTreeNode*)node
- (NSString *) leafKeyPathForNode: (NSTreeNode *)node
{
// FIXME
return nil;
return _leafKeyPath;
}
- (void) moveNode: (NSTreeNode*)node toIndexPath: (NSIndexPath*)indexPath
- (void) moveNode: (NSTreeNode *)node toIndexPath: (NSIndexPath *)indexPath
{
// FIXME
}
- (void) moveNodes: (NSArray*)nodes toIndexPath: (NSIndexPath*)startingIndexPath
- (void) moveNodes: (NSArray *)nodes toIndexPath: (NSIndexPath *)startingIndexPath
{
// FIXME
}
- (NSArray*) selectedNodes
- (NSArray *) selectedNodes
{
// FIXME
return nil;
return [self selectedObjects];
}
- (id) initWithCoder: (NSCoder*)coder
- (void) bind: (NSString *)binding
toObject: (id)anObject
withKeyPath: (NSString *)keyPath
options: (NSDictionary *)options
{
if ([binding isEqual: NSContentArrayBinding])
{
GSKeyValueBinding *kvb;
[self unbind: binding];
kvb = [[GSKeyValueBinding alloc] initWithBinding: @"content"
withName: binding
toObject: anObject
withKeyPath: keyPath
options: options
fromObject: self];
// The binding will be retained in the binding table
RELEASE(kvb);
}
else
{
[super bind: binding
toObject: anObject
withKeyPath: keyPath
options: options];
}
}
- (id) initWithCoder: (NSCoder *)coder
{
self = [super initWithCoder: coder];
if (self != nil)
{
[self _initDefaults]; // set up default values...
if ([coder allowsKeyedCoding])
{
// These names do not stick to convention. Usually it would be
// NS* or NSTreeController* so they must be overriden in
// GSXib5KeyedUnarchver.
if ([coder containsValueForKey: @"NSTreeContentChildrenKey"])
{
[self setChildrenKeyPath:
[coder decodeObjectForKey: @"NSTreeContentChildrenKey"]];
}
if ([coder containsValueForKey: @"NSTreeContentCountKey"])
{
[self setCountKeyPath:
[coder decodeObjectForKey: @"NSTreeContentCountKey"]];
}
if ([coder containsValueForKey: @"NSTreeContentLeafKey"])
{
[self setLeafKeyPath:
[coder decodeObjectForKey: @"NSTreeContentLeafKey"]];
}
// Since we don't inherit from NSArrayController these are decoded here
// as well.
if ([coder containsValueForKey: @"NSAvoidsEmptySelection"])
{
[self setAvoidsEmptySelection:
[coder decodeBoolForKey: @"NSAvoidsEmptySelection"]];
}
if ([coder containsValueForKey: @"NSPreservesSelection"])
{
[self setPreservesSelection:
[coder decodeBoolForKey: @"NSPreservesSelection"]];
}
if ([coder containsValueForKey: @"NSSelectsInsertedObjects"])
{
[self setSelectsInsertedObjects:
[coder decodeBoolForKey: @"NSSelectsInsertedObjects"]];
}
}
}
else
{
id obj = nil;
BOOL f = NO;
obj = [coder decodeObject];
[self setChildrenKeyPath: obj];
obj = [coder decodeObject];
[self setCountKeyPath: obj];
obj = [coder decodeObject];
[self setLeafKeyPath: obj];
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
[self setAvoidsEmptySelection: f];
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
[self setPreservesSelection: f];
[coder decodeValueOfObjCType: @encode(BOOL)
at: &f];
[self setSelectsInsertedObjects: f];
}
return self;
}
- (void) encodeWithCoder: (NSCoder*)coder
- (void) encodeWithCoder: (NSCoder *)coder
{
// Do nothing...
[super encodeWithCoder: coder];
if ([coder allowsKeyedCoding])
{
[coder encodeObject: _childrenKeyPath
forKey: @"NSTreeContentChildrenKey"];
[coder encodeObject: _countKeyPath
forKey: @"NSTreeContentCountKey"];
[coder encodeObject: _leafKeyPath
forKey: @"NSTreeContentLeafKey"];
[coder encodeBool: _avoidsEmptySelection
forKey: @"NSAvoidsEmptySelection"];
[coder encodeBool: _preservesSelection
forKey: @"NSPreservesSelection"];
[coder encodeBool: _selectsInsertedObjects
forKey: @"NSSelectsInsertedObjects"];
}
else
{
id obj = nil;
BOOL f = NO;
obj = [self childrenKeyPath];
[coder encodeObject: obj];
obj = [self countKeyPath];
[coder encodeObject: obj];
obj = [self leafKeyPath];
[coder encodeObject: obj];
f = [self avoidsEmptySelection];
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = [self preservesSelection];
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
f = [self selectsInsertedObjects];
[coder encodeValueOfObjCType: @encode(BOOL)
at: &f];
}
}
- (id) copyWithZone: (NSZone*)zone
- (id) copyWithZone: (NSZone *)zone
{
return [self retain];
id copy = [[NSTreeController allocWithZone: zone] initWithContent: [self content]];
if (copy != nil)
{
[copy setChildrenKeyPath: [self childrenKeyPath]];
[copy setCountKeyPath: [self countKeyPath]];
[copy setLeafKeyPath: [self leafKeyPath]];
[copy setAvoidsEmptySelection: [self avoidsEmptySelection]];
[copy setPreservesSelection: [self preservesSelection]];
[copy setSelectsInsertedObjects: [self selectsInsertedObjects]];
}
return copy;
}
@end

View file

@ -32,7 +32,7 @@
#import <Foundation/NSString.h>
#import <Foundation/NSSortDescriptor.h>
#import <AppKit/NSTreeNode.h>
#import "AppKit/NSTreeNode.h"
@interface NSTreeNode (Private)
- (NSMutableArray*) _childNodes;
@ -159,7 +159,7 @@
NSIndexPath *path;
NSUInteger index;
index = [_parentNode->_childNodes indexOfObject: self];
index = [[_parentNode childNodes] indexOfObject: self];
path = [_parentNode indexPath];
if (path != nil)
{
@ -248,4 +248,10 @@
}
}
- (NSString *) description
{
return [NSString stringWithFormat: @"<%@> _representedObject = %@, _childNode = %@, _parentNode = %@",
[super description], _representedObject, _childNodes, _parentNode];
}
@end

View file

@ -711,6 +711,7 @@ APPKIT_DECLARE NSString *NSSelectedObjectBinding = @"selectedObject";
APPKIT_DECLARE NSString *NSSelectedTagBinding = @"selectedTag";
APPKIT_DECLARE NSString *NSSelectedValueBinding = @"selectedValue";
APPKIT_DECLARE NSString *NSSelectionIndexesBinding = @"selectionIndexes";
APPKIT_DECLARE NSString *NSSelectionIndexPathsBinding = @"selectionIndexPaths";
APPKIT_DECLARE NSString *NSSortDescriptorsBinding = @"sortDescriptors";
APPKIT_DECLARE NSString *NSTextColorBinding = @"textColor";
APPKIT_DECLARE NSString *NSTitleBinding = @"title";