Make outline view DnD fully functioual.

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@29083 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
rfm 2009-11-30 18:56:51 +00:00
parent b70463270b
commit 8d0b8c6fbc
2 changed files with 258 additions and 263 deletions

View file

@ -1,3 +1,10 @@
2009-11-30 Richard Frith-Macdonald <rfm@gnu.org>
* Source/NSOutlineView.m: Improve DnD allowing drop on items as well
as inside them. Attempt to mimix OSX behavior. Simplify code.
Use triangular images similar to OSX appearance.
Doubtless needs more polishing.
2009-11-29 Wolfgang Lux <wolfgang.lux@gmail.com>
* Source/NSView.m (-dealloc): Fix bug where -dealloc could break

View file

@ -66,11 +66,11 @@ static int lastVerticalQuarterPosition;
static int lastHorizontalHalfPosition;
static NSRect oldDraggingRect;
static int oldDropRow;
static int oldProposedDropRow;
static int currentDropRow;
static int oldDropLevel;
static int currentDropLevel;
static id oldDropItem;
static id currentDropItem;
static int oldDropIndex;
static int currentDropIndex;
static NSMutableSet *autoExpanded = nil;
static NSDate *lastDragUpdate = nil;
static NSDate *lastDragChange = nil;
@ -134,9 +134,18 @@ static NSImage *unexpandable = nil;
{
[self setVersion: current_version];
nc = [NSNotificationCenter defaultCenter];
#if 0
/* Old Interface Builder style. */
collapsed = [NSImage imageNamed: @"common_outlineCollapsed"];
expanded = [NSImage imageNamed: @"common_outlineExpanded"];
unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable"];
#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
autoExpanded = [NSMutableSet new];
}
}
@ -279,7 +288,7 @@ static NSImage *unexpandable = nil;
* Expands the given item only. This is the equivalent of calling
* [NSOutlineView-expandItem:expandChildren:] with NO.
*/
- (void)expandItem: (id)item
- (void) expandItem: (id)item
{
[self expandItem: item expandChildren: NO];
}
@ -796,7 +805,7 @@ static NSImage *unexpandable = nil;
/*
* Drawing
*/
- (void)drawRow: (int)rowIndex clipRect: (NSRect)aRect
- (void) drawRow: (int)rowIndex clipRect: (NSRect)aRect
{
int startingColumn;
int endingColumn;
@ -955,30 +964,21 @@ static NSImage *unexpandable = nil;
[super drawRect: aRect];
}
- (void) setDropItem: (id) item
dropChildIndex: (int) childIndex
- (void) setDropItem: (id)item
dropChildIndex: (int)childIndex
{
int row = [_items indexOfObject: item];
id itemAfter;
if (row == NSNotFound)
if (item != nil && [_items indexOfObject: item] == NSNotFound)
{
/* 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?
*/
return;
}
if (childIndex == NSOutlineViewDropOnItemIndex)
{
currentDropRow = row;
currentDropLevel = NSOutlineViewDropOnItemIndex;
}
else
{
itemAfter = [_dataSource outlineView: self
child: childIndex
ofItem: item];
currentDropRow = [_items indexOfObject: itemAfter];
currentDropLevel = [self levelForItem: itemAfter];
}
currentDropItem = item;
currentDropIndex = childIndex;
}
/*
@ -988,9 +988,8 @@ static NSImage *unexpandable = nil;
- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
{
//NSLog(@"draggingEntered");
currentDropRow = -1;
// currentDropOperation = -1;
oldDropRow = -1;
oldDropItem = currentDropItem = nil;
oldDropIndex = currentDropIndex = -1;
lastVerticalQuarterPosition = -1;
oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
return NSDragOperationCopy;
@ -1013,10 +1012,10 @@ static NSImage *unexpandable = nil;
int row;
int verticalQuarterPosition;
int horizontalHalfPosition;
int positionInRow;
int levelBefore;
int levelAfter;
int level;
BOOL dropOn = NO;
NSDragOperation dragOperation = [sender draggingSourceOperationMask];
ASSIGN(lastDragUpdate, [NSDate date]);
@ -1028,23 +1027,13 @@ static NSImage *unexpandable = nil;
horizontalHalfPosition =
((p.x - _bounds.origin.y) / _indentationPerLevel) * 2.;
row = verticalQuarterPosition;
row = row % 4;
if (row == 1 || row == 2) dropOn = YES;
if ((verticalQuarterPosition - oldProposedDropRow * 4 <= 2)
&& (verticalQuarterPosition - oldProposedDropRow * 4 >= -3))
{
row = oldProposedDropRow;
}
else
{
row = (verticalQuarterPosition + 2) / 4;
}
row = (verticalQuarterPosition + 1) / 4;
positionInRow = verticalQuarterPosition % 4;
if (row > _numberOfRows)
row = _numberOfRows;
{
row = _numberOfRows; // beyond the last real row
positionInRow = 1; // inside the root item
}
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
@ -1079,22 +1068,30 @@ static NSImage *unexpandable = nil;
{
int childIndex;
/* Save positions to avoid executing this code when the general
* position of the mouse is unchanged.
*/
lastVerticalQuarterPosition = verticalQuarterPosition;
lastHorizontalHalfPosition = horizontalHalfPosition;
if (horizontalHalfPosition / 2 < levelAfter)
horizontalHalfPosition = levelAfter * 2;
else if (horizontalHalfPosition / 2 > levelBefore)
horizontalHalfPosition = levelBefore * 2 + 1;
level = horizontalHalfPosition / 2;
lastVerticalQuarterPosition = verticalQuarterPosition;
lastHorizontalHalfPosition = horizontalHalfPosition;
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
//NSLog(@"verticalQuarterPosition = %d", verticalQuarterPosition);
currentDropRow = row;
currentDropLevel = level;
if (positionInRow > 0 && positionInRow < 3)
{
/* We are directly over the middle of a row ... so the drop
* should be directory on the item in that row.
*/
item = [self itemAtRow: row];
childIndex = NSOutlineViewDropOnItemIndex;
}
else
{
int i;
int j = 0;
@ -1120,12 +1117,9 @@ static NSImage *unexpandable = nil;
childIndex = j;
}
if (YES == dropOn)
{
childIndex = NSOutlineViewDropOnItemIndex;
}
currentDropItem = item;
currentDropIndex = childIndex;
oldProposedDropRow = currentDropRow;
if ([_dataSource respondsToSelector:
@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
{
@ -1135,10 +1129,15 @@ static NSImage *unexpandable = nil;
proposedChildIndex: childIndex];
}
if ((currentDropRow != oldDropRow) || (currentDropLevel != oldDropLevel))
//NSLog(@"Drop on %@ %d", currentDropItem, currentDropIndex);
if ((currentDropItem != oldDropItem)
|| (currentDropIndex != oldDropIndex))
{
NSBezierPath *path;
oldDropItem = currentDropItem;
oldDropIndex = currentDropIndex;
ASSIGN(lastDragChange, lastDragUpdate);
[self lockFocus];
@ -1147,34 +1146,69 @@ static NSImage *unexpandable = nil;
[[NSColor darkGrayColor] set];
//NSLog(@"currentDropLevel %d, currentDropRow %d",
//currentDropLevel, currentDropRow);
if (currentDropLevel != NSOutlineViewDropOnItemIndex)
if (currentDropIndex != NSOutlineViewDropOnItemIndex)
{
if (currentDropRow == 0)
int numberOfChildren;
numberOfChildren = [_dataSource outlineView: self
numberOfChildrenOfItem: currentDropItem];
if (currentDropIndex >= numberOfChildren)
{
/* The index lies beyond the last item,
* so we get the last but one item and we
* use the row after it. If there are no
* children at all, we use the parent item row.
*/
if (numberOfChildren == 0)
{
row = [self rowForItem: currentDropItem];
}
else
{
item = [_dataSource outlineView: self
child: numberOfChildren - 1
ofItem: currentDropItem];
row = [self rowForItem: item] + 1;
}
}
else
{
/* Find the row for the item containing the child
* we will be dropping on.
*/
item = [_dataSource outlineView: self
child: currentDropIndex
ofItem: currentDropItem];
row = [self rowForItem: item];
}
level = [self levelForItem: item];
if (currentDropItem == nil && currentDropIndex == 0)
{
newRect = NSMakeRect([self visibleRect].origin.x,
currentDropRow * _rowHeight,
0,
[self visibleRect].size.width,
2);
}
else if (currentDropRow == _numberOfRows)
else if (row == _numberOfRows)
{
newRect = NSMakeRect([self visibleRect].origin.x,
currentDropRow * _rowHeight - 2,
row * _rowHeight - 2,
[self visibleRect].size.width,
2);
}
else
{
newRect = NSMakeRect([self visibleRect].origin.x,
currentDropRow * _rowHeight - 1,
row * _rowHeight - 1,
[self visibleRect].size.width,
2);
}
newRect.origin.x += currentDropLevel * _indentationPerLevel;
newRect.size.width -= currentDropLevel * _indentationPerLevel;
level++;
newRect.origin.x += level * _indentationPerLevel;
newRect.size.width -= level * _indentationPerLevel;
/* 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.
@ -1207,8 +1241,10 @@ static NSImage *unexpandable = nil;
}
else
{
row = [_items indexOfObject: currentDropItem];
level = [self levelForItem: currentDropItem];
newRect = [self frameOfCellAtColumn: 0
row: currentDropRow];
row: row];
newRect.origin.x = _bounds.origin.x;
newRect.size.width = _bounds.size.width + 2;
newRect.origin.x -= _intercellSpacing.height / 2;
@ -1233,8 +1269,8 @@ static NSImage *unexpandable = nil;
{
}
newRect.origin.x += currentDropLevel * _indentationPerLevel;
newRect.size.width -= currentDropLevel * _indentationPerLevel;
newRect.origin.x += level * _indentationPerLevel;
newRect.size.width -= level * _indentationPerLevel;
NSFrameRectWithWidth(newRect, 2.0);
// NSRectFill(newRect);
@ -1244,23 +1280,6 @@ static NSImage *unexpandable = nil;
[self unlockFocus];
oldDropRow = currentDropRow;
oldDropLevel = currentDropLevel;
}
else
{
if (YES == dropOn)
{
item = [_items objectAtIndex: currentDropRow];
if ([self isExpandable: item] && ![self isItemExpanded: item])
{
[self expandItem: item expandChildren: NO];
if ([self isItemExpanded: item])
{
[autoExpanded addObject: item];
}
}
}
}
}
else
@ -1268,10 +1287,10 @@ static NSImage *unexpandable = nil;
/* If we have been hovering over an item for more than half a second,
* we should expand it.
*/
if (YES == dropOn
if (positionInRow > 0 && positionInRow < 3
&& [lastDragUpdate timeIntervalSinceDate: lastDragChange] >= 0.5)
{
item = [_items objectAtIndex: currentDropRow];
item = [_items objectAtIndex: row];
if ([self isExpandable: item] && ![self isItemExpanded: item])
{
[self expandItem: item expandChildren: NO];
@ -1298,42 +1317,10 @@ static NSImage *unexpandable = nil;
respondsToSelector:
@selector(outlineView:acceptDrop:item:childIndex:)])
{
id item;
int childIndex;
if (currentDropLevel == NSOutlineViewDropOnItemIndex)
{
item = [self itemAtRow: currentDropRow];
childIndex = currentDropLevel;
}
else
{
int lvl, i, j = 0;
for (i = currentDropRow - 1; i >= 0; i--)
{
lvl = [self levelForRow: i];
if (lvl == currentDropLevel - 1)
{
break;
}
else if (lvl == currentDropLevel)
{
j++;
}
}
if (i == -1)
item = nil;
else
item = [self itemAtRow: i];
childIndex = j;
}
result = [_dataSource outlineView: self
acceptDrop: sender
item: item
childIndex: childIndex];
item: currentDropItem
childIndex: currentDropIndex];
}
[self _autoCollapse];
@ -1469,7 +1456,8 @@ static NSImage *unexpandable = nil;
if (![self isExpandable: item])
{
image = unexpandable;
// image = unexpandable;
image = nil;
}
level = [self levelForItem: item];