1""" 2Abstract: Custom that handles Drag and Drop for table views by acting as a datasource. 3""" 4from Cocoa import * 5import objc 6 7class DragSupportDataSource (NSObject): 8 # all the table views for which self is the datasource 9 registeredTableViews = objc.ivar() 10 11 def init(self): 12 self = super(DragSupportDataSource, self).init() 13 if self is None: 14 return None 15 16 self.registeredTableViews = NSMutableSet.alloc().init() 17 return self; 18 19 # ******** table view data source necessities ********* 20 21 # We use this method as a way of registering for drag types for all 22 # the table views that will depend on us to implement D&D. Instead of 23 # setting up innumerable outlets, simply depend on the fact that every 24 # table view will ask its datasource for number of rows. 25 def numberOfRowsInTableView_(self, aTableView): 26 # this is potentially slow if there are lots of table views 27 if not self.registeredTableViews.containsObject_(aTableView): 28 aTableView.registerForDraggedTypes_([NSStringPboardType]) 29 #Cache the table views that have "registered" with us. 30 self.registeredTableViews.addObject_(aTableView) 31 32 # return 0 so the table view will fall back to getting data from 33 # its binding 34 return 0 35 36 def tableView_objectValueForTableColumn_row_(self, aView, aColumn, rowIdx): 37 # return None so the table view will fall back to getting data from 38 # its binding 39 return None 40 41 # put the managedobject's ID on the pasteboard as an URL 42 def tableView_writeRowsWithIndexes_toPasteboard_(self, tv, rowIndexes, pboard): 43 success = False 44 45 infoForBinding = tv.infoForBinding_(NSContentBinding) 46 if infoForBinding is not None: 47 arrayController = infoForBinding.objectForKey_(NSObservedObjectKey) 48 objects = arrayController.arrangedObjects().objectsAtIndexes_( 49 rowIndexes) 50 51 objectIDs = NSMutableArray.array() 52 for i in xrange(objects.count()): 53 item = objects[i] 54 objectID = item.objectID() 55 representedURL = objectID.URIRepresentation() 56 objectIDs.append(representedURL) 57 58 pboard.declareTypes_owner_([NSStringPboardType], None) 59 pboard.addTypes_owner_([NSStringPboardType], None) 60 success = pboard.setString_forType_( 61 objectIDs.componentsJoinedByString_(', '), NSStringPboardType) 62 63 return success 64 65 # *************** actual drag and drop work ***************** 66 def tableView_validateDrop_proposedRow_proposedDropOperation_( 67 self, tableView, info, row, operation): 68 69 # Avoid drag&drop on self. This might be interersting to enable in 70 # light of ordered relationships 71 if info.draggingSource() is not tableView: 72 return NSDragOperationCopy 73 else: 74 return NSDragOperationNone 75 76 def tableView_acceptDrop_row_dropOperation_( 77 self, tableView, info, row, operation): 78 79 success = False 80 urlStrings = info.draggingPasteboard().stringForType_(NSStringPboardType) 81 82 # get to the arraycontroller feeding the destination table view 83 destinationContentBindingInfo = tableView.infoForBinding_(NSContentBinding) 84 if destinationContentBindingInfo is not None: 85 86 destinationArrayController = destinationContentBindingInfo.objectForKey_(NSObservedObjectKey) 87 sourceArrayController = None 88 89 # check for the arraycontroller feeding the source table view 90 contentSetBindingInfo = destinationArrayController.infoForBinding_(NSContentSetBinding) 91 if contentSetBindingInfo is not None: 92 sourceArrayController = contentSetBindingInfo.objectForKey_(NSObservedObjectKey) 93 94 # there should be exactly one item selected in the source controller, otherwise the destination controller won't be able to manipulate the relationship when we do addObject: 95 if (sourceArrayController is not None) and (sourceArrayController.selectedObjects().count() == 1): 96 context = destinationArrayController.managedObjectContext() 97 destinationControllerEntity = NSEntityDescription.entityForName_inManagedObjectContext_(destinationArrayController.entityName(), context) 98 99 items = urlStrings.split(', ') 100 itemsToAdd = [] 101 102 for i in xrange(len(items)): 103 urlString = items[i] 104 105 # take the URL and get the managed object - assume 106 # all controllers using the same context 107 url = NSURL.URLWithString_(urlString) 108 objectID = context.persistentStoreCoordinator().managedObjectIDForURIRepresentation_(url) 109 if objectID is not None: 110 object = context.objectRegisteredForID_(objectID) 111 112 # make sure objects match the entity expected by 113 # the destination controller, and not already there 114 if object is not None and (object.entity() is destinationControllerEntity) and not (destinationArrayController.arrangedObjects().containsObject_(object)): 115 itemsToAdd.append(object) 116 117 if len(itemsToAdd) > 0: 118 destinationArrayController.addObjects_(itemsToAdd) 119 success = True 120 121 return success 122