Adding improved implementation of NSOutlineView

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@13217 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
gcasa 2002-03-23 16:39:19 +00:00
parent 711e462420
commit 07925a8b3f
3 changed files with 287 additions and 153 deletions

View file

@ -1,3 +1,13 @@
Sat Mar 23 11:34:10 2002 Gregory John Casamento <greg_casamento@yahoo.com>
* Headers/gnustep/gui/NSOutlineView.h: Added dictionary to speed
up generation of the _items list. Also, this keeps the outline view
from being updated without an explicit call to reloadData.
* Source/NSOutlineView.m: Implemented -[NSOutlineView reloadItem:]
and -[NSOutlineView reloadItem:reloadChildren:] and improved
-[NSOutlineView reloadData]
Fri Mar 22 13:09:03 2002 Nicola Pero <n.pero@mi.flashnet.it> Fri Mar 22 13:09:03 2002 Nicola Pero <n.pero@mi.flashnet.it>
* Headers/gnustep/gui/NSRulerView.h: Merged new code from Diego * Headers/gnustep/gui/NSRulerView.h: Merged new code from Diego

View file

@ -36,6 +36,7 @@
@interface NSOutlineView : NSTableView @interface NSOutlineView : NSTableView
{ {
NSMutableDictionary *_itemDict;
NSMutableArray *_items; NSMutableArray *_items;
NSMutableArray *_expandedItems; NSMutableArray *_expandedItems;
BOOL _autoResizesOutlineColumn; BOOL _autoResizesOutlineColumn;
@ -43,7 +44,6 @@
BOOL _autosaveExpandedItems; BOOL _autosaveExpandedItems;
float _indentationPerLevel; float _indentationPerLevel;
NSTableColumn *_outlineTableColumn; NSTableColumn *_outlineTableColumn;
BOOL _shouldCollapse;
} }
// Instance methods // Instance methods

View file

