I have a mostly successful implementation of dragging and dropping within a table. However, the two lines below will only execute the first and not the second (depending on their order). No error is generated and the script does continue beyond this point…
Code is located inside of
refUITableArrayController's insertObject_atArrangedObjectIndex_(listData, numTargetRow)
refUITableArrayController's remove_(numRow)
--> results in a new row being added but the original row is not deleted
refUITableArrayController's remove_(numRow)
refUITableArrayController's insertObject_atArrangedObjectIndex_(listData, numTargetRow-1)
--> results in a original row being deleted but the new row not being added
I’m at a loss to explain why each line will work when placed first in order but not second. Note that in the second example, I am subtracting 1 to account for the deleted original row.
In both cases, refUITableArrayController is my referencing outlet to my table’s array controller.
Also, these same two lines together work great when I’m replacing a line within a table (deleting a line and putting a new one in it’s exact location. So, it is frustrating that this doesn’t work.
The entire routine:
on tableView_acceptDrop_row_dropOperation_(tv, info, numTargetRow, proposedOp)
-- This subroutine handles a drop command
try
-- Get data from the pasteboard
set dataPasteboard to info's draggingPasteboard()
set numRowDragged to dataPasteboard's stringForType_("wtmColumnDataType")
-- Insert the moving item into the list at the drop position
set numRowDragged to ((numRowDragged as text) as integer)
set numTargetRow to (numTargetRow as integer)
-- Get the current selection's data and then delete it
tell refUITableArrayController to set recDataSelected to selectedObjects() as list
set listData to item 1 of recDataSelected
refUITableArrayController's remove_(numRow)
-- Add the deleted row at the new location
tell refUITable to set numRow to selectedRow()
refUITableArrayController's insertObject_atArrangedObjectIndex_(listData, numTargetRow)
end
If you are going to drag an object to change its order in the table view, the object is technically removed and then reinserted at the now location.
Objects are dynamically allocated and released in memory controlled by reference counting.
Creating an object sets the counter to 1, adding the object to collection objects increments the counter,
removing the object decrements the counter. When the counter reaches 0 the object and its reference will be destroyed.
Imagine the reference counter is 1:
When you first remove the object from the array the reference counter will be decremented to 0 and the object will be destroyed. It cannot be inserted
When you first insert the object at the new location in the array, the reference counter will be incremented to 2.
After removing the object it will be reset to 1 and the object is still living.
As the remove/insert operations are too fast to be noticed, you’ll never see the duplicate
What process is doing that? My drag and drop is only notifying my routine of the number dragged and the position dropped. Without my code attempting to delete and insert, the item snaps back to it’s original position. I’m not sure what you are describing…
What counter are you talking about? Is this a property of the table or the array… and how would I use that to solve my problem?
To provide drag & drop for a table view controlled by an array controller
you have to subclass NSArrayController and override/add the appropriate methods
That’s just an observation right… not the source of my problem? I have since realized that depending on if the user drags up or down (above or below the selection) I may need to adjust those numbers. But I don’t think that error you point out solves the issue… correct? Or am I missing something?
I usually check to see which number is biggest, and swap order of insertion/deletion to suit. If the new position is the bigger number, I insert first; otherwise I delete first. That way the numbers don’t need to change.
This code works great for anyone interested. Note that I am selecting the dragged row, then getting its data using selectedObjects(). That’s in case the user drags a row they are not clicked on. I’d be curious to know if there is a command to get the data from an array controller for a row that is not selected as I didn’t see one in the documentation (though I may have overlooked it). Any other suggestions on improving this code would be welcome as well.
on tableView_acceptDrop_row_dropOperation_(tv, info, numTargetRow, proposedOp)
-- This subroutine handles a drop command
try
-- Get data from the pasteboard
set dataPasteboard to info's draggingPasteboard()
set numRowDragged to dataPasteboard's stringForType_("wtmColumnDataType")
set numRowDragged to ((numRowDragged as text) as integer)
set numTargetRow to (numTargetRow as integer)
-- Adjust the target row if we are going to be deleting above the dragged row
if numRowDragged < numTargetRow then
set numTargetRow to numTargetRow - 1
end
-- Get the data from the dragged row
tell class "NSIndexSet" of current application
set recIndexSet to indexSetWithIndex_(numRowDragged)
end tell
tell refUITable to selectRowIndexes_byExtendingSelection_(recIndexSet, false)
tell refUITableArrayController to set recDataSelected to selectedObjects() as list
set listData to item 1 of recDataSelected
-- Delete the original row's location & insert it's duplicate in the dropped location
tell refUITableArrayController
remove_(numRowDragged)
insertObject_atArrangedObjectIndex_(listData, numTargetRow)
end
-- Selected the row at it's new dropped location
tell class "NSIndexSet" of current application
set recIndexSet to indexSetWithIndex_(numTargetRow)
end tell
tell refUITable to selectRowIndexes_byExtendingSelection_(recIndexSet, false)
tell refUITableArrayController to set recDataSelected to selectedObjects() as list
set listData to item 1 of recDataSelected
return true
on error
return false
end try
end tableView_acceptDrop_row_dropOperation_
You should also be implementing tableView_writeRowsWithIndexes_toPasteboard_. You don’t actually have to write the stuff to the pasteboard – you can store it in a property – but it gives you an index set of the of the items being dragged, which you can then use in tableView_acceptDrop_row_dropOperation_.
I am using tableView_writeRowsWithIndexes_toPasteboard_ but that only gives me the index of the row dragged.
on tableView_writeRowsWithIndexes_toPasteboard_(tv, indexSet, dataPasteboard)
set numRowDragged to (indexSet's firstIndex()) as integer as text
dataPasteboard's declareTypes_owner_({"wtmColumnDataType"}, me)
dataPasteboard's setString_forType_(numRowDragged, "wtmColumnDataType")
end tableView_writeRowsWithIndexes_toPasteboard_
Is that appropriate.
What I’m curious about is if there is a command that will give me the contents of any row, not just the selected one… independent of a drag operation… just in general. I would use it when the drop occurs rather than selecting the dragged row and then getting it’s values. Does an array controller have a method that allows me to get the contents of any row I please?