2001-12-17 16:51:51 +00:00
|
|
|
/** <title>NSOutlineView</title>
|
2001-10-25 21:41:03 +00:00
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
<abstract>
|
|
|
|
This class is a subclass of NSTableView which provides the user with a way
|
2006-07-04 21:31:49 +00:00
|
|
|
to display tree structured data in an outline format.
|
|
|
|
It is particularly useful for show hierarchical data such as a
|
2015-11-02 16:35:02 +00:00
|
|
|
class inheritance tree or any other set of relationships.<br />
|
|
|
|
NB. While it its illegal to have the same item in the view more than once,
|
|
|
|
it is possible to have multiple equal items since tests for pointer
|
|
|
|
equality are used rather than calls to the -isEqual: method.
|
2003-10-18 21:15:25 +00:00
|
|
|
</abstract>
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
Copyright (C) 2001 Free Software Foundation, Inc.
|
|
|
|
|
|
|
|
Author: Gregory John Casamento <greg_casamento@yahoo.com>
|
|
|
|
Date: October 2001
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
This file is part of the GNUstep GUI Library.
|
|
|
|
|
|
|
|
This library is free software; you can redistribute it and/or
|
2007-10-29 21:16:17 +00:00
|
|
|
modify it under the terms of the GNU Lesser General Public
|
2001-10-25 21:41:03 +00:00
|
|
|
License as published by the Free Software Foundation; either
|
2008-06-10 04:01:49 +00:00
|
|
|
version 2 of the License, or (at your option) any later version.
|
2007-10-29 21:16:17 +00:00
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2008-03-07 23:51:55 +00:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
2007-10-29 21:16:17 +00:00
|
|
|
Lesser General Public License for more details.
|
2001-10-25 21:41:03 +00:00
|
|
|
|
2007-10-29 21:16:17 +00:00
|
|
|
You should have received a copy of the GNU Lesser General Public
|
2001-10-25 21:41:03 +00:00
|
|
|
License along with this library; see the file COPYING.LIB.
|
2009-11-30 18:56:51 +00:00
|
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
2007-10-29 21:16:17 +00:00
|
|
|
Boston, MA 02110-1301, USA.
|
2009-11-30 18:56:51 +00:00
|
|
|
*/
|
2001-10-25 21:41:03 +00:00
|
|
|
|
2010-03-31 08:14:50 +00:00
|
|
|
#import <Foundation/NSArray.h>
|
|
|
|
#import <Foundation/NSDictionary.h>
|
|
|
|
#import <Foundation/NSEnumerator.h>
|
|
|
|
#import <Foundation/NSException.h>
|
|
|
|
#import <Foundation/NSIndexSet.h>
|
|
|
|
#import <Foundation/NSMapTable.h>
|
|
|
|
#import <Foundation/NSNotification.h>
|
|
|
|
#import <Foundation/NSNull.h>
|
2010-04-10 17:48:46 +00:00
|
|
|
#import <Foundation/NSSet.h>
|
2010-03-31 08:14:50 +00:00
|
|
|
#import <Foundation/NSUserDefaults.h>
|
|
|
|
#import <Foundation/NSValue.h>
|
|
|
|
|
|
|
|
#import "AppKit/NSApplication.h"
|
|
|
|
#import "AppKit/NSBezierPath.h"
|
|
|
|
#import "AppKit/NSCell.h"
|
|
|
|
#import "AppKit/NSClipView.h"
|
|
|
|
#import "AppKit/NSColor.h"
|
|
|
|
#import "AppKit/NSEvent.h"
|
|
|
|
#import "AppKit/NSGraphics.h"
|
|
|
|
#import "AppKit/NSImage.h"
|
2024-06-19 05:46:27 +00:00
|
|
|
#import "AppKit/NSKeyValueBinding.h"
|
2010-03-31 08:14:50 +00:00
|
|
|
#import "AppKit/NSOutlineView.h"
|
|
|
|
#import "AppKit/NSScroller.h"
|
|
|
|
#import "AppKit/NSTableColumn.h"
|
|
|
|
#import "AppKit/NSTableHeaderView.h"
|
|
|
|
#import "AppKit/NSText.h"
|
|
|
|
#import "AppKit/NSTextFieldCell.h"
|
2024-06-19 05:46:27 +00:00
|
|
|
#import "AppKit/NSTreeController.h"
|
2024-07-19 12:04:13 +00:00
|
|
|
#import "AppKit/NSTreeNode.h"
|
2010-03-31 08:14:50 +00:00
|
|
|
#import "AppKit/NSWindow.h"
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2024-02-16 06:09:41 +00:00
|
|
|
#import "GNUstepGUI/GSTheme.h"
|
2024-06-19 05:46:27 +00:00
|
|
|
#import "GSBindingHelpers.h"
|
2024-08-03 18:31:51 +00:00
|
|
|
#import "GSFastEnumeration.h"
|
2011-07-12 21:12:22 +00:00
|
|
|
#import "GSGuiPrivate.h"
|
2024-06-19 05:46:27 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
2015-11-02 18:31:04 +00:00
|
|
|
static NSMapTableKeyCallBacks keyCallBacks;
|
2002-02-23 16:37:17 +00:00
|
|
|
static NSNotificationCenter *nc = nil;
|
|
|
|
static const int current_version = 1;
|
|
|
|
|
2016-10-08 21:17:53 +00:00
|
|
|
static NSInteger lastVerticalQuarterPosition;
|
|
|
|
static NSInteger lastHorizontalHalfPosition;
|
2012-03-09 08:03:45 +00:00
|
|
|
static NSDragOperation dragOperation;
|
2002-03-22 00:15:03 +00:00
|
|
|
|
|
|
|
static NSRect oldDraggingRect;
|
2009-11-30 18:56:51 +00:00
|
|
|
static id oldDropItem;
|
|
|
|
static id currentDropItem;
|
2016-10-08 21:17:53 +00:00
|
|
|
static NSInteger oldDropIndex;
|
|
|
|
static NSInteger currentDropIndex;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-11-29 18:02:06 +00:00
|
|
|
static NSMutableSet *autoExpanded = nil;
|
|
|
|
static NSDate *lastDragUpdate = nil;
|
|
|
|
static NSDate *lastDragChange = nil;
|
2002-03-22 00:15:03 +00:00
|
|
|
|
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
// Cache the arrow images...
|
2002-03-02 22:20:52 +00:00
|
|
|
static NSImage *collapsed = nil;
|
|
|
|
static NSImage *expanded = nil;
|
2002-03-03 05:58:51 +00:00
|
|
|
static NSImage *unexpandable = nil;
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2002-03-04 23:53:27 +00:00
|
|
|
@interface NSOutlineView (NotificationRequestMethods)
|
|
|
|
- (void) _postSelectionIsChangingNotification;
|
|
|
|
- (void) _postSelectionDidChangeNotification;
|
2016-10-08 21:17:53 +00:00
|
|
|
- (void) _postColumnDidMoveNotificationWithOldIndex: (NSInteger) oldIndex
|
2024-06-16 12:38:12 +00:00
|
|
|
newIndex: (NSInteger) newIndex;
|
2004-07-03 16:34:24 +00:00
|
|
|
// FIXME: There is a method with a similar name.but this is never called
|
|
|
|
//- (void) _postColumnDidResizeNotification;
|
2002-03-04 23:53:27 +00:00
|
|
|
- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn;
|
2016-10-08 21:17:53 +00:00
|
|
|
- (BOOL) _shouldSelectRow: (NSInteger)rowIndex;
|
2002-03-04 23:53:27 +00:00
|
|
|
- (BOOL) _shouldSelectionChange;
|
|
|
|
- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
|
2024-06-16 12:38:12 +00:00
|
|
|
row: (NSInteger) rowIndex;
|
2004-06-21 11:41:26 +00:00
|
|
|
- (void) _willDisplayCell: (NSCell*)cell
|
2024-06-16 12:38:12 +00:00
|
|
|
forTableColumn: (NSTableColumn *)tb
|
|
|
|
row: (NSInteger)index;
|
2007-12-02 20:43:32 +00:00
|
|
|
- (BOOL) _writeRows: (NSIndexSet *)rows
|
2004-07-03 16:34:24 +00:00
|
|
|
toPasteboard: (NSPasteboard *)pboard;
|
|
|
|
- (BOOL) _isDraggingSource;
|
|
|
|
- (id) _objectValueForTableColumn: (NSTableColumn *)tb
|
2024-06-16 12:38:12 +00:00
|
|
|
row: (NSInteger)index;
|
2002-07-11 04:52:33 +00:00
|
|
|
- (void) _setObjectValue: (id)value
|
2024-06-16 12:38:12 +00:00
|
|
|
forTableColumn: (NSTableColumn *)tb
|
|
|
|
row: (NSInteger) index;
|
2016-10-08 21:17:53 +00:00
|
|
|
- (NSInteger) _numRows;
|
2002-03-04 23:53:27 +00:00
|
|
|
@end
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2002-03-23 16:39:19 +00:00
|
|
|
// These methods are private...
|
2002-02-23 16:37:17 +00:00
|
|
|
@interface NSOutlineView (TableViewInternalPrivate)
|
2008-03-07 23:51:55 +00:00
|
|
|
- (void) _initOutlineDefaults;
|
2002-04-02 05:04:57 +00:00
|
|
|
- (void) _autosaveExpandedItems;
|
|
|
|
- (void) _autoloadExpandedItems;
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _collectItemsStartingWith: (id)startitem
|
2024-06-16 12:38:12 +00:00
|
|
|
into: (NSMutableArray *)allChildren;
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _loadDictionaryStartingWith: (id) startitem
|
2024-06-16 12:38:12 +00:00
|
|
|
atLevel: (NSInteger) level;
|
2002-02-23 16:37:17 +00:00
|
|
|
- (void) _openItem: (id)item;
|
|
|
|
- (void) _closeItem: (id)item;
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _removeChildren: (id)startitem;
|
2016-10-08 21:17:53 +00:00
|
|
|
- (void) _noteNumberOfRowsChangedBelowItem: (id)item by: (NSInteger)n;
|
2002-02-23 16:37:17 +00:00
|
|
|
@end
|
2001-10-25 21:41:03 +00:00
|
|
|
|
2024-04-28 13:49:19 +00:00
|
|
|
@interface NSOutlineView (Private)
|
2009-11-29 18:02:06 +00:00
|
|
|
- (void) _autoCollapse;
|
|
|
|
@end
|
|
|
|
|
2024-04-28 13:49:19 +00:00
|
|
|
@interface NSTableView (Private)
|
|
|
|
- (NSView *) _renderedViewForPath: (NSIndexPath *)path;
|
|
|
|
- (void) _setRenderedView: (NSView *)view forPath: (NSIndexPath *)path;
|
|
|
|
- (id) _prototypeCellViewFromTableColumn: (NSTableColumn *)tb;
|
2024-06-17 00:52:06 +00:00
|
|
|
- (void) _drawCellViewRow: (NSInteger)rowIndex
|
|
|
|
clipRect: (NSRect)clipRect;
|
2024-08-08 02:55:14 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@interface NSTableColumn (Private)
|
2024-07-14 20:00:18 +00:00
|
|
|
- (void) _applyBindingsToCell: (NSCell *)cell
|
2024-08-08 02:55:14 +00:00
|
|
|
atRow: (NSInteger)index;
|
2024-08-21 02:53:31 +00:00
|
|
|
- (NSString *) _keyPathForValueBinding;
|
2024-04-28 13:49:19 +00:00
|
|
|
@end
|
|
|
|
|
2024-08-02 15:20:58 +00:00
|
|
|
@interface NSTreeNode (Private_NSOutlineView)
|
|
|
|
- (void) _setParentNode: (NSTreeNode*)parentNode;
|
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSTreeNode (Private_NSOutlineView)
|
|
|
|
|
|
|
|
- (void) _setParentNode: (NSTreeNode*)parentNode
|
|
|
|
{
|
|
|
|
_parentNode = parentNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
2001-10-25 21:41:03 +00:00
|
|
|
@implementation NSOutlineView
|
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
// Initialize the class when it is loaded
|
|
|
|
+ (void) initialize
|
|
|
|
{
|
|
|
|
if (self == [NSOutlineView class])
|
|
|
|
{
|
|
|
|
[self setVersion: current_version];
|
|
|
|
nc = [NSNotificationCenter defaultCenter];
|
2015-11-02 18:31:04 +00:00
|
|
|
/* We need special map table callbacks, to check for identical
|
|
|
|
* objects rather than merely equal objects.
|
|
|
|
*/
|
|
|
|
keyCallBacks = NSObjectMapKeyCallBacks;
|
|
|
|
keyCallBacks.isEqual = NSOwnedPointerMapKeyCallBacks.isEqual;
|
2009-11-30 18:56:51 +00:00
|
|
|
#if 0
|
|
|
|
/* Old Interface Builder style. */
|
2006-09-26 18:29:11 +00:00
|
|
|
collapsed = [NSImage imageNamed: @"common_outlineCollapsed"];
|
|
|
|
expanded = [NSImage imageNamed: @"common_outlineExpanded"];
|
|
|
|
unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable"];
|
2009-11-30 18:56:51 +00:00
|
|
|
#else
|
|
|
|
/* Current OSX style images. */
|
|
|
|
// FIXME ... better ones?
|
|
|
|
collapsed = [NSImage imageNamed: @"common_ArrowRightH"];
|
|
|
|
expanded = [NSImage imageNamed: @"common_ArrowDownH"];
|
|
|
|
unexpandable = [[NSImage alloc] initWithSize: [expanded size]];
|
|
|
|
#endif
|
2009-11-29 18:02:06 +00:00
|
|
|
autoExpanded = [NSMutableSet new];
|
2024-06-19 05:46:27 +00:00
|
|
|
|
|
|
|
// Bindings..
|
|
|
|
[self exposeBinding: NSContentBinding];
|
2024-07-07 02:02:10 +00:00
|
|
|
[self exposeBinding: NSContentArrayBinding];
|
2024-06-19 05:46:27 +00:00
|
|
|
[self exposeBinding: NSSelectionIndexesBinding];
|
|
|
|
[self exposeBinding: NSSortDescriptorsBinding];
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
// Instance methods
|
2003-10-18 21:15:25 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Initalizes the outline view with the given frame. Invokes
|
|
|
|
* the superclass method initWithFrame: as well to initialize the object.
|
|
|
|
*
|
|
|
|
*/
|
2009-11-26 20:33:44 +00:00
|
|
|
- (id) initWithFrame: (NSRect)frame
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2008-03-07 23:51:55 +00:00
|
|
|
self = [super initWithFrame: frame];
|
|
|
|
|
|
|
|
if (self != nil)
|
|
|
|
{
|
|
|
|
[self _initOutlineDefaults];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2002-06-30 05:14:21 +00:00
|
|
|
- (void) dealloc
|
|
|
|
{
|
|
|
|
RELEASE(_items);
|
|
|
|
RELEASE(_expandedItems);
|
2004-01-04 03:39:09 +00:00
|
|
|
|
2002-06-30 05:14:21 +00:00
|
|
|
NSFreeMapTable(_itemDict);
|
|
|
|
NSFreeMapTable(_levelOfItems);
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if (_autosaveExpandedItems)
|
2002-06-30 05:14:21 +00:00
|
|
|
{
|
|
|
|
// notify when an item expands...
|
|
|
|
[nc removeObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
name: NSOutlineViewItemDidExpandNotification
|
|
|
|
object: self];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-06-30 05:14:21 +00:00
|
|
|
// notify when an item collapses...
|
|
|
|
[nc removeObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
name: NSOutlineViewItemDidCollapseNotification
|
|
|
|
object: self];
|
2002-06-30 05:14:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[super dealloc];
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-26 20:33:44 +00:00
|
|
|
* Causes the outline column, the column containing the expand/collapse
|
|
|
|
* gadget, to resize based on the amount of space needed by widest content.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2015-11-02 18:31:04 +00:00
|
|
|
- (BOOL) autoResizesOutlineColumn
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
return _autoResizesOutlineColumn;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-26 20:33:44 +00:00
|
|
|
* Causes the outline column, the column containing the expand/collapse
|
|
|
|
* gadget, to resize based on the amount of space needed by widest content.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2015-11-02 18:31:04 +00:00
|
|
|
- (BOOL) autosaveExpandedItems
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-23 16:37:17 +00:00
|
|
|
return _autosaveExpandedItems;
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Collapses the given item only. This is the equivalent of calling
|
2003-10-18 21:15:25 +00:00
|
|
|
* [NSOutlineView-collapseItem:collapseChildren:] with NO.
|
|
|
|
*/
|
2009-11-26 20:33:44 +00:00
|
|
|
- (void) collapseItem: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-23 16:37:17 +00:00
|
|
|
[self collapseItem: item collapseChildren: NO];
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-26 20:33:44 +00:00
|
|
|
* Collapses the specified item. If collapseChildren is set to YES,
|
|
|
|
* then all of the expandable children of this item all also collapsed
|
|
|
|
* in a recursive fashion (i.e. all children, grandchildren and etc).
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2009-11-26 20:33:44 +00:00
|
|
|
- (void) collapseItem: (id)item collapseChildren: (BOOL)collapseChildren
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-23 16:37:17 +00:00
|
|
|
const SEL shouldSelector = @selector(outlineView:shouldCollapseItem:);
|
|
|
|
BOOL canCollapse = YES;
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([_delegate respondsToSelector: shouldSelector])
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
|
|
|
canCollapse = [_delegate outlineView: self shouldCollapseItem: item];
|
|
|
|
}
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([self isExpandable: item] && [self isItemExpanded: item] && canCollapse)
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2004-01-04 03:39:09 +00:00
|
|
|
NSMutableDictionary *infoDict = [NSMutableDictionary dictionary];
|
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
[infoDict setObject: item forKey: @"NSObject"];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
// Send out the notification to let observers know that this is about
|
|
|
|
// to occur.
|
|
|
|
[nc postNotificationName: NSOutlineViewItemWillCollapseNotification
|
2024-06-16 12:38:12 +00:00
|
|
|
object: self
|
|
|
|
userInfo: infoDict];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2010-08-27 13:30:23 +00:00
|
|
|
// recursively find all children and call this method to close them.
|
|
|
|
// Note: The children must be collapsed before their parent item so
|
|
|
|
// that the selected row indexes are properly updated (and in particular
|
|
|
|
// are valid when we post our notifications).
|
2005-11-16 11:34:25 +00:00
|
|
|
if (collapseChildren) // collapse all
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
int index, numChildren;
|
|
|
|
NSMutableArray *allChildren;
|
|
|
|
id sitem = (item == nil) ? (id)[NSNull null] : (id)item;
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
allChildren = NSMapGet(_itemDict, sitem);
|
|
|
|
numChildren = [allChildren count];
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
for (index = 0; index < numChildren; index++)
|
|
|
|
{
|
|
|
|
id child = [allChildren objectAtIndex: index];
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
if ([self isExpandable: child])
|
|
|
|
{
|
|
|
|
[self collapseItem: child collapseChildren: collapseChildren];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2010-08-27 13:30:23 +00:00
|
|
|
|
|
|
|
// collapse...
|
|
|
|
[self _closeItem: item];
|
|
|
|
|
|
|
|
// Send out the notification to let observers know that this has
|
2016-05-28 09:49:15 +00:00
|
|
|
// occurred.
|
2010-08-27 13:30:23 +00:00
|
|
|
[nc postNotificationName: NSOutlineViewItemDidCollapseNotification
|
2024-06-16 12:38:12 +00:00
|
|
|
object: self
|
|
|
|
userInfo: infoDict];
|
2010-08-27 13:30:23 +00:00
|
|
|
|
2005-02-07 21:27:06 +00:00
|
|
|
// Should only mark the rect below the closed item for redraw
|
|
|
|
[self setNeedsDisplay: YES];
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2024-04-22 11:21:05 +00:00
|
|
|
// If it is view based, then refresh the outline view...
|
|
|
|
if (_viewBased)
|
|
|
|
{
|
|
|
|
[self reloadData];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Expands the given item only. This is the equivalent of calling
|
2003-10-18 21:15:25 +00:00
|
|
|
* [NSOutlineView-expandItem:expandChildren:] with NO.
|
|
|
|
*/
|
2009-11-30 18:56:51 +00:00
|
|
|
- (void) expandItem: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-23 16:37:17 +00:00
|
|
|
[self expandItem: item expandChildren: NO];
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-29 18:02:06 +00:00
|
|
|
* Expands the specified item. If expandChildren is set to YES, then all
|
|
|
|
* of the expandable children of this item all also expanded in a recursive
|
|
|
|
* fashion (i.e. all children, grandchildren and etc).
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (void) expandItem: (id)item expandChildren: (BOOL)expandChildren
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-23 16:37:17 +00:00
|
|
|
const SEL shouldExpandSelector = @selector(outlineView:shouldExpandItem:);
|
|
|
|
BOOL canExpand = YES;
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([_delegate respondsToSelector: shouldExpandSelector])
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
|
|
|
canExpand = [_delegate outlineView: self shouldExpandItem: item];
|
|
|
|
}
|
|
|
|
|
2002-06-15 22:00:18 +00:00
|
|
|
// if the item is expandable
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([self isExpandable: item])
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2002-06-15 22:00:18 +00:00
|
|
|
// if it is not already expanded and it can be expanded, then expand
|
2005-11-16 11:34:25 +00:00
|
|
|
if (![self isItemExpanded: item] && canExpand)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
NSMutableDictionary *infoDict = [NSMutableDictionary dictionary];
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
[infoDict setObject: item forKey: @"NSObject"];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
// Send out the notification to let observers know that this is about
|
|
|
|
// to occur.
|
|
|
|
[nc postNotificationName: NSOutlineViewItemWillExpandNotification
|
|
|
|
object: self
|
|
|
|
userInfo: infoDict];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
// insert the root element, if necessary otherwise insert the
|
|
|
|
// actual object.
|
|
|
|
[self _openItem: item];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
// Send out the notification to let observers know that this has
|
|
|
|
// occurred.
|
|
|
|
[nc postNotificationName: NSOutlineViewItemDidExpandNotification
|
|
|
|
object: self
|
|
|
|
userInfo: infoDict];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2002-06-15 22:00:18 +00:00
|
|
|
// recursively find all children and call this method to open them.
|
2005-11-16 11:34:25 +00:00
|
|
|
if (expandChildren) // expand all
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
int index, numChildren;
|
|
|
|
NSMutableArray *allChildren;
|
|
|
|
id sitem = (item == nil) ? (id)[NSNull null] : (id)item;
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
allChildren = NSMapGet(_itemDict, sitem);
|
|
|
|
numChildren = [allChildren count];
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
for (index = 0; index < numChildren; index++)
|
|
|
|
{
|
|
|
|
id child = [allChildren objectAtIndex: index];
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
if ([self isExpandable: child])
|
|
|
|
{
|
|
|
|
[self expandItem: child expandChildren: expandChildren];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2005-02-07 21:27:06 +00:00
|
|
|
// Should only mark the rect below the expanded item for redraw
|
|
|
|
[self setNeedsDisplay: YES];
|
2024-04-22 11:21:05 +00:00
|
|
|
|
|
|
|
// If it is view based, then refresh the outline view...
|
|
|
|
if (_viewBased)
|
|
|
|
{
|
|
|
|
[self reloadData];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2010-05-31 22:18:45 +00:00
|
|
|
- (NSRect) frameOfOutlineCellAtRow: (NSInteger)row
|
|
|
|
{
|
|
|
|
NSRect frameRect;
|
|
|
|
|
|
|
|
if (![self isExpandable: [self itemAtRow: row]])
|
|
|
|
return NSZeroRect;
|
|
|
|
|
|
|
|
frameRect = [self frameOfCellAtColumn: 0
|
2024-06-16 12:38:12 +00:00
|
|
|
row: row];
|
|
|
|
|
2010-05-31 22:18:45 +00:00
|
|
|
if (_indentationMarkerFollowsCell)
|
|
|
|
{
|
|
|
|
frameRect.origin.x += _indentationPerLevel * [self levelForRow: row];
|
|
|
|
}
|
|
|
|
|
|
|
|
return frameRect;
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns whether or not the indentation marker or "knob" is indented
|
|
|
|
* along with the content inside the cell.
|
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (BOOL) indentationMarkerFollowsCell
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
return _indentationMarkerFollowsCell;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Returns the amount of indentation, in points, for each level
|
2003-10-18 21:15:25 +00:00
|
|
|
* of the tree represented by the outline view.
|
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
- (CGFloat) indentationPerLevel
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
return _indentationPerLevel;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns YES, if the item is able to be expanded, NO otherwise.
|
2009-12-05 18:28:45 +00:00
|
|
|
*
|
|
|
|
* Returns NO when the item is nil (as Cocoa does).
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (BOOL) isExpandable: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2024-06-27 09:37:01 +00:00
|
|
|
BOOL result = NO;
|
2024-06-27 09:54:06 +00:00
|
|
|
GSKeyValueBinding *theBinding = [GSKeyValueBinding getBinding: NSContentBinding
|
2024-06-27 09:37:01 +00:00
|
|
|
forObject: self];
|
|
|
|
if (theBinding != nil)
|
2009-12-05 18:28:45 +00:00
|
|
|
{
|
2024-06-27 09:37:01 +00:00
|
|
|
BOOL leaf = YES;
|
|
|
|
id observedObject = [theBinding observedObject];
|
|
|
|
NSTreeController *tc = (NSTreeController *)observedObject;
|
2024-07-03 08:08:43 +00:00
|
|
|
NSString *leafKeyPath = [tc leafKeyPathForNode: item];
|
2024-06-27 09:37:01 +00:00
|
|
|
|
|
|
|
if (leafKeyPath == nil)
|
|
|
|
{
|
2024-07-03 08:08:43 +00:00
|
|
|
NSString *countKeyPath = [tc countKeyPathForNode: item];
|
2024-06-27 09:37:01 +00:00
|
|
|
|
|
|
|
if (countKeyPath == nil)
|
|
|
|
{
|
2024-07-03 08:08:43 +00:00
|
|
|
NSString *childrenKeyPath = [tc childrenKeyPathForNode: item];
|
2024-06-27 09:37:01 +00:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-27 09:37:01 +00:00
|
|
|
result = !leaf; // if item is a leaf, it's not expandable...
|
|
|
|
}
|
|
|
|
else if (item != nil)
|
|
|
|
{
|
|
|
|
result = [_dataSource outlineView: self isItemExpandable: item];
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-27 09:37:01 +00:00
|
|
|
return result;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns YES if the item is expanded or open, NO otherwise.
|
2009-12-05 18:28:45 +00:00
|
|
|
*
|
|
|
|
* Returns YES when the item is nil (as Cocoa does).
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (BOOL) isItemExpanded: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2005-11-16 11:34:25 +00:00
|
|
|
if (item == nil)
|
2009-11-29 18:02:06 +00:00
|
|
|
{
|
2002-03-23 16:39:19 +00:00
|
|
|
return YES;
|
2009-11-29 18:02:06 +00:00
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
// Check the array to determine if it is expanded.
|
2015-11-02 16:02:27 +00:00
|
|
|
if ([_expandedItems indexOfObjectIdenticalTo: item] == NSNotFound)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
return YES;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Returns the item at a given row. If no item exists for the given row,
|
2008-03-07 23:51:55 +00:00
|
|
|
* returns nil.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
- (id) itemAtRow: (NSInteger)row
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2008-03-07 23:51:55 +00:00
|
|
|
if ((row >= [_items count]) || (row < 0))
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
return [_items objectAtIndex: row];
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns the level for a given item.
|
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
- (NSInteger) levelForItem: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2005-11-16 11:34:25 +00:00
|
|
|
if (item != nil)
|
2002-02-27 06:05:33 +00:00
|
|
|
{
|
2002-04-01 16:03:02 +00:00
|
|
|
id object = NSMapGet(_levelOfItems, item);
|
2010-05-31 22:18:45 +00:00
|
|
|
return [object integerValue];
|
2002-02-27 06:05:33 +00:00
|
|
|
}
|
|
|
|
|
2001-10-25 21:41:03 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns the level for the given row.
|
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
- (NSInteger) levelForRow: (NSInteger)row
|
2002-02-27 06:05:33 +00:00
|
|
|
{
|
|
|
|
return [self levelForItem: [self itemAtRow: row]];
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Returns the outline table column.
|
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (NSTableColumn *) outlineTableColumn
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
|
|
|
return _outlineTableColumn;
|
|
|
|
}
|
|
|
|
|
2010-05-31 22:18:45 +00:00
|
|
|
/**
|
2015-11-02 16:35:02 +00:00
|
|
|
* Returns the parent of the given item or nil if the item is not found.
|
2010-05-31 22:18:45 +00:00
|
|
|
*/
|
|
|
|
- (id) parentForItem: (id)item
|
|
|
|
{
|
|
|
|
NSArray *allKeys = NSAllMapTableKeys(_itemDict);
|
|
|
|
NSEnumerator *en = [allKeys objectEnumerator];
|
|
|
|
NSInteger index;
|
|
|
|
id parent;
|
|
|
|
|
|
|
|
while ((parent = [en nextObject]))
|
|
|
|
{
|
|
|
|
NSMutableArray *childArray = NSMapGet(_itemDict, parent);
|
|
|
|
|
2015-11-02 16:35:02 +00:00
|
|
|
if ((index = [childArray indexOfObjectIdenticalTo: item]) != NSNotFound)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
return (parent == [NSNull null]) ? (id)nil : (id)parent;
|
|
|
|
}
|
2010-05-31 22:18:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Causes an item to be reloaded. This is the equivalent of calling
|
|
|
|
* [NSOutlineView-reloadItem:reloadChildren:] with reloadChildren set to NO.
|
|
|
|
*/
|
2006-07-04 21:31:49 +00:00
|
|
|
- (void) reloadItem: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-03-23 16:39:19 +00:00
|
|
|
[self reloadItem: item reloadChildren: NO];
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Causes an item and all of it's children to be reloaded if reloadChildren is
|
2006-07-04 21:31:49 +00:00
|
|
|
* set to YES, if it's set to NO, then only the item itself is refreshed
|
|
|
|
* from the datasource.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (void) reloadItem: (id)item reloadChildren: (BOOL)reloadChildren
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger index;
|
2004-02-14 12:49:56 +00:00
|
|
|
id parent;
|
2004-07-03 16:34:24 +00:00
|
|
|
BOOL expanded;
|
|
|
|
id dsobj = nil;
|
2006-07-04 21:31:49 +00:00
|
|
|
id object = (item == nil) ? (id)[NSNull null] : (id)item;
|
2004-02-14 12:49:56 +00:00
|
|
|
NSArray *allKeys = NSAllMapTableKeys(_itemDict);
|
|
|
|
NSEnumerator *en = [allKeys objectEnumerator];
|
2002-03-23 16:39:19 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
expanded = [self isItemExpanded: item];
|
2002-03-23 16:39:19 +00:00
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
// find the parent of the item
|
2004-02-14 12:49:56 +00:00
|
|
|
while ((parent = [en nextObject]))
|
|
|
|
{
|
|
|
|
NSMutableArray *childArray = NSMapGet(_itemDict, parent);
|
|
|
|
|
2015-11-02 16:35:02 +00:00
|
|
|
if ((index = [childArray indexOfObjectIdenticalTo: object]) != NSNotFound)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
parent = (parent == [NSNull null]) ? (id)nil : (id)parent;
|
|
|
|
dsobj = [_dataSource outlineView: self
|
|
|
|
child: index
|
|
|
|
ofItem: parent];
|
|
|
|
|
|
|
|
if (dsobj != item)
|
|
|
|
{
|
|
|
|
[childArray replaceObjectAtIndex: index withObject: dsobj];
|
|
|
|
// FIXME We need to correct _items, _itemDict, _levelOfItems,
|
|
|
|
// _expandedItems and _selectedItems
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2004-02-14 12:49:56 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
if (reloadChildren)
|
2002-03-23 16:39:19 +00:00
|
|
|
{
|
2004-07-03 16:34:24 +00:00
|
|
|
[self _removeChildren: dsobj];
|
|
|
|
[self _loadDictionaryStartingWith: dsobj
|
2024-06-16 12:38:12 +00:00
|
|
|
atLevel: [self levelForItem: dsobj]];
|
2002-03-23 16:39:19 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
if (expanded)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
[self _openItem: dsobj];
|
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
[self setNeedsDisplay: YES];
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2006-07-04 21:31:49 +00:00
|
|
|
* Returns the corresponding row in the outline view for the given item.
|
|
|
|
* Returns -1 if item is nil or not found.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
- (NSInteger) rowForItem: (id)item
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger row;
|
2005-11-16 11:34:25 +00:00
|
|
|
if (item == nil)
|
2005-07-26 02:24:54 +00:00
|
|
|
return -1;
|
|
|
|
|
2015-11-02 16:35:02 +00:00
|
|
|
row = [_items indexOfObjectIdenticalTo: item];
|
2005-07-26 02:22:28 +00:00
|
|
|
return (row == NSNotFound) ? -1 : row;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* When set to YES this causes the outline column, the column containing
|
|
|
|
* the expand/collapse gadget, to resize based on the amount of space
|
2003-10-18 21:15:25 +00:00
|
|
|
* needed by widest content.
|
|
|
|
*/
|
2006-07-04 21:31:49 +00:00
|
|
|
- (void) setAutoresizesOutlineColumn: (BOOL)resize
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
_autoResizesOutlineColumn = resize;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* When set to YES, the outline view will save the state of all expanded or
|
2003-10-18 21:15:25 +00:00
|
|
|
* collapsed items in the view to the users defaults for the application the
|
|
|
|
* outline view is running in.
|
|
|
|
*/
|
2006-07-04 21:31:49 +00:00
|
|
|
- (void) setAutosaveExpandedItems: (BOOL)flag
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2005-11-16 11:34:25 +00:00
|
|
|
if (flag == _autosaveExpandedItems)
|
2002-04-02 05:04:57 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
_autosaveExpandedItems = flag;
|
2005-11-16 11:34:25 +00:00
|
|
|
if (flag)
|
2002-04-02 05:04:57 +00:00
|
|
|
{
|
|
|
|
[self _autoloadExpandedItems];
|
|
|
|
// notify when an item expands...
|
|
|
|
[nc addObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
selector: @selector(_autosaveExpandedItems)
|
|
|
|
name: NSOutlineViewItemDidExpandNotification
|
|
|
|
object: self];
|
2002-04-02 05:04:57 +00:00
|
|
|
|
|
|
|
// notify when an item collapses...
|
|
|
|
[nc addObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
selector: @selector(_autosaveExpandedItems)
|
|
|
|
name: NSOutlineViewItemDidCollapseNotification
|
|
|
|
object: self];
|
2002-04-02 05:04:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// notify when an item expands...
|
|
|
|
[nc removeObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
name: NSOutlineViewItemDidExpandNotification
|
|
|
|
object: self];
|
2002-04-02 05:04:57 +00:00
|
|
|
|
|
|
|
// notify when an item collapses...
|
|
|
|
[nc removeObserver: self
|
2024-06-16 12:38:12 +00:00
|
|
|
name: NSOutlineViewItemDidCollapseNotification
|
|
|
|
object: self];
|
2002-04-02 05:04:57 +00:00
|
|
|
}
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* If set to YES, the indentation marker will follow the content at each level.
|
|
|
|
* Otherwise, the indentation marker will remain at the left most position of
|
|
|
|
* the view regardless of how many levels in the content is indented.
|
|
|
|
*/
|
2009-11-29 18:02:06 +00:00
|
|
|
- (void) setIndentationMarkerFollowsCell: (BOOL)followsCell
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
_indentationMarkerFollowsCell = followsCell;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Sets the amount, in points, that each level is to be indented by.
|
|
|
|
*/
|
2015-11-02 18:31:04 +00:00
|
|
|
- (void) setIndentationPerLevel: (CGFloat)newIndentLevel
|
2001-10-25 21:41:03 +00:00
|
|
|
{
|
2002-02-24 03:10:16 +00:00
|
|
|
_indentationPerLevel = newIndentLevel;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
|
|
|
* Sets the outline table column in which to place the indentation marker.
|
|
|
|
*/
|
2001-10-25 21:41:03 +00:00
|
|
|
- (void)setOutlineTableColumn: (NSTableColumn *)outlineTableColumn
|
|
|
|
{
|
|
|
|
_outlineTableColumn = outlineTableColumn;
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Returns YES, by default. Subclasses should override this method if
|
2003-10-18 21:15:25 +00:00
|
|
|
* a different behaviour is required.
|
|
|
|
*/
|
2001-10-25 21:41:03 +00:00
|
|
|
- (BOOL)shouldCollapseAutoExpandedItemsForDeposited: (BOOL)deposited
|
|
|
|
{
|
2002-03-23 16:39:19 +00:00
|
|
|
return YES;
|
2001-10-25 21:41:03 +00:00
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Sets the data source for this outline view.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2002-02-23 16:37:17 +00:00
|
|
|
- (void) setDataSource: (id)anObject
|
|
|
|
{
|
2024-06-19 05:46:27 +00:00
|
|
|
GSKeyValueBinding *theBinding;
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2002-06-15 22:00:18 +00:00
|
|
|
#define CHECK_REQUIRED_METHOD(selector_name) \
|
2003-06-27 00:45:24 +00:00
|
|
|
if (anObject && ![anObject respondsToSelector: @selector(selector_name)]) \
|
2002-06-15 22:00:18 +00:00
|
|
|
[NSException raise: NSInternalInconsistencyException \
|
2024-06-16 12:38:12 +00:00
|
|
|
format: @"data source does not respond to %@", @#selector_name]
|
2002-06-15 22:00:18 +00:00
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
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:);
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
// This method is @optional in NSOutlineViewDataSource as of macOS10.0
|
|
|
|
// CHECK_REQUIRED_METHOD(outlineView:objectValueForTableColumn:byItem:);
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2024-07-05 05:40:51 +00:00
|
|
|
// 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;
|
|
|
|
}
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
/* We do *not* retain the dataSource, it's like a delegate */
|
|
|
|
_dataSource = anObject;
|
|
|
|
[self tile];
|
|
|
|
[self reloadData];
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Forces a from scratch reload of all data in the outline view.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2002-02-23 16:37:17 +00:00
|
|
|
- (void) reloadData
|
|
|
|
{
|
2024-04-22 11:21:05 +00:00
|
|
|
// Refresh the views if it is view based...
|
|
|
|
if (_viewBased)
|
|
|
|
{
|
|
|
|
NSEnumerator *en = [[self subviews] objectEnumerator];
|
|
|
|
NSView *v = nil;
|
|
|
|
|
|
|
|
while ((v = [en nextObject]) != nil)
|
|
|
|
{
|
|
|
|
[v removeFromSuperview];
|
|
|
|
}
|
|
|
|
}
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2002-03-23 16:39:19 +00:00
|
|
|
// release the old array
|
2005-11-16 11:34:25 +00:00
|
|
|
if (_items != nil)
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
RELEASE(_items);
|
2002-03-23 16:39:19 +00:00
|
|
|
}
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if (_itemDict != NULL)
|
2002-03-23 16:39:19 +00:00
|
|
|
{
|
2002-04-01 16:03:02 +00:00
|
|
|
NSFreeMapTable(_itemDict);
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
2002-03-23 16:39:19 +00:00
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if (_levelOfItems != NULL)
|
2002-03-28 00:23:37 +00:00
|
|
|
{
|
2002-04-01 16:03:02 +00:00
|
|
|
NSFreeMapTable(_levelOfItems);
|
2002-03-28 00:23:37 +00:00
|
|
|
}
|
|
|
|
|
2002-03-23 16:39:19 +00:00
|
|
|
// create a new empty one
|
2009-11-30 18:56:51 +00:00
|
|
|
_items = [[NSMutableArray alloc] init];
|
2015-11-02 18:31:04 +00:00
|
|
|
_itemDict = NSCreateMapTable(keyCallBacks,
|
2024-06-16 12:38:12 +00:00
|
|
|
NSObjectMapValueCallBacks,
|
|
|
|
64);
|
2015-11-02 18:31:04 +00:00
|
|
|
_levelOfItems = NSCreateMapTable(keyCallBacks,
|
2024-06-16 12:38:12 +00:00
|
|
|
NSObjectMapValueCallBacks,
|
|
|
|
64);
|
2002-03-23 16:39:19 +00:00
|
|
|
|
|
|
|
// reload all the open items...
|
|
|
|
[self _openItem: nil];
|
2002-02-23 16:37:17 +00:00
|
|
|
[super reloadData];
|
|
|
|
}
|
|
|
|
|
2003-10-18 21:15:25 +00:00
|
|
|
/**
|
2009-11-30 18:56:51 +00:00
|
|
|
* Sets the delegate of the outlineView.
|
2003-10-18 21:15:25 +00:00
|
|
|
*/
|
2002-02-23 16:37:17 +00:00
|
|
|
- (void) setDelegate: (id)anObject
|
2002-07-11 04:52:33 +00:00
|
|
|
{
|
|
|
|
const SEL sel = @selector(outlineView:willDisplayCell:forTableColumn:item:);
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
if (_delegate)
|
|
|
|
[nc removeObserver: _delegate name: nil object: self];
|
|
|
|
_delegate = anObject;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
#define SET_DELEGATE_NOTIFICATION(notif_name) \
|
|
|
|
if ([_delegate respondsToSelector: @selector(outlineView##notif_name:)]) \
|
|
|
|
[nc addObserver: _delegate \
|
|
|
|
selector: @selector(outlineView##notif_name:) \
|
|
|
|
name: NSOutlineView##notif_name##Notification object: self]
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
SET_DELEGATE_NOTIFICATION(ColumnDidMove);
|
|
|
|
SET_DELEGATE_NOTIFICATION(ColumnDidResize);
|
|
|
|
SET_DELEGATE_NOTIFICATION(SelectionDidChange);
|
|
|
|
SET_DELEGATE_NOTIFICATION(SelectionIsChanging);
|
|
|
|
SET_DELEGATE_NOTIFICATION(ItemDidExpand);
|
|
|
|
SET_DELEGATE_NOTIFICATION(ItemDidCollapse);
|
|
|
|
SET_DELEGATE_NOTIFICATION(ItemWillExpand);
|
|
|
|
SET_DELEGATE_NOTIFICATION(ItemWillCollapse);
|
2002-07-11 04:52:33 +00:00
|
|
|
|
|
|
|
_del_responds = [_delegate respondsToSelector: sel];
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
|
|
{
|
|
|
|
[super encodeWithCoder: aCoder];
|
2006-08-12 22:44:56 +00:00
|
|
|
if ([aCoder allowsKeyedCoding] == NO)
|
2006-05-20 22:12:46 +00:00
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
float indentation = _indentationPerLevel;
|
2006-07-04 21:31:49 +00:00
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_autoResizesOutlineColumn];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_indentationMarkerFollowsCell];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_autosaveExpandedItems];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aCoder encodeValueOfObjCType: @encode(float)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &indentation];
|
2006-05-20 22:12:46 +00:00
|
|
|
[aCoder encodeConditionalObject: _outlineTableColumn];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) initWithCoder: (NSCoder *)aDecoder
|
|
|
|
{
|
|
|
|
// Since we only have one version....
|
|
|
|
self = [super initWithCoder: aDecoder];
|
2008-03-07 23:51:55 +00:00
|
|
|
if (self == nil)
|
|
|
|
return self;
|
|
|
|
|
|
|
|
[self _initOutlineDefaults];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2006-07-04 21:31:49 +00:00
|
|
|
if ([aDecoder allowsKeyedCoding])
|
2009-11-30 18:56:51 +00:00
|
|
|
{
|
2006-05-20 22:12:46 +00:00
|
|
|
// init the table column... (this can't be chosen on IB either)...
|
2006-07-04 21:31:49 +00:00
|
|
|
if ([_tableColumns count] > 0)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
_outlineTableColumn = [_tableColumns objectAtIndex: 0];
|
|
|
|
}
|
2006-05-20 22:12:46 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
float indentation;
|
2009-11-30 18:56:51 +00:00
|
|
|
// overrides outline defaults with archived values
|
2006-07-04 21:31:49 +00:00
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_autoResizesOutlineColumn];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_indentationMarkerFollowsCell];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &_autosaveExpandedItems];
|
2006-07-04 21:31:49 +00:00
|
|
|
[aDecoder decodeValueOfObjCType: @encode(float)
|
2024-06-16 12:38:12 +00:00
|
|
|
at: &indentation];
|
2010-05-31 22:18:45 +00:00
|
|
|
_indentationPerLevel = indentation;
|
2006-05-20 22:12:46 +00:00
|
|
|
_outlineTableColumn = [aDecoder decodeObject];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) mouseDown: (NSEvent *)theEvent
|
|
|
|
{
|
|
|
|
NSPoint location = [theEvent locationInWindow];
|
|
|
|
|
|
|
|
location = [self convertPoint: location fromView: nil];
|
|
|
|
_clickedRow = [self rowAtPoint: location];
|
|
|
|
_clickedColumn = [self columnAtPoint: location];
|
|
|
|
|
2006-11-10 19:12:32 +00:00
|
|
|
if (_clickedRow != -1
|
|
|
|
&& [_tableColumns objectAtIndex: _clickedColumn] == _outlineTableColumn)
|
2002-03-04 23:53:27 +00:00
|
|
|
{
|
2004-01-04 03:39:09 +00:00
|
|
|
NSImage *image;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-04-22 11:21:05 +00:00
|
|
|
id item = [self itemAtRow: _clickedRow];
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger level = [self levelForRow: _clickedRow];
|
|
|
|
NSInteger position = 0;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2007-02-05 18:06:03 +00:00
|
|
|
if ([self isItemExpanded: item])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
image = expanded;
|
|
|
|
}
|
2004-01-04 03:39:09 +00:00
|
|
|
else
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
image = collapsed;
|
|
|
|
}
|
2004-01-04 03:39:09 +00:00
|
|
|
|
|
|
|
if (_indentationMarkerFollowsCell)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
position = _indentationPerLevel * level;
|
|
|
|
}
|
2002-04-26 04:30:04 +00:00
|
|
|
|
|
|
|
position += _columnOrigins[_clickedColumn];
|
|
|
|
|
2007-02-05 18:06:03 +00:00
|
|
|
if ([self isExpandable:item]
|
2024-04-26 16:40:11 +00:00
|
|
|
&& location.x >= position - 5
|
|
|
|
&& location.x <= position + [image size].width + 10)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
BOOL withChildren =
|
2010-08-19 16:52:59 +00:00
|
|
|
([theEvent modifierFlags] & NSAlternateKeyMask) ? YES : NO;
|
2024-06-16 12:38:12 +00:00
|
|
|
if (![self isItemExpanded: item])
|
|
|
|
{
|
|
|
|
[self expandItem: item expandChildren: withChildren];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self collapseItem: item collapseChildren: withChildren];
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
2002-03-04 23:53:27 +00:00
|
|
|
[super mouseDown: theEvent];
|
2009-11-30 18:56:51 +00:00
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2011-04-30 05:46:44 +00:00
|
|
|
- (void)keyDown: (NSEvent*)event
|
|
|
|
{
|
|
|
|
NSString *characters = [event characters];
|
|
|
|
|
|
|
|
if ([characters length] == 1)
|
|
|
|
{
|
|
|
|
unichar c = [characters characterAtIndex: 0];
|
2011-07-15 05:39:10 +00:00
|
|
|
|
|
|
|
NSIndexSet *selected = [self selectedRowIndexes];
|
|
|
|
NSInteger i;
|
|
|
|
for (i = [selected firstIndex]; i != NSNotFound; i = [selected indexGreaterThanIndex: i])
|
|
|
|
{
|
|
|
|
id item = [self itemAtRow: i];
|
|
|
|
switch (c)
|
|
|
|
{
|
|
|
|
case NSLeftArrowFunctionKey:
|
2011-07-02 17:40:58 +00:00
|
|
|
{
|
2011-07-15 05:39:10 +00:00
|
|
|
if ([self isItemExpanded: item])
|
|
|
|
{
|
|
|
|
[self collapseItem: item];
|
|
|
|
}
|
|
|
|
else
|
2011-07-02 17:40:58 +00:00
|
|
|
{
|
2011-07-15 05:39:10 +00:00
|
|
|
id parent = [self parentForItem: item];
|
|
|
|
if (parent != nil)
|
|
|
|
{
|
|
|
|
NSInteger parentRow = [self rowForItem: parent];
|
|
|
|
[self selectRow: parentRow
|
|
|
|
byExtendingSelection: NO];
|
|
|
|
[self scrollRowToVisible: parentRow];
|
|
|
|
}
|
2011-07-02 17:40:58 +00:00
|
|
|
}
|
2011-07-15 05:39:10 +00:00
|
|
|
return;
|
2011-07-02 17:40:58 +00:00
|
|
|
}
|
2011-07-15 05:39:10 +00:00
|
|
|
case NSRightArrowFunctionKey:
|
|
|
|
[self expandItem: item];
|
|
|
|
return;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2011-04-30 05:46:44 +00:00
|
|
|
}
|
|
|
|
}
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2011-04-30 05:46:44 +00:00
|
|
|
[super keyDown: event];
|
|
|
|
}
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
/*
|
|
|
|
* Drawing
|
2002-02-23 16:37:17 +00:00
|
|
|
*/
|
2013-01-30 09:48:54 +00:00
|
|
|
- (void) drawRow: (NSInteger)rowIndex clipRect: (NSRect)aRect
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2024-07-27 22:29:00 +00:00
|
|
|
GSKeyValueBinding *theBinding = nil;
|
|
|
|
|
2024-08-04 01:02:33 +00:00
|
|
|
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
|
2024-07-27 22:29:00 +00:00
|
|
|
forObject: self];
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2024-07-27 22:29:00 +00:00
|
|
|
if (_dataSource == nil && theBinding == nil)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2024-02-16 09:04:42 +00:00
|
|
|
if (_viewBased)
|
|
|
|
{
|
2024-06-17 00:52:06 +00:00
|
|
|
[self _drawCellViewRow: rowIndex
|
|
|
|
clipRect: aRect];
|
2024-02-16 09:04:42 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[[GSTheme theme] drawOutlineViewRow: rowIndex
|
|
|
|
clipRect: aRect
|
|
|
|
inView: self];
|
|
|
|
}
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
2002-02-27 06:05:33 +00:00
|
|
|
- (void) drawRect: (NSRect)aRect
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger index = 0;
|
2002-02-23 16:37:17 +00:00
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if (_autoResizesOutlineColumn)
|
2002-02-23 16:37:17 +00:00
|
|
|
{
|
2016-10-08 21:17:53 +00:00
|
|
|
CGFloat widest = 0;
|
2009-11-26 20:33:44 +00:00
|
|
|
for (index = 0; index < _numberOfRows; index++)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
CGFloat offset = [self levelForRow: index] *
|
|
|
|
[self indentationPerLevel];
|
|
|
|
NSRect drawingRect = [self frameOfCellAtColumn: 0
|
|
|
|
row: index];
|
|
|
|
CGFloat length = drawingRect.size.width + offset;
|
|
|
|
if (widest < length) widest = length;
|
|
|
|
}
|
2002-03-23 16:39:19 +00:00
|
|
|
// [_outlineTableColumn setWidth: widest];
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
|
|
|
|
2002-02-27 06:05:33 +00:00
|
|
|
[super drawRect: aRect];
|
2002-02-23 16:37:17 +00:00
|
|
|
}
|
2002-02-27 06:05:33 +00:00
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
- (void) setDropItem: (id)item
|
2010-05-31 22:18:45 +00:00
|
|
|
dropChildIndex: (NSInteger)childIndex
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2015-11-02 16:35:02 +00:00
|
|
|
if (item != nil && [_items indexOfObjectIdenticalTo: item] == NSNotFound)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
/* FIXME raise an exception, or perhaps we should support
|
|
|
|
* setting an item which is not visible (inside a collapsed
|
|
|
|
* item presumably), or perhaps we should treat this as
|
|
|
|
* cancelling the drop?
|
|
|
|
*/
|
2004-07-03 16:34:24 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
currentDropItem = item;
|
|
|
|
currentDropIndex = childIndex;
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2002-03-04 23:53:27 +00:00
|
|
|
|
|
|
|
/*
|
2004-07-03 16:34:24 +00:00
|
|
|
* Drag'n'drop support
|
2002-03-04 23:53:27 +00:00
|
|
|
*/
|
2004-07-03 16:34:24 +00:00
|
|
|
|
2006-05-10 22:11:28 +00:00
|
|
|
- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
|
2002-03-04 23:53:27 +00:00
|
|
|
{
|
2004-07-03 16:34:24 +00:00
|
|
|
//NSLog(@"draggingEntered");
|
2009-11-30 18:56:51 +00:00
|
|
|
oldDropItem = currentDropItem = nil;
|
|
|
|
oldDropIndex = currentDropIndex = -1;
|
2004-07-03 16:34:24 +00:00
|
|
|
lastVerticalQuarterPosition = -1;
|
2012-03-09 08:03:45 +00:00
|
|
|
dragOperation = NSDragOperationCopy;
|
2004-07-03 16:34:24 +00:00
|
|
|
oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
|
|
|
|
return NSDragOperationCopy;
|
2002-03-04 23:53:27 +00:00
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) draggingExited: (id <NSDraggingInfo>) sender
|
2002-03-04 23:53:27 +00:00
|
|
|
{
|
2004-07-03 16:34:24 +00:00
|
|
|
[self setNeedsDisplayInRect: oldDraggingRect];
|
2009-11-29 18:02:06 +00:00
|
|
|
[self _autoCollapse];
|
2004-07-03 16:34:24 +00:00
|
|
|
[self displayIfNeeded];
|
2009-11-29 18:02:06 +00:00
|
|
|
DESTROY(lastDragUpdate);
|
|
|
|
DESTROY(lastDragChange);
|
2002-03-04 23:53:27 +00:00
|
|
|
}
|
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
// TODO: Move the part that starts at 'Compute the indicator rect area' to GSTheme
|
2024-06-16 12:38:12 +00:00
|
|
|
- (void) drawDropAboveIndicatorWithDropItem: (id)currentDropItem
|
|
|
|
atRow: (NSInteger)row
|
|
|
|
childDropIndex: (NSInteger)currentDropIndex
|
2009-12-05 18:28:45 +00:00
|
|
|
{
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger level = 0;
|
2009-12-05 18:28:45 +00:00
|
|
|
NSBezierPath *path = nil;
|
|
|
|
NSRect newRect = NSZeroRect;
|
|
|
|
|
|
|
|
/* Compute the indicator rect area */
|
|
|
|
if (currentDropItem == nil && currentDropIndex == 0)
|
|
|
|
{
|
|
|
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
2024-06-16 12:38:12 +00:00
|
|
|
0,
|
|
|
|
[self visibleRect].size.width,
|
|
|
|
2);
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
|
|
|
else if (row == _numberOfRows)
|
|
|
|
{
|
|
|
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
2024-06-16 12:38:12 +00:00
|
|
|
row * _rowHeight - 2,
|
|
|
|
[self visibleRect].size.width,
|
|
|
|
2);
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
2024-06-16 12:38:12 +00:00
|
|
|
row * _rowHeight - 1,
|
|
|
|
[self visibleRect].size.width,
|
|
|
|
2);
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
2009-12-06 01:36:26 +00:00
|
|
|
level = [self levelForItem: currentDropItem] + 1;
|
2009-12-05 18:28:45 +00:00
|
|
|
newRect.origin.x += level * _indentationPerLevel;
|
|
|
|
newRect.size.width -= level * _indentationPerLevel;
|
|
|
|
|
|
|
|
[[NSColor darkGrayColor] set];
|
|
|
|
|
|
|
|
/* The rectangle is a line across the cell indicating the
|
|
|
|
* insertion position. We adjust by enough pixels to allow for
|
|
|
|
* a ring drawn on the left end.
|
|
|
|
*/
|
|
|
|
newRect.size.width -= 7;
|
|
|
|
newRect.origin.x += 7;
|
|
|
|
NSRectFill(newRect);
|
|
|
|
|
|
|
|
/* We make the redraw rectangle big enough to hold both the
|
|
|
|
* line and the circle (8 pixels high).
|
|
|
|
*/
|
|
|
|
newRect.size.width += 7;
|
|
|
|
newRect.origin.x -= 7;
|
|
|
|
newRect.size.height = 8;
|
|
|
|
newRect.origin.y -= 3;
|
|
|
|
oldDraggingRect = newRect;
|
|
|
|
if (newRect.size.width < 8)
|
|
|
|
oldDraggingRect.size.width = 8;
|
|
|
|
|
|
|
|
/* We draw the circle at the left of the line, and make it
|
|
|
|
* a little smaller than the redraw rectangle so that the
|
|
|
|
* bezier path will draw entirely inside the redraw area
|
|
|
|
* and we won't leave artifacts behind on the screen.
|
|
|
|
*/
|
|
|
|
newRect.size.width = 7;
|
|
|
|
newRect.size.height = 7;
|
|
|
|
newRect.origin.x += 0.5;
|
|
|
|
newRect.origin.y += 0.5;
|
|
|
|
path = [NSBezierPath bezierPath];
|
|
|
|
[path appendBezierPathWithOvalInRect: newRect];
|
|
|
|
[path stroke];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* When the drop item is nil and the drop child index is -1 */
|
|
|
|
- (void) drawDropOnRootIndicator
|
|
|
|
{
|
|
|
|
NSRect indicatorRect = [self visibleRect];
|
|
|
|
|
|
|
|
/* Remember indicator area to be redrawn next time */
|
|
|
|
oldDraggingRect = indicatorRect;
|
|
|
|
|
|
|
|
[[NSColor darkGrayColor] set];
|
|
|
|
NSFrameRectWithWidth(indicatorRect, 2.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: Move a method common to -drapOnRootIndicator and the one below to GSTheme
|
|
|
|
- (void) drawDropOnIndicatorWithDropItem: (id)currentDropItem
|
|
|
|
{
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger row = [_items indexOfObjectIdenticalTo: currentDropItem];
|
|
|
|
NSInteger level = [self levelForItem: currentDropItem];
|
2009-12-05 18:28:45 +00:00
|
|
|
NSRect newRect = [self frameOfCellAtColumn: 0
|
2024-06-16 12:38:12 +00:00
|
|
|
row: row];
|
2009-12-05 18:28:45 +00:00
|
|
|
|
|
|
|
newRect.origin.x = _bounds.origin.x;
|
|
|
|
newRect.size.width = _bounds.size.width + 2;
|
|
|
|
newRect.origin.x -= _intercellSpacing.height / 2;
|
|
|
|
newRect.size.height += _intercellSpacing.height;
|
|
|
|
|
|
|
|
/* Remember indicator area to be redrawn next time */
|
|
|
|
oldDraggingRect = newRect;
|
|
|
|
oldDraggingRect.origin.y -= 1;
|
|
|
|
oldDraggingRect.size.height += 2;
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
newRect.size.height -= 1;
|
|
|
|
newRect.origin.x += 3;
|
|
|
|
newRect.size.width -= 3;
|
|
|
|
|
|
|
|
if (_drawsGrid)
|
|
|
|
{
|
|
|
|
//newRect.origin.y += 1;
|
|
|
|
//newRect.origin.x += 1;
|
|
|
|
//newRect.size.width -= 2;
|
|
|
|
newRect.size.height += 1;
|
|
|
|
}
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
newRect.origin.x += level * _indentationPerLevel;
|
|
|
|
newRect.size.width -= level * _indentationPerLevel;
|
|
|
|
|
|
|
|
[[NSColor darkGrayColor] set];
|
|
|
|
NSFrameRectWithWidth(newRect, 2.0);
|
|
|
|
}
|
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
/* Returns the row whose item is the parent that owns the child at the given row.
|
2009-12-05 18:28:45 +00:00
|
|
|
Also returns the child index relative to this parent. */
|
2024-06-16 12:38:12 +00:00
|
|
|
- (NSInteger) _parentRowForRow: (NSInteger)row
|
|
|
|
atLevel: (NSInteger)level
|
|
|
|
andReturnChildIndex: (NSInteger *)childIndex
|
2009-12-05 18:28:45 +00:00
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger i;
|
|
|
|
NSInteger lvl;
|
2009-12-05 18:28:45 +00:00
|
|
|
|
|
|
|
*childIndex = 0;
|
|
|
|
|
|
|
|
for (i = row - 1; i >= 0; i--)
|
|
|
|
{
|
2009-12-15 22:22:01 +00:00
|
|
|
BOOL foundParent;
|
|
|
|
BOOL foundSibling;
|
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
lvl = [self levelForRow: i];
|
|
|
|
|
2009-12-15 22:22:01 +00:00
|
|
|
foundParent = (lvl == level - 1);
|
|
|
|
foundSibling = (lvl == level);
|
2009-12-05 18:28:45 +00:00
|
|
|
|
|
|
|
if (foundParent)
|
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
break;
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
|
|
|
else if (foundSibling)
|
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
(*childIndex)++;
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
2006-05-10 22:11:28 +00:00
|
|
|
- (NSDragOperation) draggingUpdated: (id <NSDraggingInfo>) sender
|
2002-03-04 23:53:27 +00:00
|
|
|
{
|
2009-12-05 18:28:45 +00:00
|
|
|
NSPoint p = [self convertPoint: [sender draggingLocation] fromView: nil];
|
|
|
|
/* The insertion row.
|
2024-06-16 12:38:12 +00:00
|
|
|
* The insertion row is identical to the hovered row, except when p is in
|
2009-12-05 18:28:45 +00:00
|
|
|
* the hovered row bottom part (the last quarter).
|
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger row;
|
2009-12-05 18:28:45 +00:00
|
|
|
/* A row can be divided into 4 vertically stacked portions.
|
2024-06-16 12:38:12 +00:00
|
|
|
* We call each portion a quarter.
|
|
|
|
* verticalQuarterPosition is the number of quarters that exists between the
|
|
|
|
* top left origin (NSOutlineView is flipped) and the hovered row (precisely
|
|
|
|
* up to the quarter occupied by the pointer in this row).
|
2009-12-05 18:28:45 +00:00
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger verticalQuarterPosition;
|
2009-12-05 18:28:45 +00:00
|
|
|
/* An indentation unit can be divided into 2 portions (left and right).
|
|
|
|
* We call each portion a half.
|
|
|
|
* We use it to compute the insertion level. */
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger horizontalHalfPosition;
|
2009-12-05 18:28:45 +00:00
|
|
|
/* The quarter (0, 1, 2 or 3) occupied by the pointer within the hovered row
|
|
|
|
* (not in the insertion row). */
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger positionInRow;
|
2009-12-05 18:28:45 +00:00
|
|
|
/* The previous row level (the row before the insertion row) */
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger levelBefore;
|
2009-12-05 18:28:45 +00:00
|
|
|
/* The next row level (the row after the insertion row) */
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger levelAfter;
|
2024-06-16 12:38:12 +00:00
|
|
|
/* The insertion level that may vary with the horizontal pointer position,
|
2009-12-05 18:28:45 +00:00
|
|
|
* when the pointer is between two rows and the bottom row is a parent.
|
|
|
|
*/
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger level;
|
2012-03-09 08:03:45 +00:00
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-11-29 18:02:06 +00:00
|
|
|
ASSIGN(lastDragUpdate, [NSDate date]);
|
2009-12-05 18:28:45 +00:00
|
|
|
//NSLog(@"draggingUpdated");
|
2009-11-29 18:02:06 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
/* _bounds.origin is (0, 0) when the outline view is not clipped.
|
|
|
|
* When the view is scrolled, _bounds.origin.y returns the scrolled height. */
|
2009-11-30 18:56:51 +00:00
|
|
|
verticalQuarterPosition =
|
2011-07-12 21:12:22 +00:00
|
|
|
GSRoundTowardsInfinity(((p.y + _bounds.origin.y) / _rowHeight) * 4.);
|
2009-11-30 18:56:51 +00:00
|
|
|
horizontalHalfPosition =
|
2011-07-12 21:12:22 +00:00
|
|
|
GSRoundTowardsInfinity(((p.x + _bounds.origin.y) / _indentationPerLevel) * 2.);
|
2004-07-03 16:34:24 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
/* We add an extra quarter to shift the insertion row below the hovered row. */
|
2009-11-30 18:56:51 +00:00
|
|
|
row = (verticalQuarterPosition + 1) / 4;
|
|
|
|
positionInRow = verticalQuarterPosition % 4;
|
|
|
|
if (row > _numberOfRows)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2009-12-05 18:28:45 +00:00
|
|
|
row = _numberOfRows; // beyond the last real row
|
|
|
|
positionInRow = 1; // inside the root item (we could also use 2)
|
2002-03-04 23:53:27 +00:00
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
|
2009-12-05 18:28:45 +00:00
|
|
|
//NSLog(@"verticalQuarterPosition = %d", verticalQuarterPosition);
|
|
|
|
//NSLog(@"insertion row = %d", row);
|
2002-03-04 23:53:27 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
if (row == 0)
|
2002-03-04 23:53:27 +00:00
|
|
|
{
|
2004-07-03 16:34:24 +00:00
|
|
|
levelBefore = 0;
|
2002-03-22 00:15:03 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
levelBefore = [self levelForRow: (row - 1)];
|
|
|
|
}
|
|
|
|
if (row == _numberOfRows)
|
|
|
|
{
|
|
|
|
levelAfter = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
levelAfter = [self levelForRow: row];
|
|
|
|
}
|
2004-01-04 03:39:09 +00:00
|
|
|
//NSLog(@"level before = %d", levelBefore);
|
|
|
|
//NSLog(@"level after = %d", levelAfter);
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-03-22 00:15:03 +00:00
|
|
|
if ((lastVerticalQuarterPosition != verticalQuarterPosition)
|
2009-11-29 13:51:15 +00:00
|
|
|
|| (lastHorizontalHalfPosition != horizontalHalfPosition))
|
2002-03-22 00:15:03 +00:00
|
|
|
{
|
2010-05-31 22:18:45 +00:00
|
|
|
NSInteger minInsertionLevel = levelAfter;
|
|
|
|
NSInteger maxInsertionLevel = levelBefore;
|
2011-07-12 21:12:22 +00:00
|
|
|
NSInteger pointerInsertionLevel = GSRoundTowardsInfinity((float)horizontalHalfPosition / 2.);
|
2002-03-22 00:15:03 +00:00
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
/* Save positions to avoid executing this code when the general
|
|
|
|
* position of the mouse is unchanged.
|
|
|
|
*/
|
|
|
|
lastVerticalQuarterPosition = verticalQuarterPosition;
|
|
|
|
lastHorizontalHalfPosition = horizontalHalfPosition;
|
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
/* When the row before is an empty parent, we allow to insert the dragged
|
|
|
|
* item as its child.
|
2009-12-06 01:36:26 +00:00
|
|
|
*/
|
|
|
|
if ([self isExpandable: [self itemAtRow: (row - 1)]])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
maxInsertionLevel++;
|
|
|
|
}
|
2009-12-06 01:36:26 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
/* Find the insertion level to be used with a drop above
|
|
|
|
*
|
2024-06-16 12:38:12 +00:00
|
|
|
* In the outline below, when the pointer moves horizontally on
|
|
|
|
* the dashed line, it can insert at three levels: x level, C level or
|
2009-12-05 18:28:45 +00:00
|
|
|
* B/D level but not at A level.
|
2024-06-16 12:38:12 +00:00
|
|
|
*
|
2009-12-05 18:28:45 +00:00
|
|
|
* + A
|
|
|
|
* + B
|
|
|
|
* + C
|
|
|
|
* - x
|
|
|
|
* --- pointer ---
|
2024-06-16 12:38:12 +00:00
|
|
|
* + D
|
2009-12-05 18:28:45 +00:00
|
|
|
*/
|
|
|
|
if (pointerInsertionLevel < minInsertionLevel)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
level = minInsertionLevel;
|
|
|
|
}
|
2009-12-05 18:28:45 +00:00
|
|
|
else if (pointerInsertionLevel > maxInsertionLevel)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
level = maxInsertionLevel;
|
|
|
|
}
|
2009-12-05 18:28:45 +00:00
|
|
|
else
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
level = pointerInsertionLevel;
|
|
|
|
}
|
2002-03-22 00:15:03 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
//NSLog(@"min insert level = %d", minInsertionLevel);
|
|
|
|
//NSLog(@"max insert level = %d", maxInsertionLevel);
|
|
|
|
//NSLog(@"insert level = %d", level);
|
|
|
|
//NSLog(@"row = %d and position in row = %d", row, positionInRow);
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
if (positionInRow > 0 && positionInRow < 3) /* Drop on */
|
2009-11-29 18:02:06 +00:00
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
/* We are directly over the middle of a row ... so the drop
|
|
|
|
* should be directory on the item in that row.
|
|
|
|
*/
|
2009-12-05 18:28:45 +00:00
|
|
|
currentDropItem = [self itemAtRow: row];
|
|
|
|
currentDropIndex = NSOutlineViewDropOnItemIndex;
|
2009-11-29 18:02:06 +00:00
|
|
|
}
|
2009-12-05 18:28:45 +00:00
|
|
|
else /* Drop above */
|
2009-11-30 18:56:51 +00:00
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
NSInteger childIndex = 0;
|
|
|
|
NSInteger parentRow = [self _parentRowForRow: row
|
|
|
|
atLevel: level
|
|
|
|
andReturnChildIndex: &childIndex];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
//NSLog(@"found %d (proposed childIndex = %d)", parentRow, childIndex);
|
2002-03-22 00:15:03 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
currentDropItem = (parentRow == -1 ? nil : [self itemAtRow: parentRow]);
|
2024-06-16 12:38:12 +00:00
|
|
|
currentDropIndex = childIndex;
|
2009-12-05 18:28:45 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
|
|
|
if ([_dataSource respondsToSelector:
|
2009-11-29 13:51:15 +00:00
|
|
|
@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
dragOperation = [_dataSource outlineView: self
|
|
|
|
validateDrop: sender
|
|
|
|
proposedItem: currentDropItem
|
2009-12-05 18:28:45 +00:00
|
|
|
proposedChildIndex: currentDropIndex];
|
2024-06-16 12:38:12 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-12-05 18:28:45 +00:00
|
|
|
//NSLog(@"Drop on %@ %d", currentDropItem, currentDropIndex);
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
if ((currentDropItem != oldDropItem)
|
|
|
|
|| (currentDropIndex != oldDropIndex))
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
oldDropItem = currentDropItem;
|
|
|
|
oldDropIndex = currentDropIndex;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2009-11-29 18:02:06 +00:00
|
|
|
ASSIGN(lastDragChange, lastDragUpdate);
|
2024-06-16 12:38:12 +00:00
|
|
|
[self lockFocus];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
[self setNeedsDisplayInRect: oldDraggingRect];
|
|
|
|
[self displayIfNeeded];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2012-03-09 08:03:45 +00:00
|
|
|
if (dragOperation != NSDragOperationNone)
|
|
|
|
{
|
|
|
|
if (currentDropIndex != NSOutlineViewDropOnItemIndex && currentDropItem != nil)
|
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
[self drawDropAboveIndicatorWithDropItem: currentDropItem
|
2012-03-09 08:03:45 +00:00
|
|
|
atRow: row
|
|
|
|
childDropIndex: currentDropIndex];
|
|
|
|
}
|
|
|
|
else if (currentDropIndex == NSOutlineViewDropOnItemIndex && currentDropItem == nil)
|
|
|
|
{
|
|
|
|
[self drawDropOnRootIndicator];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[self drawDropOnIndicatorWithDropItem: currentDropItem];
|
|
|
|
}
|
|
|
|
}
|
2008-03-07 23:51:55 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
[_window flushWindow];
|
|
|
|
[self unlockFocus];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
}
|
2009-11-29 18:02:06 +00:00
|
|
|
}
|
2009-12-05 18:28:45 +00:00
|
|
|
else if (row != _numberOfRows)
|
2009-11-29 18:02:06 +00:00
|
|
|
{
|
|
|
|
/* If we have been hovering over an item for more than half a second,
|
|
|
|
* we should expand it.
|
|
|
|
*/
|
2010-04-24 19:43:35 +00:00
|
|
|
if (lastDragChange != nil && [lastDragUpdate timeIntervalSinceDate: lastDragChange] >= 0.5)
|
2009-11-29 18:02:06 +00:00
|
|
|
{
|
2009-12-05 18:28:45 +00:00
|
|
|
id item = [_items objectAtIndex: row];
|
2009-11-29 18:02:06 +00:00
|
|
|
if ([self isExpandable: item] && ![self isItemExpanded: item])
|
|
|
|
{
|
|
|
|
[self expandItem: item expandChildren: NO];
|
|
|
|
if ([self isItemExpanded: item])
|
|
|
|
{
|
|
|
|
[autoExpanded addObject: item];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Set the change date even if we didn't actually expand ... so
|
|
|
|
* we don't keep trying to expand the same item unnecessarily.
|
|
|
|
*/
|
|
|
|
ASSIGN(lastDragChange, lastDragUpdate);
|
|
|
|
}
|
2002-03-22 00:15:03 +00:00
|
|
|
}
|
|
|
|
|
2004-02-11 23:14:35 +00:00
|
|
|
return dragOperation;
|
2002-03-22 00:15:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) performDragOperation: (id<NSDraggingInfo>)sender
|
|
|
|
{
|
2009-11-29 18:02:06 +00:00
|
|
|
BOOL result = NO;
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
if ([_dataSource
|
2024-06-16 12:38:12 +00:00
|
|
|
respondsToSelector:
|
|
|
|
@selector(outlineView:acceptDrop:item:childIndex:)])
|
2002-03-22 00:15:03 +00:00
|
|
|
{
|
2009-11-29 18:02:06 +00:00
|
|
|
result = [_dataSource outlineView: self
|
|
|
|
acceptDrop: sender
|
2009-11-30 18:56:51 +00:00
|
|
|
item: currentDropItem
|
|
|
|
childIndex: currentDropIndex];
|
2002-03-22 00:15:03 +00:00
|
|
|
}
|
2004-01-04 03:39:09 +00:00
|
|
|
|
2009-11-29 18:02:06 +00:00
|
|
|
[self _autoCollapse];
|
|
|
|
|
|
|
|
return result;
|
2002-03-22 00:15:03 +00:00
|
|
|
}
|
|
|
|
|
2002-03-22 01:14:35 +00:00
|
|
|
- (BOOL) prepareForDragOperation: (id<NSDraggingInfo>)sender
|
|
|
|
{
|
|
|
|
[self setNeedsDisplayInRect: oldDraggingRect];
|
|
|
|
[self displayIfNeeded];
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2011-03-17 10:19:11 +00:00
|
|
|
- (NSArray*) namesOfPromisedFilesDroppedAtDestination: (NSURL *)dropDestination
|
|
|
|
{
|
|
|
|
if ([_dataSource respondsToSelector:
|
2024-06-16 12:38:12 +00:00
|
|
|
@selector(outlineView:namesOfPromisedFilesDroppedAtDestination:forDraggedItems:)])
|
2011-03-17 10:19:11 +00:00
|
|
|
{
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger count = [_selectedRows count];
|
2011-03-30 08:17:00 +00:00
|
|
|
NSMutableArray *itemArray = [NSMutableArray arrayWithCapacity: count];
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger index = [_selectedRows firstIndex];
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2011-03-30 08:17:00 +00:00
|
|
|
while (index != NSNotFound)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
[itemArray addObject: [self itemAtRow: index]];
|
|
|
|
index = [_selectedRows indexGreaterThanIndex: index];
|
|
|
|
}
|
2011-03-30 08:17:00 +00:00
|
|
|
|
2011-03-17 10:19:11 +00:00
|
|
|
return [_dataSource outlineView: self
|
2024-06-16 12:38:12 +00:00
|
|
|
namesOfPromisedFilesDroppedAtDestination: dropDestination
|
|
|
|
forDraggedItems: itemArray];
|
2011-03-17 10:19:11 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return nil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-04-02 05:04:57 +00:00
|
|
|
// Autosave methods...
|
|
|
|
- (void) setAutosaveName: (NSString *)name
|
|
|
|
{
|
|
|
|
[super setAutosaveName: name];
|
|
|
|
[self _autoloadExpandedItems];
|
|
|
|
}
|
|
|
|
|
2013-01-30 09:48:54 +00:00
|
|
|
- (void) editColumn: (NSInteger) columnIndex
|
2024-06-16 12:38:12 +00:00
|
|
|
row: (NSInteger) rowIndex
|
|
|
|
withEvent: (NSEvent *) theEvent
|
|
|
|
select: (BOOL) flag
|
2002-07-11 04:52:33 +00:00
|
|
|
{
|
|
|
|
NSText *t;
|
|
|
|
NSTableColumn *tb;
|
2011-02-14 20:11:57 +00:00
|
|
|
NSRect drawingRect;
|
2002-07-11 04:52:33 +00:00
|
|
|
unsigned length = 0;
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
// We refuse to edit cells if the delegate can not accept results
|
2002-07-11 04:52:33 +00:00
|
|
|
// of editing.
|
|
|
|
if (_dataSource_editable == NO)
|
|
|
|
{
|
2006-09-16 23:19:48 +00:00
|
|
|
flag = YES;
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2015-03-15 23:31:43 +00:00
|
|
|
if (rowIndex != _selectedRow)
|
|
|
|
{
|
|
|
|
[NSException raise:NSInvalidArgumentException
|
|
|
|
format:@"Attempted to edit unselected row"];
|
|
|
|
}
|
2002-07-11 04:52:33 +00:00
|
|
|
|
2015-03-15 23:31:43 +00:00
|
|
|
if (rowIndex < 0 || rowIndex >= _numberOfRows
|
2002-07-11 04:52:33 +00:00
|
|
|
|| columnIndex < 0 || columnIndex >= _numberOfColumns)
|
|
|
|
{
|
|
|
|
[NSException raise: NSInvalidArgumentException
|
2024-06-16 12:38:12 +00:00
|
|
|
format: @"Row/column out of index in edit"];
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2015-03-15 23:31:43 +00:00
|
|
|
[self scrollRowToVisible: rowIndex];
|
|
|
|
[self scrollColumnToVisible: columnIndex];
|
|
|
|
|
2002-07-11 04:52:33 +00:00
|
|
|
if (_textObject != nil)
|
|
|
|
{
|
|
|
|
[self validateEditing];
|
|
|
|
[self abortEditing];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now (_textObject == nil)
|
|
|
|
|
|
|
|
t = [_window fieldEditor: YES forObject: self];
|
|
|
|
|
|
|
|
if ([t superview] != nil)
|
|
|
|
{
|
|
|
|
if ([t resignFirstResponder] == NO)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2002-07-11 04:52:33 +00:00
|
|
|
_editedRow = rowIndex;
|
|
|
|
_editedColumn = columnIndex;
|
|
|
|
|
|
|
|
// Prepare the cell
|
|
|
|
// NB: need to be released when no longer used
|
2013-02-14 10:34:20 +00:00
|
|
|
_editedCell = [[self preparedCellAtColumn: columnIndex row: rowIndex] copy];
|
2002-07-11 04:52:33 +00:00
|
|
|
|
2006-09-16 23:19:48 +00:00
|
|
|
[_editedCell setEditable: _dataSource_editable];
|
2013-02-14 10:34:20 +00:00
|
|
|
tb = [_tableColumns objectAtIndex: columnIndex];
|
2002-07-11 04:52:33 +00:00
|
|
|
[_editedCell setObjectValue: [self _objectValueForTableColumn: tb
|
2024-06-16 12:38:12 +00:00
|
|
|
row: rowIndex]];
|
2002-07-11 04:52:33 +00:00
|
|
|
|
|
|
|
// But of course the delegate can mess it up if it wants
|
2004-06-21 11:41:26 +00:00
|
|
|
[self _willDisplayCell: _editedCell
|
2024-06-16 12:38:12 +00:00
|
|
|
forTableColumn: tb
|
|
|
|
row: rowIndex];
|
2002-07-11 04:52:33 +00:00
|
|
|
|
|
|
|
/* Please note the important point - calling stringValue normally
|
|
|
|
causes the _editedCell to call the validateEditing method of its
|
|
|
|
control view ... which happens to be this object :-)
|
|
|
|
but we don't want any spurious validateEditing to be performed
|
|
|
|
before the actual editing is started (otherwise you easily end up
|
|
|
|
with the table view picking up the string stored in the field
|
|
|
|
editor, which is likely to be the string resulting from the last
|
|
|
|
edit somewhere else ... getting into the bug that when you TAB
|
|
|
|
from one cell to another one, the string is copied!), so we must
|
|
|
|
call stringValue when _textObject is still nil. */
|
|
|
|
if (flag)
|
|
|
|
{
|
|
|
|
length = [[_editedCell stringValue] length];
|
|
|
|
}
|
|
|
|
|
|
|
|
_textObject = [_editedCell setUpFieldEditorAttributes: t];
|
2010-01-11 14:04:44 +00:00
|
|
|
// FIXME: Which background color do we want here?
|
|
|
|
[_textObject setBackgroundColor: [NSColor selectedControlColor]];
|
|
|
|
[_textObject setDrawsBackground: YES];
|
2002-07-11 04:52:33 +00:00
|
|
|
|
|
|
|
drawingRect = [self frameOfCellAtColumn: columnIndex row: rowIndex];
|
|
|
|
|
2005-11-16 11:34:25 +00:00
|
|
|
if (tb == [self outlineTableColumn])
|
2002-07-11 04:52:33 +00:00
|
|
|
{
|
2006-09-16 23:19:48 +00:00
|
|
|
id item = nil;
|
|
|
|
NSImage *image = nil;
|
|
|
|
NSCell *imageCell = nil;
|
2011-02-14 20:11:57 +00:00
|
|
|
NSRect imageRect;
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger level = 0;
|
|
|
|
CGFloat indentationFactor = 0.0;
|
2006-09-16 23:19:48 +00:00
|
|
|
|
2011-02-14 20:11:57 +00:00
|
|
|
item = [self itemAtRow: rowIndex];
|
2006-09-16 23:19:48 +00:00
|
|
|
// determine which image to use...
|
|
|
|
if ([self isItemExpanded: item])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
image = expanded;
|
|
|
|
}
|
2006-09-16 23:19:48 +00:00
|
|
|
else
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
image = collapsed;
|
|
|
|
}
|
2006-09-16 23:19:48 +00:00
|
|
|
|
|
|
|
if (![self isExpandable: item])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
image = unexpandable;
|
|
|
|
}
|
2006-09-16 23:19:48 +00:00
|
|
|
|
2002-07-14 23:44:48 +00:00
|
|
|
level = [self levelForItem: item];
|
|
|
|
indentationFactor = _indentationPerLevel * level;
|
|
|
|
// create the image cell..
|
|
|
|
imageCell = [[NSCell alloc] initImageCell: image];
|
2011-02-14 20:11:57 +00:00
|
|
|
imageRect = [self frameOfOutlineCellAtRow: rowIndex];
|
|
|
|
|
|
|
|
if ([_delegate respondsToSelector: @selector(outlineView:willDisplayOutlineCell:forTableColumn:item:)])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
[_delegate outlineView: self
|
|
|
|
willDisplayOutlineCell: imageCell
|
|
|
|
forTableColumn: tb
|
|
|
|
item: item];
|
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2006-07-02 11:25:04 +00:00
|
|
|
|
2011-02-14 20:11:57 +00:00
|
|
|
if ([imageCell image])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
imageRect.size.width = [image size].width;
|
|
|
|
imageRect.size.height = [image size].height;
|
|
|
|
|
|
|
|
// draw...
|
|
|
|
[self lockFocus];
|
|
|
|
[imageCell drawWithFrame: imageRect inView: self];
|
|
|
|
[self unlockFocus];
|
|
|
|
|
|
|
|
// move the drawing rect over like in the drawRow routine...
|
|
|
|
drawingRect.origin.x += indentationFactor + 5 + imageRect.size.width;
|
|
|
|
drawingRect.size.width
|
|
|
|
-= indentationFactor + 5 + imageRect.size.width;
|
|
|
|
}
|
2011-02-14 20:11:57 +00:00
|
|
|
else
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
// move the drawing rect over like in the drawRow routine...
|
|
|
|
drawingRect.origin.x += indentationFactor;
|
|
|
|
drawingRect.size.width -= indentationFactor;
|
|
|
|
}
|
2006-09-16 23:19:48 +00:00
|
|
|
|
|
|
|
RELEASE(imageCell);
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2002-07-14 23:44:48 +00:00
|
|
|
|
2002-07-11 04:52:33 +00:00
|
|
|
if (flag)
|
|
|
|
{
|
|
|
|
[_editedCell selectWithFrame: drawingRect
|
2024-06-16 12:38:12 +00:00
|
|
|
inView: self
|
|
|
|
editor: _textObject
|
|
|
|
delegate: self
|
|
|
|
start: 0
|
|
|
|
length: length];
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
[_editedCell editWithFrame: drawingRect
|
2024-06-16 12:38:12 +00:00
|
|
|
inView: self
|
|
|
|
editor: _textObject
|
|
|
|
delegate: self
|
|
|
|
event: theEvent];
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2003-02-01 05:10:00 +00:00
|
|
|
|
|
|
|
return;
|
2002-07-11 04:52:33 +00:00
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
|
2002-02-23 16:37:17 +00:00
|
|
|
@end /* implementation of NSOutlineView */
|
2001-10-25 21:41:03 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
@implementation NSOutlineView (NotificationRequestMethods)
|
2024-07-19 12:04:13 +00:00
|
|
|
|
2024-08-03 19:05:44 +00:00
|
|
|
- (NSIndexPath *) _findIndexPathForItem: (id)item
|
|
|
|
parentItem: (id)pItem
|
2024-08-03 18:31:51 +00:00
|
|
|
{
|
2024-08-03 19:05:44 +00:00
|
|
|
id parentItem = (pItem == nil) ? (id)[NSNull null] : (id)pItem;
|
|
|
|
NSArray *children = NSMapGet(_itemDict, parentItem);
|
|
|
|
NSInteger childCount = [children count];
|
2024-08-04 01:44:02 +00:00
|
|
|
NSInteger index = 0;
|
|
|
|
|
|
|
|
for (index = 0; index < childCount; index++)
|
2024-08-03 18:31:51 +00:00
|
|
|
{
|
2024-08-03 19:05:44 +00:00
|
|
|
id childItem = [children objectAtIndex: index];
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2024-08-03 19:05:44 +00:00
|
|
|
if (childItem == item)
|
2024-08-03 18:31:51 +00:00
|
|
|
{
|
2024-08-03 19:05:44 +00:00
|
|
|
return [NSIndexPath indexPathWithIndex: index];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSIndexPath *foundPath = [self _findIndexPathForItem: item
|
|
|
|
parentItem: childItem];
|
|
|
|
|
|
|
|
if (foundPath != nil)
|
|
|
|
{
|
2024-08-04 01:40:04 +00:00
|
|
|
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;
|
2024-08-03 19:05:44 +00:00
|
|
|
}
|
2024-08-03 18:31:51 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-03 19:05:44 +00:00
|
|
|
|
|
|
|
return nil;
|
2024-08-03 18:31:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (NSIndexPath *) _indexPathForItem: (id)item
|
|
|
|
{
|
2024-08-03 19:05:44 +00:00
|
|
|
return [self _findIndexPathForItem: item
|
2024-08-18 21:25:47 +00:00
|
|
|
parentItem: nil];
|
2024-08-03 18:31:51 +00:00
|
|
|
}
|
|
|
|
|
2024-08-18 21:25:47 +00:00
|
|
|
- (NSArray *) _indexPathsFromSelectedRows
|
2024-07-19 12:04:13 +00:00
|
|
|
{
|
|
|
|
NSUInteger index = [_selectedRows firstIndex];
|
2024-08-18 21:25:47 +00:00
|
|
|
NSMutableArray *result = [[NSMutableArray alloc] init];
|
|
|
|
|
2024-08-03 18:31:51 +00:00
|
|
|
// Regenerate the array...
|
2024-07-19 12:04:13 +00:00
|
|
|
while (index != NSNotFound)
|
|
|
|
{
|
|
|
|
id item = [_items objectAtIndex: index];
|
2024-08-03 18:31:51 +00:00
|
|
|
NSIndexPath *path = nil;
|
2024-07-19 12:04:13 +00:00
|
|
|
|
|
|
|
if ([item respondsToSelector: @selector(indexPath)])
|
|
|
|
{
|
2024-08-03 18:31:51 +00:00
|
|
|
path = [item indexPath];
|
2024-07-19 12:04:13 +00:00
|
|
|
}
|
2024-08-03 18:31:51 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
path = [self _indexPathForItem: item];
|
|
|
|
}
|
|
|
|
|
2024-08-18 21:25:47 +00:00
|
|
|
[result addObject: path];
|
2024-07-19 12:04:13 +00:00
|
|
|
|
|
|
|
index = [_selectedRows indexGreaterThanIndex: index];
|
|
|
|
}
|
2024-08-18 21:25:47 +00:00
|
|
|
|
|
|
|
return result;
|
2024-07-19 12:04:13 +00:00
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
/*
|
|
|
|
* (NotificationRequestMethods)
|
|
|
|
*/
|
|
|
|
- (void) _postSelectionIsChangingNotification
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
[nc postNotificationName:
|
2024-06-16 12:38:12 +00:00
|
|
|
NSOutlineViewSelectionIsChangingNotification
|
2004-07-03 16:34:24 +00:00
|
|
|
object: self];
|
|
|
|
}
|
2024-07-19 12:04:13 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _postSelectionDidChangeNotification
|
|
|
|
{
|
2024-07-19 12:04:13 +00:00
|
|
|
NSTableColumn *tb = [_tableColumns objectAtIndex: 0];
|
|
|
|
GSKeyValueBinding *theBinding;
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2024-07-19 12:04:13 +00:00
|
|
|
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)
|
|
|
|
{
|
2024-08-18 21:25:47 +00:00
|
|
|
NSArray *paths = [self _indexPathsFromSelectedRows];
|
2024-07-29 00:49:18 +00:00
|
|
|
if ([observedObject respondsToSelector: @selector(setSelectionIndexPaths:)])
|
|
|
|
{
|
2024-08-18 21:25:47 +00:00
|
|
|
[observedObject setSelectionIndexPaths: paths];
|
2024-07-29 00:49:18 +00:00
|
|
|
}
|
2024-08-18 21:25:47 +00:00
|
|
|
[theBinding reverseSetValue: paths];
|
2024-07-19 12:04:13 +00:00
|
|
|
}
|
|
|
|
}
|
2024-08-02 15:20:58 +00:00
|
|
|
|
2024-07-19 12:04:13 +00:00
|
|
|
[nc postNotificationName: NSOutlineViewSelectionDidChangeNotification
|
|
|
|
object: self];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2024-07-19 12:04:13 +00:00
|
|
|
|
2016-10-08 21:17:53 +00:00
|
|
|
- (void) _postColumnDidMoveNotificationWithOldIndex: (NSInteger) oldIndex
|
2024-06-16 12:38:12 +00:00
|
|
|
newIndex: (NSInteger) newIndex
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
[nc postNotificationName:
|
2024-06-16 12:38:12 +00:00
|
|
|
NSOutlineViewColumnDidMoveNotification
|
2004-07-03 16:34:24 +00:00
|
|
|
object: self
|
2009-11-30 18:56:51 +00:00
|
|
|
userInfo: [NSDictionary
|
2024-06-16 12:38:12 +00:00
|
|
|
dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithInteger: newIndex],
|
|
|
|
@"NSNewColumn",
|
|
|
|
[NSNumber numberWithInteger: oldIndex],
|
|
|
|
@"NSOldColumn",
|
|
|
|
nil]];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _postColumnDidResizeNotificationWithOldWidth: (float) oldWidth
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
[nc postNotificationName:
|
2024-06-16 12:38:12 +00:00
|
|
|
NSOutlineViewColumnDidResizeNotification
|
2004-07-03 16:34:24 +00:00
|
|
|
object: self
|
2009-11-30 18:56:51 +00:00
|
|
|
userInfo: [NSDictionary
|
2024-06-16 12:38:12 +00:00
|
|
|
dictionaryWithObjectsAndKeys:
|
|
|
|
[NSNumber numberWithFloat: oldWidth],
|
|
|
|
@"NSOldWidth",
|
|
|
|
nil]];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) _shouldSelectTableColumn: (NSTableColumn *)tableColumn
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
if ([_delegate respondsToSelector:
|
|
|
|
@selector (outlineView:shouldSelectTableColumn:)] == YES)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2006-07-04 21:31:49 +00:00
|
|
|
if ([_delegate outlineView: self shouldSelectTableColumn: tableColumn]
|
2024-06-16 12:38:12 +00:00
|
|
|
== NO)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2016-10-08 21:17:53 +00:00
|
|
|
- (BOOL) _shouldSelectRow: (NSInteger)rowIndex
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id item = [self itemAtRow: rowIndex];
|
2024-07-19 15:12:24 +00:00
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
if ([_delegate respondsToSelector:
|
|
|
|
@selector (outlineView:shouldSelectItem:)] == YES)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
if ([_delegate outlineView: self shouldSelectItem: item] == NO)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) _shouldSelectionChange
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
if ([_delegate respondsToSelector:
|
2024-04-22 08:33:38 +00:00
|
|
|
@selector (selectionShouldChangeInOutlineView:)] == YES)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-04-22 08:33:38 +00:00
|
|
|
if ([_delegate selectionShouldChangeInOutlineView: self] == NO)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2010-01-16 14:45:13 +00:00
|
|
|
- (void) _didChangeSortDescriptors: (NSArray *)oldSortDescriptors
|
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
if ([_dataSource
|
2010-01-16 14:45:13 +00:00
|
|
|
respondsToSelector: @selector(outlineView:sortDescriptorsDidChange:)])
|
|
|
|
{
|
2015-11-02 16:35:02 +00:00
|
|
|
[_dataSource outlineView: self
|
|
|
|
sortDescriptorsDidChange: oldSortDescriptors];
|
2010-01-16 14:45:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _didClickTableColumn: (NSTableColumn *)tc
|
|
|
|
{
|
2024-06-16 12:38:12 +00:00
|
|
|
if ([_delegate
|
2010-01-16 14:45:13 +00:00
|
|
|
respondsToSelector: @selector(outlineView:didClickTableColumn:)])
|
|
|
|
{
|
|
|
|
[_delegate outlineView: self didClickTableColumn: tc];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
- (BOOL) _shouldEditTableColumn: (NSTableColumn *)tableColumn
|
2024-06-16 12:38:12 +00:00
|
|
|
row: (NSInteger) rowIndex
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
if ([_delegate respondsToSelector:
|
2006-07-04 21:31:49 +00:00
|
|
|
@selector(outlineView:shouldEditTableColumn:item:)])
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id item = [self itemAtRow: rowIndex];
|
|
|
|
|
|
|
|
if ([_delegate outlineView: self shouldEditTableColumn: tableColumn
|
2024-06-16 12:38:12 +00:00
|
|
|
item: item] == NO)
|
|
|
|
{
|
|
|
|
return NO;
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return YES;
|
|
|
|
}
|
|
|
|
|
2024-07-14 20:00:18 +00:00
|
|
|
- (void) _willDisplayCell: (NSCell *)cell
|
2024-06-16 12:38:12 +00:00
|
|
|
forTableColumn: (NSTableColumn *)tb
|
|
|
|
row: (NSInteger)index
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-08-08 02:55:14 +00:00
|
|
|
[tb _applyBindingsToCell: cell
|
|
|
|
atRow: index];
|
2024-07-14 19:40:47 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
if (_del_responds)
|
|
|
|
{
|
|
|
|
id item = [self itemAtRow: index];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
|
|
|
[_delegate outlineView: self
|
2024-06-16 12:38:12 +00:00
|
|
|
willDisplayCell: cell
|
|
|
|
forTableColumn: tb
|
|
|
|
item: item];
|
2009-11-30 18:56:51 +00:00
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
2007-12-02 20:43:32 +00:00
|
|
|
- (BOOL) _writeRows: (NSIndexSet *)rows
|
2004-07-03 16:34:24 +00:00
|
|
|
toPasteboard: (NSPasteboard *)pboard
|
|
|
|
{
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger count = [rows count];
|
2006-07-04 21:31:49 +00:00
|
|
|
NSMutableArray *itemArray = [NSMutableArray arrayWithCapacity: count];
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger index = [rows firstIndex];
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2007-12-02 20:43:32 +00:00
|
|
|
while (index != NSNotFound)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2007-12-02 20:43:32 +00:00
|
|
|
[itemArray addObject: [self itemAtRow: index]];
|
|
|
|
index = [rows indexGreaterThanIndex: index];
|
2009-11-30 18:56:51 +00:00
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
if ([_dataSource respondsToSelector:
|
2024-06-16 12:38:12 +00:00
|
|
|
@selector(outlineView:writeItems:toPasteboard:)] == YES)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
return [_dataSource outlineView: self
|
2024-06-16 12:38:12 +00:00
|
|
|
writeItems: itemArray
|
|
|
|
toPasteboard: pboard];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
return NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (BOOL) _isDraggingSource
|
|
|
|
{
|
|
|
|
return [_dataSource respondsToSelector:
|
2024-06-16 12:38:12 +00:00
|
|
|
@selector(outlineView:writeItems:toPasteboard:)];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (id) _objectValueForTableColumn: (NSTableColumn *)tb
|
2024-06-16 12:38:12 +00:00
|
|
|
row: (NSInteger) index
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id result = nil;
|
2024-08-21 02:53:31 +00:00
|
|
|
NSString *keyPath = [tb _keyPathForValueBinding];
|
2004-07-03 16:34:24 +00:00
|
|
|
|
2024-08-21 02:53:31 +00:00
|
|
|
if (keyPath != nil)
|
2024-06-24 12:50:22 +00:00
|
|
|
{
|
2024-06-26 22:17:21 +00:00
|
|
|
id theItem = [_items objectAtIndex: index];
|
|
|
|
result = [theItem valueForKeyPath: keyPath];
|
2024-06-24 12:50:22 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ([_dataSource respondsToSelector:
|
|
|
|
@selector(outlineView:objectValueForTableColumn:byItem:)])
|
|
|
|
{
|
|
|
|
id item = [self itemAtRow: index];
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-24 12:50:22 +00:00
|
|
|
result = [_dataSource outlineView: self
|
|
|
|
objectValueForTableColumn: tb
|
|
|
|
byItem: item];
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _setObjectValue: (id)value
|
2024-06-16 12:38:12 +00:00
|
|
|
forTableColumn: (NSTableColumn *)tb
|
|
|
|
row: (NSInteger) index
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-08-21 02:56:18 +00:00
|
|
|
NSString *keyPath = [tb _keyPathForValueBinding];
|
|
|
|
|
2024-07-07 03:44:31 +00:00
|
|
|
// If we have content binding the data source is used only
|
|
|
|
// like a delegate
|
2024-08-21 02:56:18 +00:00
|
|
|
if (keyPath != nil)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-07-07 03:44:31 +00:00
|
|
|
id theItem = [_items objectAtIndex: index];
|
2004-07-03 16:34:24 +00:00
|
|
|
|
2024-07-07 03:44:31 +00:00
|
|
|
// 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];
|
2024-08-04 01:02:33 +00:00
|
|
|
|
2024-07-07 03:44:31 +00:00
|
|
|
[_dataSource outlineView: self
|
|
|
|
setObjectValue: value
|
|
|
|
forTableColumn: tb
|
|
|
|
byItem: item];
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-10-08 21:17:53 +00:00
|
|
|
- (NSInteger) _numRows
|
2007-02-06 01:07:44 +00:00
|
|
|
{
|
|
|
|
return [_items count];
|
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
@end
|
|
|
|
|
|
|
|
@implementation NSOutlineView (TableViewInternalPrivate)
|
|
|
|
|
2008-03-07 23:51:55 +00:00
|
|
|
- (void) _initOutlineDefaults
|
|
|
|
{
|
2015-11-02 18:37:00 +00:00
|
|
|
_itemDict = NSCreateMapTable(keyCallBacks,
|
2024-06-16 12:38:12 +00:00
|
|
|
NSObjectMapValueCallBacks,
|
|
|
|
64);
|
2008-03-07 23:51:55 +00:00
|
|
|
_items = [[NSMutableArray alloc] init];
|
|
|
|
_expandedItems = [[NSMutableArray alloc] init];
|
2015-11-02 18:37:00 +00:00
|
|
|
_levelOfItems = NSCreateMapTable(keyCallBacks,
|
2024-06-16 12:38:12 +00:00
|
|
|
NSObjectMapValueCallBacks,
|
|
|
|
64);
|
2008-03-07 23:51:55 +00:00
|
|
|
|
|
|
|
_indentationMarkerFollowsCell = YES;
|
|
|
|
_autoResizesOutlineColumn = NO;
|
|
|
|
_autosaveExpandedItems = NO;
|
|
|
|
_indentationPerLevel = 10.0;
|
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _autosaveExpandedItems
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
if (_autosaveExpandedItems && _autosaveName != nil)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2008-03-07 23:51:55 +00:00
|
|
|
NSUserDefaults *defaults;
|
|
|
|
NSString *tableKey;
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
defaults = [NSUserDefaults standardUserDefaults];
|
2009-11-30 18:56:51 +00:00
|
|
|
tableKey = [NSString stringWithFormat: @"NSOutlineView Expanded Items %@",
|
2024-06-16 12:38:12 +00:00
|
|
|
_autosaveName];
|
2004-07-03 16:34:24 +00:00
|
|
|
[defaults setObject: _expandedItems forKey: tableKey];
|
|
|
|
[defaults synchronize];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _autoloadExpandedItems
|
|
|
|
{
|
2009-11-30 18:56:51 +00:00
|
|
|
if (_autosaveExpandedItems && _autosaveName != nil)
|
|
|
|
{
|
2008-03-07 23:51:55 +00:00
|
|
|
NSUserDefaults *defaults;
|
|
|
|
id config;
|
|
|
|
NSString *tableKey;
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
defaults = [NSUserDefaults standardUserDefaults];
|
2006-07-04 21:31:49 +00:00
|
|
|
tableKey = [NSString stringWithFormat: @"NSOutlineView Expanded Items %@",
|
2024-06-16 12:38:12 +00:00
|
|
|
_autosaveName];
|
2004-07-03 16:34:24 +00:00
|
|
|
config = [defaults objectForKey: tableKey];
|
2009-11-30 18:56:51 +00:00
|
|
|
if (config != nil)
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
NSEnumerator *en = [config objectEnumerator];
|
|
|
|
id item = nil;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
while ((item = [en nextObject]) != nil)
|
|
|
|
{
|
|
|
|
[self expandItem: item];
|
|
|
|
}
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all of the items under a given element.
|
|
|
|
- (void)_collectItemsStartingWith: (id)startitem
|
2024-06-16 12:38:12 +00:00
|
|
|
into: (NSMutableArray *)allChildren
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2016-10-08 21:17:53 +00:00
|
|
|
NSUInteger num;
|
|
|
|
NSUInteger i;
|
2006-07-04 21:31:49 +00:00
|
|
|
id sitem = (startitem == nil) ? (id)[NSNull null] : (id)startitem;
|
2004-07-03 16:34:24 +00:00
|
|
|
NSMutableArray *anarray;
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
anarray = NSMapGet(_itemDict, sitem);
|
2004-07-03 16:34:24 +00:00
|
|
|
num = [anarray count];
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
id anitem = [anarray objectAtIndex: i];
|
|
|
|
|
|
|
|
// Only collect the children if the item is expanded
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([self isItemExpanded: startitem])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
[allChildren addObject: anitem];
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
[self _collectItemsStartingWith: anitem
|
2024-06-16 12:38:12 +00:00
|
|
|
into: allChildren];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-27 12:21:54 +00:00
|
|
|
- (BOOL) _isItemLoaded: (id)item
|
|
|
|
{
|
|
|
|
id sitem = (item == nil) ? (id)[NSNull null] : (id)item;
|
|
|
|
id object = NSMapGet(_itemDict, sitem);
|
|
|
|
|
2024-06-16 12:38:12 +00:00
|
|
|
// NOTE: We could store the loaded items in a map to ensure we only load
|
2010-09-27 12:21:54 +00:00
|
|
|
// the children of item when it gets expanded for the first time. This would
|
|
|
|
// allow to write: return (NSMapGet(_loadedItemDict, sitem) != nil);
|
2024-06-16 12:38:12 +00:00
|
|
|
// The last line isn't truly correct because it implies an item without
|
|
|
|
// children will get incorrectly reloaded automatically on each
|
2010-09-27 12:21:54 +00:00
|
|
|
// expand/collapse.
|
|
|
|
return ([object count] != 0);
|
|
|
|
}
|
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
- (void) _loadDictionaryStartingWith: (id) startitem
|
2024-06-16 12:38:12 +00:00
|
|
|
atLevel: (NSInteger) level
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
GSKeyValueBinding *theBinding = nil;
|
2016-10-08 21:17:53 +00:00
|
|
|
NSInteger num = 0;
|
|
|
|
NSInteger i = 0;
|
2006-07-04 21:31:49 +00:00
|
|
|
id sitem = (startitem == nil) ? (id)[NSNull null] : (id)startitem;
|
2024-06-27 09:54:06 +00:00
|
|
|
NSMutableArray *anarray = nil;
|
2008-12-13 00:32:24 +00:00
|
|
|
|
2024-06-27 09:54:06 +00:00
|
|
|
theBinding = [GSKeyValueBinding getBinding: NSContentBinding
|
2024-06-19 05:46:27 +00:00
|
|
|
forObject: self];
|
|
|
|
if (theBinding != nil)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
id observedObject = [theBinding observedObject];
|
|
|
|
NSArray *children = nil;
|
2024-06-25 23:43:12 +00:00
|
|
|
|
2024-07-05 05:57:09 +00:00
|
|
|
/* 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;
|
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
/* Implement logic to build the internal data structure here using
|
|
|
|
* bindings...
|
2024-06-27 09:54:06 +00:00
|
|
|
*/
|
2024-06-19 05:46:27 +00:00
|
|
|
if ([observedObject isKindOfClass: [NSTreeController class]])
|
|
|
|
{
|
2024-10-08 16:49:59 +00:00
|
|
|
NSTreeController *tc = (NSTreeController *)observedObject;
|
|
|
|
|
2024-06-19 07:28:39 +00:00
|
|
|
if (startitem == nil)
|
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
NSTreeNode *node = (NSTreeNode *)[theBinding destinationValue];
|
|
|
|
|
2024-06-27 09:37:01 +00:00
|
|
|
/* 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
|
2024-07-01 02:07:54 +00:00
|
|
|
* GSControllerTreeProxy.
|
2024-06-27 09:37:01 +00:00
|
|
|
*/
|
2024-07-19 12:04:13 +00:00
|
|
|
children = [node mutableChildNodes];
|
2024-06-25 21:11:57 +00:00
|
|
|
num = [children count];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-07-01 01:58:37 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2024-08-02 15:20:58 +00:00
|
|
|
if ([self isExpandable: startitem]
|
2024-06-25 23:43:12 +00:00
|
|
|
&& [self isItemExpanded: startitem])
|
|
|
|
{
|
2024-07-03 08:08:43 +00:00
|
|
|
NSString *childrenKeyPath = [tc childrenKeyPathForNode: startitem];
|
2024-06-27 09:37:01 +00:00
|
|
|
|
2024-07-01 01:58:37 +00:00
|
|
|
if (childrenKeyPath != nil)
|
|
|
|
{
|
2024-07-03 08:08:43 +00:00
|
|
|
NSString *countKeyPath = [tc countKeyPathForNode: startitem];
|
2024-07-01 01:58:37 +00:00
|
|
|
|
|
|
|
children = [sitem valueForKeyPath: childrenKeyPath];
|
|
|
|
if (countKeyPath == nil)
|
|
|
|
{
|
|
|
|
num = [children count]; // get the count directly...
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
NSNumber *countValue = [sitem valueForKeyPath: countKeyPath];
|
|
|
|
num = [countValue integerValue];
|
2024-08-04 01:02:33 +00:00
|
|
|
}
|
2024-07-01 01:58:37 +00:00
|
|
|
}
|
2024-06-25 23:43:12 +00:00
|
|
|
}
|
2024-06-19 07:28:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (num > 0)
|
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
anarray = [NSMutableArray arrayWithCapacity: num];
|
2024-06-19 07:28:39 +00:00
|
|
|
NSMapInsert(_itemDict, sitem, anarray);
|
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-19 07:28:39 +00:00
|
|
|
NSMapInsert(_levelOfItems, sitem, [NSNumber numberWithInteger: level]);
|
|
|
|
|
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
id anitem = [children objectAtIndex: i];
|
|
|
|
|
2024-08-02 15:20:58 +00:00
|
|
|
if ([anitem respondsToSelector: @selector(_setParentNode:)])
|
|
|
|
{
|
|
|
|
[anitem _setParentNode: startitem];
|
|
|
|
}
|
2024-06-19 07:28:39 +00:00
|
|
|
[anarray addObject: anitem];
|
|
|
|
[self _loadDictionaryStartingWith: anitem
|
|
|
|
atLevel: level + 1];
|
|
|
|
}
|
2024-06-19 05:46:27 +00:00
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2024-06-19 05:46:27 +00:00
|
|
|
else
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2024-06-19 05:46:27 +00:00
|
|
|
/* 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.
|
|
|
|
*/
|
2024-06-27 09:37:01 +00:00
|
|
|
if (startitem == nil
|
2024-07-07 03:06:26 +00:00
|
|
|
|| ([self isExpandable: startitem]
|
2024-06-27 09:37:01 +00:00
|
|
|
&& [self isItemExpanded: startitem]))
|
2024-06-19 05:46:27 +00:00
|
|
|
{
|
|
|
|
num = [_dataSource outlineView: self
|
|
|
|
numberOfChildrenOfItem: startitem];
|
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
if (num > 0)
|
|
|
|
{
|
2024-06-25 21:11:57 +00:00
|
|
|
anarray = [NSMutableArray arrayWithCapacity: num];
|
2024-06-19 05:46:27 +00:00
|
|
|
NSMapInsert(_itemDict, sitem, anarray);
|
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
NSMapInsert(_levelOfItems, sitem, [NSNumber numberWithInteger: level]);
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-06-19 05:46:27 +00:00
|
|
|
for (i = 0; i < num; i++)
|
|
|
|
{
|
|
|
|
id anitem = [_dataSource outlineView: self
|
|
|
|
child: i
|
|
|
|
ofItem: startitem];
|
|
|
|
[anarray addObject: anitem];
|
|
|
|
[self _loadDictionaryStartingWith: anitem
|
|
|
|
atLevel: level + 1];
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_closeItem: (id)item
|
|
|
|
{
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger i, numChildren;
|
2004-07-03 16:34:24 +00:00
|
|
|
NSMutableArray *removeAll = [NSMutableArray array];
|
|
|
|
|
|
|
|
[self _collectItemsStartingWith: item into: removeAll];
|
2010-08-27 13:30:23 +00:00
|
|
|
numChildren = [removeAll count];
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
// close the item...
|
2005-11-16 11:34:25 +00:00
|
|
|
if (item != nil)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
2015-11-02 16:02:27 +00:00
|
|
|
[_expandedItems removeObjectIdenticalTo: item];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
2009-11-30 18:56:51 +00:00
|
|
|
// For the close method it doesn't matter what order they are
|
2004-07-03 16:34:24 +00:00
|
|
|
// removed in.
|
2010-08-27 13:30:23 +00:00
|
|
|
for (i = 0; i < numChildren; i++)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id child = [removeAll objectAtIndex: i];
|
2015-11-02 18:48:36 +00:00
|
|
|
[_items removeObjectIdenticalTo: child];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
2010-08-27 13:30:23 +00:00
|
|
|
[self _noteNumberOfRowsChangedBelowItem: item by: -numChildren];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void)_openItem: (id)item
|
|
|
|
{
|
2011-12-18 21:06:31 +00:00
|
|
|
NSUInteger insertionPoint, numChildren, numDescendants;
|
|
|
|
NSInteger i;
|
2010-08-27 13:30:23 +00:00
|
|
|
id object;
|
2006-07-04 21:31:49 +00:00
|
|
|
id sitem = (item == nil) ? (id)[NSNull null] : (id)item;
|
2004-07-03 16:34:24 +00:00
|
|
|
|
|
|
|
// open the item...
|
2005-11-16 11:34:25 +00:00
|
|
|
if (item != nil)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
[_expandedItems addObject: item];
|
|
|
|
}
|
|
|
|
|
2010-09-27 12:21:54 +00:00
|
|
|
// Load the children of the item if needed
|
|
|
|
if ([self _isItemLoaded: item] == NO)
|
|
|
|
{
|
2015-11-02 16:35:02 +00:00
|
|
|
[self _loadDictionaryStartingWith: item
|
2024-06-16 12:38:12 +00:00
|
|
|
atLevel: [self levelForItem: item]];
|
2010-09-27 12:21:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
object = NSMapGet(_itemDict, sitem);
|
|
|
|
numChildren = numDescendants = [object count];
|
|
|
|
|
2015-11-02 16:35:02 +00:00
|
|
|
insertionPoint = [_items indexOfObjectIdenticalTo: item];
|
2005-11-16 11:34:25 +00:00
|
|
|
if (insertionPoint == NSNotFound)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
insertionPoint = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
insertionPoint++;
|
|
|
|
}
|
|
|
|
|
2010-08-27 13:30:23 +00:00
|
|
|
for (i = numChildren-1; i >= 0; i--)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id obj = NSMapGet(_itemDict, sitem);
|
|
|
|
id child = [obj objectAtIndex: i];
|
|
|
|
|
|
|
|
// Add all of the children...
|
2005-11-16 11:34:25 +00:00
|
|
|
if ([self isItemExpanded: child])
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
|
|
|
NSUInteger numItems;
|
|
|
|
NSInteger j;
|
|
|
|
NSMutableArray *insertAll = [NSMutableArray array];
|
|
|
|
|
|
|
|
[self _collectItemsStartingWith: child into: insertAll];
|
|
|
|
numItems = [insertAll count];
|
|
|
|
numDescendants += numItems;
|
|
|
|
for (j = numItems-1; j >= 0; j--)
|
|
|
|
{
|
|
|
|
[_items insertObject: [insertAll objectAtIndex: j]
|
|
|
|
atIndex: insertionPoint];
|
|
|
|
}
|
|
|
|
}
|
2009-11-30 18:56:51 +00:00
|
|
|
|
2004-07-03 16:34:24 +00:00
|
|
|
// Add the parent
|
|
|
|
[_items insertObject: child atIndex: insertionPoint];
|
|
|
|
}
|
2010-08-27 13:30:23 +00:00
|
|
|
|
|
|
|
[self _noteNumberOfRowsChangedBelowItem: item by: numDescendants];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
- (void) _removeChildren: (id)startitem
|
|
|
|
{
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger i, numChildren;
|
2006-07-04 21:31:49 +00:00
|
|
|
id sitem = (startitem == nil) ? (id)[NSNull null] : (id)startitem;
|
2004-07-03 16:34:24 +00:00
|
|
|
NSMutableArray *anarray;
|
2009-11-30 18:56:51 +00:00
|
|
|
|
|
|
|
anarray = NSMapGet(_itemDict, sitem);
|
2010-08-27 13:30:23 +00:00
|
|
|
numChildren = [anarray count];
|
|
|
|
for (i = 0; i < numChildren; i++)
|
2004-07-03 16:34:24 +00:00
|
|
|
{
|
|
|
|
id child = [anarray objectAtIndex: i];
|
|
|
|
|
|
|
|
[self _removeChildren: child];
|
2009-11-30 18:56:51 +00:00
|
|
|
NSMapRemove(_itemDict, child);
|
2015-11-02 18:48:36 +00:00
|
|
|
[_items removeObjectIdenticalTo: child];
|
2015-11-02 16:02:27 +00:00
|
|
|
[_expandedItems removeObjectIdenticalTo: child];
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
[anarray removeAllObjects];
|
2010-08-27 13:30:23 +00:00
|
|
|
[self _noteNumberOfRowsChangedBelowItem: startitem by: -numChildren];
|
|
|
|
}
|
|
|
|
|
2016-10-08 21:17:53 +00:00
|
|
|
- (void) _noteNumberOfRowsChangedBelowItem: (id)item by: (NSInteger)numItems
|
2010-08-27 13:30:23 +00:00
|
|
|
{
|
|
|
|
BOOL selectionDidChange = NO;
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger rowIndex, nextIndex;
|
2010-08-27 13:30:23 +00:00
|
|
|
|
|
|
|
// check for trivial case
|
|
|
|
if (numItems == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// if a row below item is selected, update the selected row indexes
|
|
|
|
/* Note: We update the selected row indexes directly instead of calling
|
|
|
|
* -selectRowIndexes:extendingSelection: to avoid posting bogus selection
|
|
|
|
* did change notifications. */
|
2015-11-02 16:35:02 +00:00
|
|
|
rowIndex = [_items indexOfObjectIdenticalTo: item];
|
2010-08-27 13:30:23 +00:00
|
|
|
rowIndex = (rowIndex == NSNotFound) ? 0 : rowIndex + 1;
|
|
|
|
nextIndex = [_selectedRows indexGreaterThanOrEqualToIndex: rowIndex];
|
|
|
|
if (nextIndex != NSNotFound)
|
|
|
|
{
|
|
|
|
if (numItems > 0)
|
|
|
|
{
|
|
|
|
[_selectedRows shiftIndexesStartingAtIndex: rowIndex by: numItems];
|
|
|
|
if (_selectedRow >= rowIndex)
|
|
|
|
{
|
|
|
|
_selectedRow += numItems;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2024-06-16 12:38:12 +00:00
|
|
|
{
|
2010-08-27 13:30:23 +00:00
|
|
|
numItems = -numItems;
|
|
|
|
[_selectedRows shiftIndexesStartingAtIndex: rowIndex + numItems
|
|
|
|
by: -numItems];
|
|
|
|
if (nextIndex < rowIndex + numItems)
|
|
|
|
{
|
|
|
|
/* Don't post the notification here, as the table view is in
|
|
|
|
* an inconsistent state. */
|
|
|
|
selectionDidChange = YES;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* If the selection becomes empty after removing items and the
|
|
|
|
* receiver does not allow empty selections, select the root item. */
|
2024-06-16 12:38:12 +00:00
|
|
|
if ([_selectedRows firstIndex] == NSNotFound &&
|
|
|
|
[self allowsEmptySelection] == NO)
|
|
|
|
{
|
|
|
|
[_selectedRows addIndex: 0];
|
|
|
|
}
|
2010-08-27 13:30:23 +00:00
|
|
|
|
|
|
|
if (_selectedRow >= rowIndex + numItems)
|
|
|
|
{
|
|
|
|
_selectedRow -= numItems;
|
|
|
|
}
|
|
|
|
else if (_selectedRow >= rowIndex)
|
|
|
|
{
|
|
|
|
/* If the item at _selectedRow was removed, we arbitrarily choose
|
|
|
|
* another selected item (if there is still any). The policy
|
|
|
|
* implemented below chooses the index most close to item. */
|
2011-12-17 17:16:09 +00:00
|
|
|
NSUInteger r1 = [_selectedRows indexLessThanIndex: rowIndex];
|
|
|
|
NSUInteger r2 = [_selectedRows indexGreaterThanOrEqualToIndex: rowIndex];
|
2010-08-27 13:30:23 +00:00
|
|
|
if (r1 != NSNotFound && r2 != NSNotFound)
|
|
|
|
{
|
|
|
|
_selectedRow = (rowIndex - r1) <= (r2 - rowIndex) ? r1 : r2;
|
|
|
|
}
|
|
|
|
else if (r1 != NSNotFound)
|
|
|
|
{
|
|
|
|
_selectedRow = r1;
|
|
|
|
}
|
|
|
|
else if (r2 != NSNotFound)
|
|
|
|
{
|
|
|
|
_selectedRow = r2;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
_selectedRow = -1;
|
|
|
|
}
|
|
|
|
}
|
2024-06-16 12:38:12 +00:00
|
|
|
}
|
2010-08-27 13:30:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
[self noteNumberOfRowsChanged];
|
|
|
|
if (selectionDidChange)
|
|
|
|
{
|
|
|
|
[self _postSelectionDidChangeNotification];
|
|
|
|
}
|
2004-07-03 16:34:24 +00:00
|
|
|
}
|
|
|
|
|
2013-02-14 10:34:20 +00:00
|
|
|
- (NSCell *) preparedCellAtColumn: (NSInteger)columnIndex row: (NSInteger)rowIndex
|
2012-02-03 21:41:41 +00:00
|
|
|
{
|
|
|
|
NSCell *cell = nil;
|
2013-02-14 10:34:20 +00:00
|
|
|
|
2024-04-21 23:36:58 +00:00
|
|
|
if (_viewBased == NO)
|
2012-02-03 21:41:41 +00:00
|
|
|
{
|
2024-02-16 09:04:42 +00:00
|
|
|
NSTableColumn *tb = [_tableColumns objectAtIndex: columnIndex];
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2024-02-16 09:04:42 +00:00
|
|
|
if ([_delegate respondsToSelector:
|
|
|
|
@selector(outlineView:dataCellForTableColumn:item:)])
|
|
|
|
{
|
|
|
|
id item = [self itemAtRow: rowIndex];
|
|
|
|
cell = [_delegate outlineView: self dataCellForTableColumn: tb
|
|
|
|
item: item];
|
|
|
|
}
|
|
|
|
if (cell == nil)
|
|
|
|
{
|
|
|
|
cell = [tb dataCellForRow: rowIndex];
|
|
|
|
}
|
2012-02-03 21:41:41 +00:00
|
|
|
}
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2012-02-03 21:41:41 +00:00
|
|
|
return cell;
|
|
|
|
}
|
|
|
|
|
2024-04-28 19:04:29 +00:00
|
|
|
- (NSView *) viewAtColumn: (NSInteger)column row: (NSInteger)row makeIfNecessary: (BOOL)flag
|
2009-11-29 18:02:06 +00:00
|
|
|
{
|
2024-04-28 19:04:29 +00:00
|
|
|
NSTableColumn *tb = [_tableColumns objectAtIndex: column];
|
|
|
|
NSIndexPath *path = [NSIndexPath indexPathForItem: column
|
|
|
|
inSection: row];
|
2024-04-28 13:49:19 +00:00
|
|
|
NSView *view = [self _renderedViewForPath: path];
|
2024-04-28 19:04:29 +00:00
|
|
|
NSRect drawingRect = [self frameOfCellAtColumn: column
|
|
|
|
row: row];
|
|
|
|
id item = [self itemAtRow: row];
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2024-04-28 13:49:19 +00:00
|
|
|
if (tb == _outlineTableColumn)
|
|
|
|
{
|
2024-06-16 20:25:00 +00:00
|
|
|
drawingRect = [[GSTheme theme] drawOutlineCell: tb
|
|
|
|
outlineView: self
|
|
|
|
item: item
|
|
|
|
drawingRect: drawingRect
|
|
|
|
rowIndex: row];
|
2024-04-28 13:49:19 +00:00
|
|
|
}
|
2024-06-27 09:54:06 +00:00
|
|
|
|
2024-04-28 19:04:29 +00:00
|
|
|
if (view == nil
|
|
|
|
&& flag == YES)
|
2024-04-28 13:49:19 +00:00
|
|
|
{
|
|
|
|
if ([_delegate respondsToSelector: @selector(outlineView:viewForTableColumn:item:)])
|
|
|
|
{
|
|
|
|
view = [_delegate outlineView: self
|
|
|
|
viewForTableColumn: tb
|
|
|
|
item: item];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
view = [self _prototypeCellViewFromTableColumn: tb];
|
|
|
|
}
|
2024-06-17 01:01:41 +00:00
|
|
|
|
|
|
|
[self _setRenderedView: view forPath: path];
|
2024-04-28 19:04:29 +00:00
|
|
|
}
|
2024-04-28 13:49:19 +00:00
|
|
|
|
|
|
|
[view setFrame: drawingRect];
|
2024-06-16 12:38:12 +00:00
|
|
|
|
2024-04-28 13:49:19 +00:00
|
|
|
return view;
|
|
|
|
}
|
|
|
|
|
2009-11-29 18:02:06 +00:00
|
|
|
@end
|
2024-04-28 19:04:29 +00:00
|
|
|
|
|
|
|
@implementation NSOutlineView (Private)
|
|
|
|
/* Collapse all the items which were automatically expanded to allow drop.
|
|
|
|
*/
|
|
|
|
- (void) _autoCollapse
|
|
|
|
{
|
|
|
|
NSEnumerator *e;
|
|
|
|
id item;
|
|
|
|
|
|
|
|
e = [autoExpanded objectEnumerator];
|
|
|
|
while ((item = [e nextObject]) != nil)
|
|
|
|
{
|
|
|
|
[self collapseItem: item collapseChildren: YES];
|
|
|
|
}
|
|
|
|
[autoExpanded removeAllObjects];
|
|
|
|
}
|
|
|
|
|
|
|
|
@end
|