@ -29,6 +29,7 @@
#import <Foundation/NSNotification.h> #import <Foundation/NSNotification.h>
#import <AppKit/NSApplication.h> #import <AppKit/NSApplication.h>
#import <AppKit/NSCell.h> #import <AppKit/NSCell.h>
#import <AppKit/NSFont.h>
#import <AppKit/NSClipView.h> #import <AppKit/NSClipView.h>
#import <AppKit/NSColor.h> #import <AppKit/NSColor.h>
#import <AppKit/NSEvent.h> #import <AppKit/NSEvent.h>
@ -64,10 +65,6 @@ static NSImage *collapsed = nil;
static NSImage *expanded = nil; static NSImage *expanded = nil;
static NSImage *unexpandable = nil; static NSImage *unexpandable = nil;
// Some necessary things which should not become ivars....
static float widest = 0.0;
@interface NSOutlineView (NotificationRequestMethods) @interface NSOutlineView (NotificationRequestMethods)
- (void) _postSelectionIsChangingNotification; - (void) _postSelectionIsChangingNotification;
- (void) _postSelectionDidChangeNotification; - (void) _postSelectionDidChangeNotification;
@ -81,7 +78,7 @@ static float widest = 0.0;
row: (int) rowIndex; row: (int) rowIndex;
@end @end
// These methods are defined in NSTableView. // These methods are private...
@interface NSOutlineView (TableViewInternalPrivate) @interface NSOutlineView (TableViewInternalPrivate)
- (void) _setSelectingColumns: (BOOL)flag; - (void) _setSelectingColumns: (BOOL)flag;
- (BOOL) _editNextEditableCellAfterRow: (int)row - (BOOL) _editNextEditableCellAfterRow: (int)row
@ -92,8 +89,82 @@ static float widest = 0.0;
- (void) _autoloadTableColumns; - (void) _autoloadTableColumns;
- (void) _openItem: (id)item; - (void) _openItem: (id)item;
- (void) _closeItem: (id)item; - (void) _closeItem: (id)item;
- (NSDictionary *) _itemDictionary;
@end @end
// static functions
static void _loadDictionary(NSOutlineView *outline,
id startitem,
NSMutableDictionary *allItems)
{
int num = [[outline dataSource] outlineView: outline
numberOfChildrenOfItem: startitem];
int i = 0;
id sitem = startitem;
if((num > 0) && !([allItems objectForKey: startitem]))
{
if(sitem == nil)
{
sitem = [NSNull null];
}
[allItems setObject: [NSMutableArray array]
forKey: sitem];
}
for(i = 0; i < num; i++)
{
id anitem = [[outline dataSource] outlineView: outline
child: i
ofItem: startitem];
id anarray = [allItems objectForKey: sitem];
[anarray addObject: anitem];
_loadDictionary(outline, anitem, allItems);
}
}
static int _levelForItem(NSDictionary *outlineDict,
id startitem,
id searchitem,
int level,
BOOL *found)
{
id sitem = startitem;
int num = 0;
int i = 0;
int finallevel = 0;
if(sitem == nil)
sitem = [NSNull null];
// get the count for the array...
num = [[outlineDict objectForKey: sitem] count];
if(*found == YES)
{
return level;
}
if(searchitem == startitem)
{
*found = YES;
return level;
}
for(i = 0; i < num; i++)
{
id anitem = [[outlineDict objectForKey: sitem] objectAtIndex: i];
finallevel = _levelForItem(outlineDict, anitem, searchitem, level + 1, found);
if(*found) break;
}
return finallevel;
}
@implementation NSOutlineView @implementation NSOutlineView
// Initialize the class when it is loaded // Initialize the class when it is loaded
@ -103,8 +174,8 @@ static float widest = 0.0;
{ {
[self setVersion: current_version]; [self setVersion: current_version];
nc = [NSNotificationCenter defaultCenter]; nc = [NSNotificationCenter defaultCenter];
collapsed = [NSImage imageNamed: @"common_outlineCollapsed.tiff"]; collapsed = [NSImage imageNamed: @"common_outlineCollapsed.tiff"];
expanded = [NSImage imageNamed: @"common_outlineExpanded.tiff"]; expanded = [NSImage imageNamed: @"common_outlineExpanded.tiff"];
unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable.tiff"]; unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable.tiff"];
} }
} }
@ -114,18 +185,20 @@ static float widest = 0.0;
{ {
[super initWithFrame: frame]; [super initWithFrame: frame];
// Initial values
_indentationMarkerFollowsCell = NO; _indentationMarkerFollowsCell = NO;
_autoResizesOutlineColumn = NO; _autoResizesOutlineColumn = NO;
_autosaveExpandedItems = NO; _autosaveExpandedItems = NO;
_indentationPerLevel = 0.0; _indentationPerLevel = 0.0;
_outlineTableColumn = nil; _outlineTableColumn = nil;
_shouldCollapse = NO; _itemDict = [NSMutableDictionary dictionary];
_items = [NSMutableArray array]; _items = [NSMutableArray array];
_expandedItems = [NSMutableArray array]; _expandedItems = [NSMutableArray array];
// Retain items // Retain items
RETAIN(_items); RETAIN(_items);
RETAIN(_expandedItems); RETAIN(_expandedItems);
RETAIN(_itemDict);
return self; return self;
} }
@ -141,27 +214,29 @@ static float widest = 0.0;
} }
// Collect all of the items under a given element. // Collect all of the items under a given element.
static void _collectItems(NSOutlineView *outline, - (void)_collectItemsStartingWith: (id)startitem
id startitem, into: (NSMutableArray *)allChildren
NSMutableArray *allChildren)
{ {
int num = [[outline dataSource] outlineView: outline int num = 0;
numberOfChildrenOfItem: startitem];
int i = 0; int i = 0;
id sitem = startitem;
if(sitem == nil)
sitem = [NSNull null];
num = [[_itemDict objectForKey: sitem] count];
for(i = 0; i < num; i++) for(i = 0; i < num; i++)
{ {
id anitem = [[outline dataSource] outlineView: outline id anitem = [[_itemDict objectForKey: sitem] objectAtIndex: i];
child: i
ofItem: startitem];
// Only collect the children if the item is expanded // Only collect the children if the item is expanded
if([outline isItemExpanded: startitem]) if([self isItemExpanded: startitem])
{ {
[allChildren addObject: anitem]; [allChildren addObject: anitem];
} }
_collectItems(outline, anitem, allChildren); [self _collectItemsStartingWith: anitem
into: allChildren];
} }
} }
@ -171,7 +246,7 @@ static void _collectItems(NSOutlineView *outline,
int i = 0; int i = 0;
NSMutableArray *removeAll = [NSMutableArray array]; NSMutableArray *removeAll = [NSMutableArray array];
_collectItems(self, item, removeAll); [self _collectItemsStartingWith: item into: removeAll];
numchildren = [removeAll count]; numchildren = [removeAll count];
// close the item... // close the item...
@ -192,11 +267,17 @@ static void _collectItems(NSOutlineView *outline,
- (void)_openItem: (id)item - (void)_openItem: (id)item
{ {
int numchildren = [_dataSource outlineView: self int numchildren = 0;
numberOfChildrenOfItem: item];
int i = 0; int i = 0;
int insertionPoint = 0; int insertionPoint = 0;
id sitem = item;
if(item == nil)
sitem = [NSNull null];
numchildren = [[_itemDict objectForKey: sitem] count];
// NSLog(@"item: %@ children: %d", sitem, numchildren);
// open the item... // open the item...
if(item != nil) if(item != nil)
{ {
@ -216,9 +297,7 @@ static void _collectItems(NSOutlineView *outline,
[self setNeedsDisplay: YES]; [self setNeedsDisplay: YES];
for(i=numchildren-1; i >= 0; i--) for(i=numchildren-1; i >= 0; i--)
{ {
id child = [_dataSource outlineView: self id child = [[_itemDict objectForKey: sitem] objectAtIndex: i];
child: i
ofItem: item];
// Add all of the children... // Add all of the children...
if([self isItemExpanded: child]) if([self isItemExpanded: child])
@ -226,9 +305,9 @@ static void _collectItems(NSOutlineView *outline,
NSMutableArray *insertAll = [NSMutableArray array]; NSMutableArray *insertAll = [NSMutableArray array];
int i = 0, numitems = 0; int i = 0, numitems = 0;
_collectItems(self, child, insertAll); [self _collectItemsStartingWith: child into: insertAll];
numitems = [insertAll count]; numitems = [insertAll count];
for(i = numitems-1; i >= 0; i--) for(i = numitems-1; i >= 0; i--)
{ {
[_items insertObject: [insertAll objectAtIndex: i] [_items insertObject: [insertAll objectAtIndex: i]
atIndex: insertionPoint]; atIndex: insertionPoint];
@ -278,37 +357,41 @@ static void _collectItems(NSOutlineView *outline,
if(collapseChildren) // collapse all if(collapseChildren) // collapse all
{ {
int numchild = [_dataSource outlineView: self int numchild = 0;
numberOfChildrenOfItem: item];
int index = 0; int index = 0;
NSMutableArray *allChildren = [NSMutableArray array];
[self _collectItemsStartingWith: item into: allChildren];
numchild = [allChildren count];
for(index = 0;index < numchild;index++) for(index = 0;index < numchild;index++)
{ {
id child = [_dataSource outlineView: self id child = [allChildren objectAtIndex: index];
child: index
ofItem: item];
NSMutableDictionary *infoDict = [NSDictionary dictionary];
[infoDict setObject: child forKey: @"NSObject"];
// Send out the notification to let observers know if([self isExpandable: child] &&
// that this is about to occur. [self isItemExpanded: child])
[nc postNotificationName: NSOutlineViewItemWillCollapseNotification
object: self
userInfo: infoDict];
if([self isItemExpanded: child])
{ {
NSMutableDictionary *childDict = [NSDictionary dictionary];
[childDict setObject: child forKey: @"NSObject"];
// Send out the notification to let observers know
// that this is about to occur.
[nc postNotificationName: NSOutlineViewItemWillCollapseNotification
object: self
userInfo: childDict];
[self _closeItem: child]; [self _closeItem: child];
// Send out the notification to let observers know that
// this is about to occur.
[nc postNotificationName: NSOutlineViewItemDidCollapseNotification
object: self
userInfo: childDict];
} }
// Send out the notification to let observers know that
// this is about to occur.
[nc postNotificationName: NSOutlineViewItemDidCollapseNotification
object: self
userInfo: infoDict];
} }
} }
[self noteNumberOfRowsChanged];
} }
[self reloadData];
} }
- (void)expandItem: (id)item - (void)expandItem: (id)item
@ -350,35 +433,41 @@ static void _collectItems(NSOutlineView *outline,
if(expandChildren) // expand all if(expandChildren) // expand all
{ {
int numchild = [_dataSource outlineView: self NSMutableArray *allChildren = nil;
numberOfChildrenOfItem: item]; int numchild = 0;
int index = 0; int index = 0;
[self _collectItemsStartingWith: item into: allChildren];
numchild = [allChildren count];
for(index = 0;index < numchild;index++) for(index = 0;index < numchild;index++)
{ {
id child = [_dataSource outlineView: self id child = [allChildren objectAtIndex: index];
child: index
ofItem: item];
// Send out the notification to let observers know that this has
// occured.
[nc postNotificationName: NSOutlineViewItemWillExpandNotification
object: self
userInfo: infoDict];
if(![self isItemExpanded: child])
{
[self _openItem: child];
}
// Send out the notification to let observers know that this has
// occured.
[nc postNotificationName: NSOutlineViewItemDidExpandNotification
object: self
userInfo: infoDict];
if([self isExpandable: child] &&
![self isItemExpanded: child])
{
NSMutableDictionary *childDict = [NSMutableDictionary dictionary];
[childDict setObject: child forKey: @"NSObject"];
// Send out the notification to let observers know that this has
// occured.
[nc postNotificationName: NSOutlineViewItemWillExpandNotification
object: self
userInfo: childDict];
[self _openItem: child];
// Send out the notification to let observers know that this has
// occured.
[nc postNotificationName: NSOutlineViewItemDidExpandNotification
object: self
userInfo: childDict];
}
} }
} }
} }
[self reloadData]; [self noteNumberOfRowsChanged];
} }
- (BOOL)indentationMarkerFollowsCell - (BOOL)indentationMarkerFollowsCell
@ -398,18 +487,11 @@ static void _collectItems(NSOutlineView *outline,
- (BOOL)isItemExpanded: (id)item - (BOOL)isItemExpanded: (id)item
{ {
id object = item; if(item == nil)
return YES;
// if the object to be expanded is nil, then the root
// object is being queried. We need to check for a
// placeholder.
if(object == nil)
{
object = @"root";
}
// Check the array to determine if it is expanded. // Check the array to determine if it is expanded.
return([_expandedItems containsObject: object]); return([_expandedItems containsObject: item]);
} }
- (id)itemAtRow: (int)row - (id)itemAtRow: (int)row
@ -417,47 +499,12 @@ static void _collectItems(NSOutlineView *outline,
return [_items objectAtIndex: row]; return [_items objectAtIndex: row];
} }
// Utility function to determine the level of an item.
static int _levelForItem(NSOutlineView *outline,
id startitem,
id searchitem,
int level,
BOOL *found)
{
int num = [[outline dataSource] outlineView: outline
numberOfChildrenOfItem: startitem];
int i = 0;
int finallevel = 0;
if(*found == YES)
{
return level;
}
if(searchitem == startitem)
{
*found = YES;
return level;
}
for(i = 0; i < num; i++)
{
id anitem = [[outline dataSource] outlineView: outline
child: i
ofItem: startitem];
finallevel = _levelForItem(outline, anitem, searchitem, level + 1, found);
if(*found) break;
}
return finallevel;
}
- (int)levelForItem: (id)item - (int)levelForItem: (id)item
{ {
if(item != nil) if(item != nil)
{ {
BOOL found = NO; BOOL found = NO;
return _levelForItem(self, nil, item, -1, &found); return _levelForItem(_itemDict, nil, item, -1, &found);
} }
return -1; return -1;
@ -473,14 +520,81 @@ static int _levelForItem(NSOutlineView *outline,
return _outlineTableColumn; return _outlineTableColumn;
} }
- (BOOL)_findItem: (id)item
childIndex: (int *)index
ofParent: (id)parent
{
NSArray *allKeys = [_itemDict allKeys];
BOOL hasChildren = NO;
NSEnumerator *en = [allKeys objectEnumerator];
id object = nil;
// initial values for return parameters
*index = NSNotFound;
parent = nil;
if([allKeys containsObject: item])
{
hasChildren = YES;
}
while((object = [en nextObject]))
{
NSArray *childArray = [_itemDict objectForKey: object];
if((*index = [childArray indexOfObject: item]) != NSNotFound)
{
parent = object;
break;
}
}
return hasChildren;
}
- (void)reloadItem: (id)item - (void)reloadItem: (id)item
{ {
[self reloadItem: item reloadChildren: YES]; [self reloadItem: item reloadChildren: NO];
} }
- (void)reloadItem: (id)item reloadChildren: (BOOL)reloadChildren - (void)reloadItem: (id)item reloadChildren: (BOOL)reloadChildren
{ {
// Nothing yet... id object = item;
id parent = nil;
id dsobj = nil;
BOOL haschildren = NO;
int index = 0;
if(object == nil)
object = [NSNull null];
// find the item
haschildren = [self _findItem: object
childIndex: &index
ofParent: parent];
dsobj = [_dataSource outlineView: self
child: index
ofItem: parent];
[[_itemDict objectForKey: parent] removeObject: item];
[[_itemDict objectForKey: parent] insertObject: dsobj atIndex: index];
if(reloadChildren && haschildren) // expand all
{
NSMutableDictionary *allChildren = [NSMutableDictionary dictionary];
_loadDictionary(self, item, allChildren);
[_itemDict addEntriesFromDictionary: allChildren];
// release the old array
if(_items != nil)
{
RELEASE(_items);
}
// regenerate the _items array based on the new dictionary
[self _openItem: nil];
}
} }
- (int)rowForItem: (id)item - (int)rowForItem: (id)item
@ -515,7 +629,7 @@ static int _levelForItem(NSOutlineView *outline,
- (BOOL)shouldCollapseAutoExpandedItemsForDeposited: (BOOL)deposited - (BOOL)shouldCollapseAutoExpandedItemsForDeposited: (BOOL)deposited
{ {
return _shouldCollapse; return YES;
} }
- (void) noteNumberOfRowsChanged - (void) noteNumberOfRowsChanged
@ -639,10 +753,25 @@ static int _levelForItem(NSOutlineView *outline,
- (void) reloadData - (void) reloadData
{ {
if([_items count] == 0) // release the old array
if(_items != nil)
{ {
[self _openItem: nil]; RELEASE(_items);
} }
if(_itemDict != nil)
{
RELEASE(_itemDict);
}
// create a new empty one
_items = RETAIN([NSMutableArray array]);
_itemDict = RETAIN([NSMutableDictionary dictionary]);
// reload all the open items...
_loadDictionary(self, nil, _itemDict);
NSLog(@"Dictionary = %@",_itemDict);
[self _openItem: nil];
[super reloadData]; [super reloadData];
} }
@ -683,7 +812,6 @@ static int _levelForItem(NSOutlineView *outline,
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autosaveExpandedItems]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_autosaveExpandedItems];
[aCoder encodeValueOfObjCType: @encode(float) at: &_indentationPerLevel]; [aCoder encodeValueOfObjCType: @encode(float) at: &_indentationPerLevel];
[aCoder encodeConditionalObject: _outlineTableColumn]; [aCoder encodeConditionalObject: _outlineTableColumn];
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &_shouldCollapse];
} }
- (id) initWithCoder: (NSCoder *)aDecoder - (id) initWithCoder: (NSCoder *)aDecoder
@ -696,7 +824,6 @@ static int _levelForItem(NSOutlineView *outline,
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autosaveExpandedItems]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_autosaveExpandedItems];
[aDecoder decodeValueOfObjCType: @encode(float) at: &_indentationPerLevel]; [aDecoder decodeValueOfObjCType: @encode(float) at: &_indentationPerLevel];
_outlineTableColumn = [aDecoder decodeObject]; _outlineTableColumn = [aDecoder decodeObject];
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_shouldCollapse];
return self; return self;
} }
@ -705,15 +832,21 @@ static int _levelForItem(NSOutlineView *outline,
{ {
NSPoint location = [theEvent locationInWindow]; NSPoint location = [theEvent locationInWindow];
NSTableColumn *tb; NSTableColumn *tb;
NSImage *image = nil;
location = [self convertPoint: location fromView: nil]; location = [self convertPoint: location fromView: nil];
_clickedRow = [self rowAtPoint: location]; _clickedRow = [self rowAtPoint: location];
_clickedColumn = [self columnAtPoint: location]; _clickedColumn = [self columnAtPoint: location];
NSLog(@"Clicked on row: %d, %d",_clickedRow, _clickedColumn); if([self isItemExpanded: [self itemAtRow: _clickedRow]])
NSLog(@"item for row: %@",[self itemAtRow: _clickedRow]); {
NSLog(@"level for row: %d",[self levelForRow: _clickedRow]); image = expanded;
}
else
{
image = collapsed;
}
tb = [_tableColumns objectAtIndex: _clickedColumn]; tb = [_tableColumns objectAtIndex: _clickedColumn];
if(tb == _outlineTableColumn) if(tb == _outlineTableColumn)
{ {
@ -725,10 +858,8 @@ static int _levelForItem(NSOutlineView *outline,
position = _indentationPerLevel * level; position = _indentationPerLevel * level;
} }
NSLog(@"x = %f", location.x); if(location.x >= position && location.x <= position + [image size].width)
if(location.x >= position && location.x <= position + 10)
{ {
NSLog(@"HIT!!");
if(![self isItemExpanded: [self itemAtRow: _clickedRow]]) if(![self isItemExpanded: [self itemAtRow: _clickedRow]])
{ {
[self expandItem: [self itemAtRow: _clickedRow]]; [self expandItem: [self itemAtRow: _clickedRow]];
@ -819,7 +950,7 @@ static int _levelForItem(NSOutlineView *outline,
NSImage *image = nil; NSImage *image = nil;
int level = 0; int level = 0;
float indentationFactor = 0.0; float indentationFactor = 0.0;
float originalWidth = drawingRect.size.width; // float originalWidth = drawingRect.size.width;
// display the correct arrow... // display the correct arrow...
if([self isItemExpanded: item]) if([self isItemExpanded: item])
@ -859,14 +990,6 @@ static int _levelForItem(NSOutlineView *outline,
drawingRect.origin.x += indentationFactor + [image size].width + 5; drawingRect.origin.x += indentationFactor + [image size].width + 5;
drawingRect.size.width -= indentationFactor + [image size].width + 5; drawingRect.size.width -= indentationFactor + [image size].width + 5;
if (widest < (drawingRect.origin.x + originalWidth))
{
widest = (drawingRect.origin.x + originalWidth);
}
else
{
// NSLog(@"Still widest = %lf", widest);
}
} }
[cell drawWithFrame: drawingRect inView: self]; [cell drawWithFrame: drawingRect inView: self];
@ -879,23 +1002,24 @@ static int _levelForItem(NSOutlineView *outline,
{ {
int index = 0; int index = 0;
for(index = 1;index <= _numberOfRows; index++) if(_autoResizesOutlineColumn)
{ {
NSRect drawingRect; float widest = 0;
for(index = 0;index < _numberOfRows; index++)
drawingRect = [self frameOfCellAtColumn: 1 {
row: index]; float offset = [self levelForRow: index] *
[self indentationPerLevel];
NSRect drawingRect = [self frameOfCellAtColumn: 0
row: index];
float length = drawingRect.size.width + offset;
NSLog(@"index = %d, offset = %f, textLength = %f",
index, offset, length);
if(widest < length) widest = length;
}
// [_outlineTableColumn setWidth: widest];
} }
[super drawRect: aRect]; [super drawRect: aRect];
// We need to resize here since all of the columns have been
// processed.
if(_autoResizesOutlineColumn)
{
// [_outlineTableColumn setWidth: widest];
widest = 0; // blank this since it was just set into the column..
}
} }