76: Drag between NSTableView and other applications

Problem: We have a NSTableView and want to be able to drag and drop multiple rows within the view, drag strings from the view to other applications, e.g. Safari, and drag and drop string data from other applications onto the view.

Drag and drop finder file paths onto an NSTableView

Drag and drop finder file paths onto an NSTableView

Drag and drop a Safari URL onto an NSTableView

Drag and drop a Safari URL onto an NSTableView

Drag and drop a row of text from NSTableView onto a BBEdit document

Drag and drop a row of text from NSTableView onto a BBEdit document

Drag various rows within a NSTableView

Drag various rows within a NSTableView


Answer: We use Apple's NSPasteboard Class Reference, and Pasteboard Programming Guide and Introduction to Drag and Drop and tutorial on Using Drag and Drop in Tables.

Source code

// MyData.h #import <Cocoa/Cocoa.h> @interface MyData : NSObject { NSString * nsStr; } @property (assign) NSString * nsStr; +(id)newWithString:(NSString *)pStr; -(id)initWithString:(NSString *)pStr; @end // MyData.m #import "MyData.h" @implementation MyData @synthesize nsStr; +(id)newWithString:(NSString *)pStr { return [[self alloc]initWithString:pStr]; } // end newWithString -(id)initWithString:(NSString *)pStr { if ( ! (self = [super init]) ) { NSLog(@"Error MyData initWithString"); return self; } // end if self.nsStr = pStr; return self; } // end initWithString @end // MyController.h #import <Cocoa/Cocoa.h> @interface MyController : NSObjectController { NSMutableArray * nsAryOfDataValues; IBOutlet NSTableView * nsTableViewObj; NSTextFieldCell * nsTextFieldCellObj; } @property (assign) NSMutableArray * nsAryOfDataValues; @property (assign) NSTextFieldCell * nsTextFieldCellObj; @property (assign) IBOutlet NSTableView * nsTableViewObj; @end // MyController.m #import "MyController.h" #import "MyData.h" @implementation MyController @synthesize nsAryOfDataValues; @synthesize nsTextFieldCellObj; @synthesize nsTableViewObj; #define MyPrivateTableViewDataType @"MyPrivateTableViewDataType" - (void) awakeFromNib { nsAryOfDataValues = [[NSMutableArray alloc]init]; [nsAryOfDataValues addObject: [MyData newWithString:@"Fuzzy Wuzzy was a bear"]]; [nsAryOfDataValues addObject: [MyData newWithString:@"Fuzzy Wuzzy had no hair"]]; [nsAryOfDataValues addObject: [MyData newWithString:@"So he wasn't Fuzzy Wuzzy?"]]; // define the drag types [self.nsTableViewObj registerForDraggedTypes: [NSArray arrayWithObjects:MyPrivateTableViewDataType, NSFilenamesPboardType, @"public.utf8-plain-text", nil] ]; // tell NSTableView we want to drag and drop accross applications // the default has forLocal:YES [self.nsTableViewObj setDraggingSourceOperationMask:NSDragOperationCopy forLocal:NO]; } // end awakeFromNib // these are called by the table(s) - (int)numberOfRowsInTableView:(NSTableView *)tableView { if (tableView == nsTableViewObj) { return [nsAryOfDataValues count]; } // end if return 0; } // end numberOfRowsInTableView - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(int)row { if (tableView == nsTableViewObj) { MyData * zData = [nsAryOfDataValues objectAtIndex:row]; return zData.nsStr; } // end if return NULL; } // end tableView:objectValueForTableColumn:tableColumn - (void)tableView:(NSTableView *)aTableView setObjectValue:(id)anObject forTableColumn:(NSTableColumn *)aTableColumn row:(int)rowIndex { if (aTableView == nsTableViewObj) { MyData * zData = [nsAryOfDataValues objectAtIndex:rowIndex]; zData.nsStr = (NSString *)anObject; [nsAryOfDataValues replaceObjectAtIndex:rowIndex withObject:zData]; } // end if } // end tableView:setObjectValue:forTableColumn:row: // =========================================== // drag operation stuff // =========================================== - (BOOL) tableView:(NSTableView *)pTableView writeRowsWithIndexes:(NSIndexSet *)pIndexSetOfRows toPasteboard:(NSPasteboard*)pboard { // this allows us to drag rows around within the table // we copy the row numbers to be dragged to the pasteboard. NSData *zNSIndexSetData = [NSKeyedArchiver archivedDataWithRootObject:pIndexSetOfRows]; [pboard declareTypes:[NSArray arrayWithObject:MyPrivateTableViewDataType] owner:self]; [pboard setData:zNSIndexSetData forType:MyPrivateTableViewDataType]; // this is to allow us to drag string data to other applications, e.g. safari, bbedit // We don't do this if more than one row is selected if ([pIndexSetOfRows count] > 1) { return YES; } // end if NSInteger zIndex = [pIndexSetOfRows firstIndex]; MyData * zDataObj = [self.nsAryOfDataValues objectAtIndex:zIndex]; NSString *zDataString = zDataObj.nsStr; [pboard setString:zDataString forType:@"public.utf8-plain-text"]; return YES; } - (NSDragOperation)tableView:(NSTableView*)pTableView validateDrop:(id )info proposedRow:(NSInteger)row proposedDropOperation:(NSTableViewDropOperation)op { // Add code here to validate the drop return NSDragOperationEvery; } - (BOOL)tableView:(NSTableView *)pTableView acceptDrop:(id )info row:(NSInteger)pRow dropOperation:(NSTableViewDropOperation)operation { NSPasteboard* zPBoard = [info draggingPasteboard]; NSArray *supportedTypes = [NSArray arrayWithObjects: MyPrivateTableViewDataType, NSFilenamesPboardType, @"public.utf8-plain-text", NSPasteboardTypeString, nil]; NSString * zStrAvailableType = [zPBoard availableTypeFromArray:supportedTypes]; if ([zStrAvailableType compare:MyPrivateTableViewDataType] == NSOrderedSame ) { NSData* zRowNSData = [zPBoard dataForType:MyPrivateTableViewDataType]; NSIndexSet* zNSIndexSetRows = [NSKeyedUnarchiver unarchiveObjectWithData:zRowNSData]; // this is not the simplest of all logics // since the rows can be anywhere in the table as may be the // point of insertion. // 1. put the selected elements into a new array NSMutableArray *zArySelectedElements = [[NSMutableArray alloc]init]; NSInteger i; for (i=[zNSIndexSetRows firstIndex]; i <= [zNSIndexSetRows lastIndex];i++) { if ( ! [zNSIndexSetRows containsIndex:i]) { continue; } // end if [zArySelectedElements addObject:[self.nsAryOfDataValues objectAtIndex:i]]; } // end for // 2. construct a new array of objects up to point of insertion NSMutableArray *zNewAry = [[NSMutableArray alloc]init]; for (i = 0; i < pRow; i++) { if ([zNSIndexSetRows containsIndex:i]) { continue; } // end if [zNewAry addObject:[self.nsAryOfDataValues objectAtIndex:i]]; } // end for // include the selection [zNewAry addObjectsFromArray:zArySelectedElements]; // and add the rest for (i = pRow; i < [self.nsAryOfDataValues count]; i++) { if ([zNSIndexSetRows containsIndex:i]) { continue; } // end if [zNewAry addObject:[self.nsAryOfDataValues objectAtIndex:i]]; } // end for // 3. substitute self.nsAryOfDataValues = zNewAry; // done [self.nsTableViewObj noteNumberOfRowsChanged]; [self.nsTableViewObj reloadData]; return YES; } // end if ...MyPrivateTableViewDataType if ([zStrAvailableType compare:@"public.utf8-plain-text"] == NSOrderedSame ) { NSData* zStringData = [zPBoard dataForType:@"public.utf8-plain-text"]; NSString * aStr = [[NSString alloc] initWithData:zStringData encoding:NSASCIIStringEncoding]; // create new entry // put string into the entry MyData * zDataObj = [MyData newWithString:aStr]; [self.nsAryOfDataValues insertObject:zDataObj atIndex:pRow]; [self.nsTableViewObj noteNumberOfRowsChanged]; [self.nsTableViewObj reloadData]; return YES; } // end if .. public.utf8-plain-text if ([zStrAvailableType compare:NSFilenamesPboardType] == NSOrderedSame ) { NSArray* zPListFilesAry = [zPBoard propertyListForType:NSFilenamesPboardType]; NSInteger i; for (i = 0; i < [zPListFilesAry count]; i++) { NSString * zStrFilePath = [zPListFilesAry objectAtIndex:i]; NSString * aStrPath = [zStrFilePath stringByStandardizingPath]; MyData * zDataObj = [MyData newWithString:aStrPath]; [self.nsAryOfDataValues insertObject:zDataObj atIndex:pRow++]; } // end for [self.nsTableViewObj noteNumberOfRowsChanged]; [self.nsTableViewObj reloadData]; return YES; } // end if ... NSFilenamesPboardType return NO; } // end tableView:acceptDrop:row:dropOperation: @end

Example source code



Please send me your comments

If you include your e-mail I may reply!  

Page last modified: 18:58 Sunday 12th. May 2013

Julius Guzy

Paintings & Drawings

  • Link to picture of tomato plants

animatedPaint