I have an NSTableView
which I wish to be able to drag and drop rows to move them, and drag and drop while holding Option (as per Apple's documentation) to copy them.
I have the following code in my view controller, which is also the dataSource
of the table view.
- (void)awakeFromNib {
[self.tableView registerForDraggedTypes:@[kRowIndexesPasteboardType]];
}
- (BOOL)tableView:(NSTableView *)tableView writeRowsWithIndexes:(NSIndexSet *)rowIndexes toPasteboard:(NSPasteboard *)pasteboard {
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:rowIndexes];
[pasteboard declareTypes:@[kRowIndexesPasteboardType] owner:self];
[pasteboard setData:data forType:kRowIndexesPasteboardType];
return YES;
}
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
// Only allow dropping above/below.
return dropOperation == NSTableViewDropAbove ? (NSDragOperationMove|NSDragOperationCopy) : NSDragOperationNone;
}
- (BOOL)tableView:(NSTableView *)tableView acceptDrop:(id <NSDraggingInfo>)info row:(NSInteger)row dropOperation:(NSTableViewDropOperation)dropOperation {
if (dropOperation == NSTableViewDropAbove) {
NSPasteboard* pasteboard = [info draggingPasteboard];
NSData* rowData = [pasteboard dataForType:kRowIndexesPasteboardType];
NSIndexSet* rowIndexes = [NSKeyedUnarchiver unarchiveObjectWithData:rowData];
BOOL copy = ???;
if (copy) {
// Copy items at rowIndexes to row.
} else {
// Move items at rowIndexes to row.
}
return YES;
}
return NO;
}
tableView:acceptDrop:row:dropOperation:
how can I tell whether the operation was a copy operation or a drop operation?As described in this discussion the value of draggingSourceOperationMask
will be NSDragOperationEvery
(unless changed) when no modifier keys are held:
When it comes back to your table as a drop validation without any modification by the user (no option key down) then any of the original options need to be considered as being possible. Your validation should then pick the operation (out of the source allowed options) that you will do based on what makes sense for the target of the drop.
Which means that the following method will return NSDragOperationCopy
when the Option key is held down, and NSDragOperationMove
otherwise:
- (NSDragOperation)tableView:(NSTableView *)tableView validateDrop:(id <NSDraggingInfo>)info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)dropOperation {
// Allow moving or copying the rows.
NSDragOperation moveOrCopy = (info.draggingSourceOperationMask == NSDragOperationCopy ? NSDragOperationCopy : NSDragOperationMove);
// Only allow dropping above/below.
return dropOperation == NSTableViewDropAbove ? moveOrCopy : NSDragOperationNone;
}
Likewise, the operation can be checked in the tableView:acceptDrop:row:dropOperation:
in a similar fashion.