Skip to content

Commit

Permalink
* added drag and drop support for the document table view
Browse files Browse the repository at this point in the history
* using drag and drop, (single and multiple selections of) table view rows can now be moved
* dragged table view rows are now also placed in the NSPasteBoard in textual CSV form (enables dropping into other applications)
  • Loading branch information
martinjankoehler authored and Martin Koehler committed Jul 1, 2017
1 parent 0456acb commit d860c6e
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 1 deletion.
6 changes: 6 additions & 0 deletions Table Tool.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
5B338F741EF03D8000D40406 /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 5B338F731EF03D8000D40406 /* Constants.m */; };
AA58C3F91CFC4DC600106C66 /* TTFormatViewControllerAccessory.xib in Resources */ = {isa = PBXBuildFile; fileRef = AA58C3F81CFC4DC600106C66 /* TTFormatViewControllerAccessory.xib */; };
AAB016721D057426005E3F7A /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = AAB016711D057426005E3F7A /* Credits.rtf */; };
E109B48B1B5E4598005B4959 /* CSVConfiguration.m in Sources */ = {isa = PBXBuildFile; fileRef = E109B48A1B5E4598005B4959 /* CSVConfiguration.m */; };
Expand Down Expand Up @@ -43,6 +44,8 @@

/* Begin PBXFileReference section */
36007A091D083BA000898043 /* Table Tool.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = "Table Tool.entitlements"; sourceTree = "<group>"; };
5B338F721EF03D8000D40406 /* Constants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = "<group>"; };
5B338F731EF03D8000D40406 /* Constants.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = "<group>"; };
AA41D3431CF2E7CE00CB3E7D /* Table Tool-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Table Tool-Bridging-Header.h"; sourceTree = "<group>"; };
AA58C3F81CFC4DC600106C66 /* TTFormatViewControllerAccessory.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = TTFormatViewControllerAccessory.xib; sourceTree = "<group>"; };
AAB016711D057426005E3F7A /* Credits.rtf */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.rtf; path = Credits.rtf; sourceTree = "<group>"; };
Expand Down Expand Up @@ -120,6 +123,8 @@
E1CC84DD1B4A5F2A00ED8314 /* Table Tool */ = {
isa = PBXGroup;
children = (
5B338F721EF03D8000D40406 /* Constants.h */,
5B338F731EF03D8000D40406 /* Constants.m */,
36007A091D083BA000898043 /* Table Tool.entitlements */,
E1CEF8811B6B63380083B957 /* ToolbarIcons.h */,
E1CEF8821B6B63380083B957 /* ToolbarIcons.m */,
Expand Down Expand Up @@ -296,6 +301,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5B338F741EF03D8000D40406 /* Constants.m in Sources */,
E1CEF8831B6B63380083B957 /* ToolbarIcons.m in Sources */,
E1CC84E71B4A5F2A00ED8314 /* Document.m in Sources */,
E16BDFCE1B4E74960024F0BD /* CSVReader.m in Sources */,
Expand Down
13 changes: 13 additions & 0 deletions Table Tool/Constants.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Constants.h
// Table Tool
//
// Created by Martin Koehler on 12.06.17.
// Copyright (c) 2017 Egger Apps. All rights reserved.
//

#include <Cocoa/Cocoa.h>

#pragma mark Pasteboard Types

extern NSString *TTRowInternalPboardType;
13 changes: 13 additions & 0 deletions Table Tool/Constants.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// Constants.h
// Table Tool
//
// Created by Martin Koehler on 12.06.17.
// Copyright (c) 2017 Egger Apps. All rights reserved.
//

#include "Constants.h"

#pragma mark Initializing Globals for Pasteboard Types

NSString *TTRowInternalPboardType = @"Table Tool Row Internal PasteBoard Type";
125 changes: 124 additions & 1 deletion Table Tool/Document.m
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// Copyright (c) 2015 Egger Apps. All rights reserved.
//

#import "Constants.h"
#import "Document.h"
#import "CSVReader.h"
#import "CSVWriter.h"
Expand All @@ -17,12 +18,18 @@ @interface Document () {
NSCell *dataCell;
NSData *savedData;
NSMutableArray *firstRow;

NSError *readingError;
NSView *errorControllerView;
NSString *errorCode5;

BOOL didNotMoveColumn;
BOOL newFile;
BOOL enableEditing;

NSArray *validPBoardTypes;
NSIndexSet *draggedRowIndexes;

TTFormatViewController *inputController;
TTErrorViewController *errorController;
TTFormatViewController *popoverViewController;
Expand All @@ -46,6 +53,9 @@ - (instancetype)init {
newFile = YES;
errorCode5 = @"Your are not allowed to save while the input format has an error. Configure the format manually, until no error occurs.";
_didSave = NO;

[self initValidPBoardTypes];

[self addObserver:self forKeyPath:@"fileURL" options:0 context:nil];
[self addObserver:self forKeyPath:@"didSave" options:0 context:nil];
}
Expand All @@ -62,6 +72,10 @@ - (void)windowControllerDidLoadNib:(NSWindowController *)aController {
statusBarFormatViewController.delegate = self;
}

[self.tableView setDraggingSourceOperationMask:NSDragOperationMove forLocal:YES];
[self.tableView setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO];
[self.tableView registerForDraggedTypes:[NSArray arrayWithObject:TTRowInternalPboardType]];

if(newFile){
_maxColumnNumber = 3;
[self updateTableColumns];
Expand Down Expand Up @@ -312,6 +326,115 @@ -(NSArray *)getColumnsOrder{
return columnsOrder.copy;
}

#pragma mark - tableViewDataSource (optional methods) - drag & drop

-(void)initValidPBoardTypes
{
validPBoardTypes = [NSArray arrayWithObjects:TTRowInternalPboardType,
NSStringPboardType,
nil];
}

- (BOOL)tableView:(NSTableView *)tableView
writeRowsWithIndexes:(NSIndexSet *)rowIndexes
toPasteboard:(NSPasteboard *)pboard
{
if (rowIndexes == nil)
return NO;

draggedRowIndexes = rowIndexes; // internally store dragged row indexes

[pboard declareTypes:validPBoardTypes owner: nil];

// TTRowInternalPboardType is used for app internal movement of rows
[pboard setData:[NSData data] forType:TTRowInternalPboardType];

// textual dragging support, for example supporting drag & drop from Table Tool to other apps such as TextEdit etc.
NSArray *rowDataAtIndexes = [_data objectsAtIndexes:rowIndexes];
CSVWriter *writer = [[CSVWriter alloc] initWithDataArray:rowDataAtIndexes columnsOrder:[self getColumnsOrder] configuration:_outputConfig];
NSError *error = nil;
NSData *csvAsText = [writer writeDataWithError:&error];
if (error == nil) {
[pboard setData:csvAsText forType:NSPasteboardTypeString];
}

return YES;
}

- (NSDragOperation)tableView:(NSTableView *)tableView
validateDrop:(id<NSDraggingInfo>)info
proposedRow:(NSInteger)row
proposedDropOperation:(NSTableViewDropOperation)dropOperation
{
NSPasteboard *pboard = [info draggingPasteboard];
NSString *type = [pboard availableTypeFromArray:validPBoardTypes];
if ([type isEqualToString:TTRowInternalPboardType] &&
[info draggingSource] == self.tableView &&
tableView == self.tableView)
{ // NOTE: for now, only drag & drop within the same tableView is supported
switch (dropOperation) {
case NSTableViewDropAbove: return NSDragOperationMove;
case NSTableViewDropOn: return NSDragOperationNone;
}
}

return NSDragOperationNone;
}

- (BOOL)tableView:(NSTableView *)tableView
acceptDrop:(id<NSDraggingInfo>)info
row:(NSInteger)rowBeforeRemoval
dropOperation:(NSTableViewDropOperation)dropOperation
{
NSPasteboard *pboard = [info draggingPasteboard];
NSString *type = [pboard availableTypeFromArray:validPBoardTypes];
if ([type isEqualToString:TTRowInternalPboardType]) {
if ([info draggingSource] == self.tableView &&
tableView == self.tableView)
{ // NOTE: for now, only drag & drop within the same tableView is supported
const NSUInteger draggedRowCount = [draggedRowIndexes count];

NSUInteger countOfIndicesBeforeDestinationRow = [draggedRowIndexes countOfIndexesInRange:NSMakeRange(0, rowBeforeRemoval)];
NSInteger destinationRow = rowBeforeRemoval - countOfIndicesBeforeDestinationRow;
NSArray *draggedRowData = [_data objectsAtIndexes:draggedRowIndexes];

NSRange postInsertReselectionRange = NSMakeRange(destinationRow, draggedRowCount);
NSIndexSet *postInsertReselectionSet = [NSIndexSet indexSetWithIndexesInRange:postInsertReselectionRange];

NSUndoManager *undoManager = [self undoManager];
if (![undoManager isUndoing]) {
[undoManager setActionName:(draggedRowCount >= 2) ? @"Moving Rows" : @"Moving Row"];
}
[[self.undoManager prepareWithInvocationTarget:self] restoreRowsWithContent:[draggedRowData copy] atIndexes:[draggedRowIndexes copy]];
[[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:postInsertReselectionSet];

[NSAnimationContext runAnimationGroup:^(NSAnimationContext *context) {
[self.tableView beginUpdates];

[_data removeObjectsAtIndexes:draggedRowIndexes];

NSEnumerator *reverseEnumerator = [draggedRowData reverseObjectEnumerator];
id nextRowToInsert = nil;
while (nextRowToInsert = [reverseEnumerator nextObject]) {
[_data insertObject:nextRowToInsert atIndex:destinationRow];
}

[self.tableView endUpdates];
} completionHandler:^{
[_tableView selectRowIndexes:postInsertReselectionSet byExtendingSelection:NO];

[self dataGotEdited];
}];

draggedRowIndexes = nil;

return YES;
}
}

return NO;
}

#pragma mark - updateTableView

-(void)updateTableColumns {
Expand Down Expand Up @@ -571,7 +694,7 @@ -(void)deleteRowsAtIndexes:(NSIndexSet *)rowIndexes{
}];
}

-(void)restoreRowsWithContent:(NSMutableArray *)rowContents atIndexes:(NSIndexSet *)rowIndexes {
-(void)restoreRowsWithContent:(NSArray *)rowContents atIndexes:(NSIndexSet *)rowIndexes {

[[self.undoManager prepareWithInvocationTarget:self] deleteRowsAtIndexes:rowIndexes];

Expand Down

0 comments on commit d860c6e

Please sign in to comment.