mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-06-02 08:30:58 +00:00
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:
parent
b70463270b
commit
8d0b8c6fbc
2 changed files with 258 additions and 263 deletions
|
@ -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>
|
2009-11-29 Wolfgang Lux <wolfgang.lux@gmail.com>
|
||||||
|
|
||||||
* Source/NSView.m (-dealloc): Fix bug where -dealloc could break
|
* Source/NSView.m (-dealloc): Fix bug where -dealloc could break
|
||||||
|
|
|
@ -66,11 +66,11 @@ static int lastVerticalQuarterPosition;
|
||||||
static int lastHorizontalHalfPosition;
|
static int lastHorizontalHalfPosition;
|
||||||
|
|
||||||
static NSRect oldDraggingRect;
|
static NSRect oldDraggingRect;
|
||||||
static int oldDropRow;
|
static id oldDropItem;
|
||||||
static int oldProposedDropRow;
|
static id currentDropItem;
|
||||||
static int currentDropRow;
|
static int oldDropIndex;
|
||||||
static int oldDropLevel;
|
static int currentDropIndex;
|
||||||
static int currentDropLevel;
|
|
||||||
static NSMutableSet *autoExpanded = nil;
|
static NSMutableSet *autoExpanded = nil;
|
||||||
static NSDate *lastDragUpdate = nil;
|
static NSDate *lastDragUpdate = nil;
|
||||||
static NSDate *lastDragChange = nil;
|
static NSDate *lastDragChange = nil;
|
||||||
|
@ -134,9 +134,18 @@ static NSImage *unexpandable = nil;
|
||||||
{
|
{
|
||||||
[self setVersion: current_version];
|
[self setVersion: current_version];
|
||||||
nc = [NSNotificationCenter defaultCenter];
|
nc = [NSNotificationCenter defaultCenter];
|
||||||
|
#if 0
|
||||||
|
/* Old Interface Builder style. */
|
||||||
collapsed = [NSImage imageNamed: @"common_outlineCollapsed"];
|
collapsed = [NSImage imageNamed: @"common_outlineCollapsed"];
|
||||||
expanded = [NSImage imageNamed: @"common_outlineExpanded"];
|
expanded = [NSImage imageNamed: @"common_outlineExpanded"];
|
||||||
unexpandable = [NSImage imageNamed: @"common_outlineUnexpandable"];
|
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];
|
autoExpanded = [NSMutableSet new];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -958,27 +967,18 @@ static NSImage *unexpandable = nil;
|
||||||
- (void) setDropItem: (id)item
|
- (void) setDropItem: (id)item
|
||||||
dropChildIndex: (int)childIndex
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
currentDropItem = item;
|
||||||
if (childIndex == NSOutlineViewDropOnItemIndex)
|
currentDropIndex = childIndex;
|
||||||
{
|
|
||||||
currentDropRow = row;
|
|
||||||
currentDropLevel = NSOutlineViewDropOnItemIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
itemAfter = [_dataSource outlineView: self
|
|
||||||
child: childIndex
|
|
||||||
ofItem: item];
|
|
||||||
currentDropRow = [_items indexOfObject: itemAfter];
|
|
||||||
currentDropLevel = [self levelForItem: itemAfter];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -988,9 +988,8 @@ static NSImage *unexpandable = nil;
|
||||||
- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
|
- (NSDragOperation) draggingEntered: (id <NSDraggingInfo>) sender
|
||||||
{
|
{
|
||||||
//NSLog(@"draggingEntered");
|
//NSLog(@"draggingEntered");
|
||||||
currentDropRow = -1;
|
oldDropItem = currentDropItem = nil;
|
||||||
// currentDropOperation = -1;
|
oldDropIndex = currentDropIndex = -1;
|
||||||
oldDropRow = -1;
|
|
||||||
lastVerticalQuarterPosition = -1;
|
lastVerticalQuarterPosition = -1;
|
||||||
oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
|
oldDraggingRect = NSMakeRect(0.,0., 0., 0.);
|
||||||
return NSDragOperationCopy;
|
return NSDragOperationCopy;
|
||||||
|
@ -1013,10 +1012,10 @@ static NSImage *unexpandable = nil;
|
||||||
int row;
|
int row;
|
||||||
int verticalQuarterPosition;
|
int verticalQuarterPosition;
|
||||||
int horizontalHalfPosition;
|
int horizontalHalfPosition;
|
||||||
|
int positionInRow;
|
||||||
int levelBefore;
|
int levelBefore;
|
||||||
int levelAfter;
|
int levelAfter;
|
||||||
int level;
|
int level;
|
||||||
BOOL dropOn = NO;
|
|
||||||
NSDragOperation dragOperation = [sender draggingSourceOperationMask];
|
NSDragOperation dragOperation = [sender draggingSourceOperationMask];
|
||||||
|
|
||||||
ASSIGN(lastDragUpdate, [NSDate date]);
|
ASSIGN(lastDragUpdate, [NSDate date]);
|
||||||
|
@ -1028,23 +1027,13 @@ static NSImage *unexpandable = nil;
|
||||||
horizontalHalfPosition =
|
horizontalHalfPosition =
|
||||||
((p.x - _bounds.origin.y) / _indentationPerLevel) * 2.;
|
((p.x - _bounds.origin.y) / _indentationPerLevel) * 2.;
|
||||||
|
|
||||||
|
row = (verticalQuarterPosition + 1) / 4;
|
||||||
row = verticalQuarterPosition;
|
positionInRow = verticalQuarterPosition % 4;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row > _numberOfRows)
|
if (row > _numberOfRows)
|
||||||
row = _numberOfRows;
|
{
|
||||||
|
row = _numberOfRows; // beyond the last real row
|
||||||
|
positionInRow = 1; // inside the root item
|
||||||
|
}
|
||||||
|
|
||||||
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
|
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
|
||||||
|
|
||||||
|
@ -1079,22 +1068,30 @@ static NSImage *unexpandable = nil;
|
||||||
{
|
{
|
||||||
int childIndex;
|
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)
|
if (horizontalHalfPosition / 2 < levelAfter)
|
||||||
horizontalHalfPosition = levelAfter * 2;
|
horizontalHalfPosition = levelAfter * 2;
|
||||||
else if (horizontalHalfPosition / 2 > levelBefore)
|
else if (horizontalHalfPosition / 2 > levelBefore)
|
||||||
horizontalHalfPosition = levelBefore * 2 + 1;
|
horizontalHalfPosition = levelBefore * 2 + 1;
|
||||||
level = horizontalHalfPosition / 2;
|
level = horizontalHalfPosition / 2;
|
||||||
|
|
||||||
|
|
||||||
lastVerticalQuarterPosition = verticalQuarterPosition;
|
|
||||||
lastHorizontalHalfPosition = horizontalHalfPosition;
|
|
||||||
|
|
||||||
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
|
//NSLog(@"horizontalHalfPosition = %d", horizontalHalfPosition);
|
||||||
//NSLog(@"verticalQuarterPosition = %d", verticalQuarterPosition);
|
//NSLog(@"verticalQuarterPosition = %d", verticalQuarterPosition);
|
||||||
|
|
||||||
currentDropRow = row;
|
if (positionInRow > 0 && positionInRow < 3)
|
||||||
currentDropLevel = level;
|
{
|
||||||
|
/* 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 i;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
@ -1120,12 +1117,9 @@ static NSImage *unexpandable = nil;
|
||||||
childIndex = j;
|
childIndex = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (YES == dropOn)
|
currentDropItem = item;
|
||||||
{
|
currentDropIndex = childIndex;
|
||||||
childIndex = NSOutlineViewDropOnItemIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
oldProposedDropRow = currentDropRow;
|
|
||||||
if ([_dataSource respondsToSelector:
|
if ([_dataSource respondsToSelector:
|
||||||
@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
|
@selector(outlineView:validateDrop:proposedItem:proposedChildIndex:)])
|
||||||
{
|
{
|
||||||
|
@ -1135,10 +1129,15 @@ static NSImage *unexpandable = nil;
|
||||||
proposedChildIndex: childIndex];
|
proposedChildIndex: childIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((currentDropRow != oldDropRow) || (currentDropLevel != oldDropLevel))
|
//NSLog(@"Drop on %@ %d", currentDropItem, currentDropIndex);
|
||||||
|
if ((currentDropItem != oldDropItem)
|
||||||
|
|| (currentDropIndex != oldDropIndex))
|
||||||
{
|
{
|
||||||
NSBezierPath *path;
|
NSBezierPath *path;
|
||||||
|
|
||||||
|
oldDropItem = currentDropItem;
|
||||||
|
oldDropIndex = currentDropIndex;
|
||||||
|
|
||||||
ASSIGN(lastDragChange, lastDragUpdate);
|
ASSIGN(lastDragChange, lastDragUpdate);
|
||||||
[self lockFocus];
|
[self lockFocus];
|
||||||
|
|
||||||
|
@ -1147,34 +1146,69 @@ static NSImage *unexpandable = nil;
|
||||||
|
|
||||||
[[NSColor darkGrayColor] set];
|
[[NSColor darkGrayColor] set];
|
||||||
|
|
||||||
//NSLog(@"currentDropLevel %d, currentDropRow %d",
|
if (currentDropIndex != NSOutlineViewDropOnItemIndex)
|
||||||
//currentDropLevel, currentDropRow);
|
|
||||||
|
|
||||||
if (currentDropLevel != 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,
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
||||||
currentDropRow * _rowHeight,
|
0,
|
||||||
[self visibleRect].size.width,
|
[self visibleRect].size.width,
|
||||||
2);
|
2);
|
||||||
}
|
}
|
||||||
else if (currentDropRow == _numberOfRows)
|
else if (row == _numberOfRows)
|
||||||
{
|
{
|
||||||
newRect = NSMakeRect([self visibleRect].origin.x,
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
||||||
currentDropRow * _rowHeight - 2,
|
row * _rowHeight - 2,
|
||||||
[self visibleRect].size.width,
|
[self visibleRect].size.width,
|
||||||
2);
|
2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
newRect = NSMakeRect([self visibleRect].origin.x,
|
newRect = NSMakeRect([self visibleRect].origin.x,
|
||||||
currentDropRow * _rowHeight - 1,
|
row * _rowHeight - 1,
|
||||||
[self visibleRect].size.width,
|
[self visibleRect].size.width,
|
||||||
2);
|
2);
|
||||||
}
|
}
|
||||||
newRect.origin.x += currentDropLevel * _indentationPerLevel;
|
level++;
|
||||||
newRect.size.width -= currentDropLevel * _indentationPerLevel;
|
newRect.origin.x += level * _indentationPerLevel;
|
||||||
|
newRect.size.width -= level * _indentationPerLevel;
|
||||||
/* The rectangle is a line across the cell indicating the
|
/* The rectangle is a line across the cell indicating the
|
||||||
* insertion position. We adjust by enough pixels to allow for
|
* insertion position. We adjust by enough pixels to allow for
|
||||||
* a ring drawn on the left end.
|
* a ring drawn on the left end.
|
||||||
|
@ -1207,8 +1241,10 @@ static NSImage *unexpandable = nil;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
row = [_items indexOfObject: currentDropItem];
|
||||||
|
level = [self levelForItem: currentDropItem];
|
||||||
newRect = [self frameOfCellAtColumn: 0
|
newRect = [self frameOfCellAtColumn: 0
|
||||||
row: currentDropRow];
|
row: row];
|
||||||
newRect.origin.x = _bounds.origin.x;
|
newRect.origin.x = _bounds.origin.x;
|
||||||
newRect.size.width = _bounds.size.width + 2;
|
newRect.size.width = _bounds.size.width + 2;
|
||||||
newRect.origin.x -= _intercellSpacing.height / 2;
|
newRect.origin.x -= _intercellSpacing.height / 2;
|
||||||
|
@ -1233,8 +1269,8 @@ static NSImage *unexpandable = nil;
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
newRect.origin.x += currentDropLevel * _indentationPerLevel;
|
newRect.origin.x += level * _indentationPerLevel;
|
||||||
newRect.size.width -= currentDropLevel * _indentationPerLevel;
|
newRect.size.width -= level * _indentationPerLevel;
|
||||||
|
|
||||||
NSFrameRectWithWidth(newRect, 2.0);
|
NSFrameRectWithWidth(newRect, 2.0);
|
||||||
// NSRectFill(newRect);
|
// NSRectFill(newRect);
|
||||||
|
@ -1244,23 +1280,6 @@ static NSImage *unexpandable = nil;
|
||||||
|
|
||||||
[self unlockFocus];
|
[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
|
else
|
||||||
|
@ -1268,10 +1287,10 @@ static NSImage *unexpandable = nil;
|
||||||
/* If we have been hovering over an item for more than half a second,
|
/* If we have been hovering over an item for more than half a second,
|
||||||
* we should expand it.
|
* we should expand it.
|
||||||
*/
|
*/
|
||||||
if (YES == dropOn
|
if (positionInRow > 0 && positionInRow < 3
|
||||||
&& [lastDragUpdate timeIntervalSinceDate: lastDragChange] >= 0.5)
|
&& [lastDragUpdate timeIntervalSinceDate: lastDragChange] >= 0.5)
|
||||||
{
|
{
|
||||||
item = [_items objectAtIndex: currentDropRow];
|
item = [_items objectAtIndex: row];
|
||||||
if ([self isExpandable: item] && ![self isItemExpanded: item])
|
if ([self isExpandable: item] && ![self isItemExpanded: item])
|
||||||
{
|
{
|
||||||
[self expandItem: item expandChildren: NO];
|
[self expandItem: item expandChildren: NO];
|
||||||
|
@ -1298,42 +1317,10 @@ static NSImage *unexpandable = nil;
|
||||||
respondsToSelector:
|
respondsToSelector:
|
||||||
@selector(outlineView:acceptDrop:item:childIndex:)])
|
@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
|
result = [_dataSource outlineView: self
|
||||||
acceptDrop: sender
|
acceptDrop: sender
|
||||||
item: item
|
item: currentDropItem
|
||||||
childIndex: childIndex];
|
childIndex: currentDropIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
[self _autoCollapse];
|
[self _autoCollapse];
|
||||||
|
@ -1469,7 +1456,8 @@ static NSImage *unexpandable = nil;
|
||||||
|
|
||||||
if (![self isExpandable: item])
|
if (![self isExpandable: item])
|
||||||
{
|
{
|
||||||
image = unexpandable;
|
// image = unexpandable;
|
||||||
|
image = nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
level = [self levelForItem: item];
|
level = [self levelForItem: item];
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue