Moving rows within table with drag and drop

In a previous post I asked how to move rows around (manually rearrange the rows) in a table.
Shane quoted there was a previous example. I indeed found the basics (also in the documentation) and used it for this implementation.

I used the Florida table example.
I think the code is working, until I have to move the selected rows to their new position !

can anybody help with the remaining code.
Here is the used code:

[code]property NSData : class “NSData” of current application
property NSKeyedArchiver : class “NSKeyedArchiver” of current application
property NSKeyedUnarchiver : class “NSKeyedUnarchiver” of current application
property NSArray : class “NSArray” of current application
property NSIndexSet : class “NSIndexSet” of current application

script Coming_to_FloridaAppDelegate
property parent : class “NSObject”

property MyPrivateTableViewDataType : "MyPrivateTableViewDataType"
property tableView : missing value
property theData : missing value --  the main property; array controller binds its content array to this
property arrayController : missing value

on applicationWillFinishLaunching_(aNotification)
– set some initial data
set my theData to {{firstName:“Ray”, lastName:“Robertson”, isAlumni:true}, {firstName:“Shane”, lastName:“Stanley”, isAlumni:true}}
set newAlumni to false – sets it to a boolean
set theMonths to {“January”, “Feb”, “March”}

            -- set datasource and register drag type(s)
	my tableView's setDataSource_(me)
	my tableView's registerForDraggedTypes_({MyPrivateTableViewDataType})
end applicationWillFinishLaunching_

on tableView_validateDrop_proposedRow_proposedDropOperation_(aTableView, info, theRow, proposedOp)
log “-------------validate drop--------------------------”
return current application’s NSDragOperationMove
end tableView_validateDrop_proposedRow_proposedDropOperation_

on tableView_writeRowsWithIndexes_toPasteboard_(aTableView, rowIndexes, pboard)
	log "------------------initiate drag from table-----------------------"
	log rowIndexes
	set archivedData to NSKeyedArchiver's archivedDataWithRootObject_(rowIndexes)
	set draggedData to NSData's dataWithData_(archivedData)
	set dragTypes to NSArray's arrayWithArray_({MyPrivateTableViewDataType} as list)
	pboard's declareTypes_owner_(dragTypes, me)
	pboard's setData_forType_(draggedData, MyPrivateTableViewDataType)
	return 1
end tableView_writeRowsWithIndexes_toPasteboard_

on tableView_acceptDrop_row_dropOperation_(aTableView, info, theRow, proposedOp)
	log "------------------accept drop-----------------------"
	-- get stuff from pasteboard
	set pb to info's draggingPasteboard()
	set rowdata to pb's dataForType_(MyPrivateTableViewDataType)
	set rowIndexes to NSKeyedUnarchiver's unarchiveObjectWithData_(rowdata)
	tell me to log "insertion point " & theRow
	log {"rows to insert ", rowIndexes}

	-- Move the specified row(s) to its(their) new location...
	-- ??????
            -- ??????
	
	return 1
end tableView_acceptDrop_row_dropOperation_

end script[/code]

When I’ve done it, I’ve always limited it to a single row – that makes things easier because then I just put the row number on the pasteboard as a string.

But the principal of the move is the same: you don’t actually move the objects, but rather you tell the array controller to insertObjects_atArrangedObjectIndex_ and to removeObjectsAtArrangedObjectIndexes_. Which you do first depends on whether the new position is before or after the old position.

You might also want to change tableView_validateDrop_proposedRow_proposedDropOperation_ to something like this:

on tableView_validateDrop_proposedRow_proposedDropOperation_(tv, info, theRow, proposedOp)
	if proposedOp = current application's NSTableViewDropOn then --  dragged on to row, so don't accept
		return current application's NSDragOperationNone
	else -- between rows
		return current application's NSDragOperationMove
	end if
end tableView_validateDrop_proposedRow_proposedDropOperation_

This is the last version of the drag and drop in a table.
I implemented it for one move at a time as Shane suggested and this is much easier than handling multiple selections.
If somebody implements the multiple selections, then I am interested in the way he/she did it.

One more thing I encountered is that when the table is unsorted, then the moves are visible.
Once the table is sorted the moves are not visible (of course), but how can I get the unsorted state again (programmatically).

Thanks for your help Shane and a happy new year for you and for all members of this forum!!!

[code]on tableView_validateDrop_proposedRow_proposedDropOperation_(aTableView, info, theRow, proposedOp)
log “-------------validate drop--------------------------”
if proposedOp = current application’s NSTableViewDropOn then – dragged on to row, so don’t accept
return current application’s NSDragOperationNone
else – between rows
return current application’s NSDragOperationMove
end if
end tableView_validateDrop_proposedRow_proposedDropOperation_

on tableView_writeRowsWithIndexes_toPasteboard_(aTableView, rowIndexes, pboard)
	try
		log "------------------initiate drag from table-----------------------"
		set archivedData to NSKeyedArchiver's archivedDataWithRootObject_(rowIndexes's firstIndex())
		set draggedData to NSData's dataWithData_(archivedData)
		set dragTypes to NSArray's arrayWithArray_({MyPrivateTableViewDataType} as list)
		pboard's declareTypes_owner_(dragTypes, me)
		pboard's setData_forType_(draggedData, MyPrivateTableViewDataType)
		return true
	on error
		return false
	end try
end tableView_writeRowsWithIndexes_toPasteboard_

on tableView_acceptDrop_row_dropOperation_(aTableView, info, theRow, proposedOp)
	try
		log "------------------accept drop-----------------------"
		-- get stuff from pasteboard
		set pb to info's draggingPasteboard()
		set rowdata to pb's dataForType_(MyPrivateTableViewDataType)
		set rowIndex to NSKeyedUnarchiver's unarchiveObjectWithData_(rowdata)
		
		-- Move the specified row(s) to its(their) new location...
		set thesel to arrayController's selectedObjects() as list
		if theRow as integer ≤ rowIndex as integer then
			arrayController's removeObjectAtArrangedObjectIndex_(rowIndex)
			arrayController's insertObject_atArrangedObjectIndex_(item 1 of thesel, theRow)
		else
			arrayController's insertObject_atArrangedObjectIndex_(item 1 of thesel, theRow)
			arrayController's removeObjectAtArrangedObjectIndex_(rowIndex)
		end if
		return true
	on error
		return false
	end try
end tableView_acceptDrop_row_dropOperation_[/code]

If you limit it to a single row, you can simplify it further by just putting the row index on the pasteboard as a string, using setString_forType_ instead of setData_forType_. So something like:

on tableView_writeRowsWithIndexes_toPasteboard_(aTableView, rowIndexes, pboard)
if indexSet's |count|() as integer is not 1 then return false -- only allow single row
set theRow to (rowIndexes's firstIndex()) as integer as text
set dragTypes to NSArray's arrayWithArray_({MyPrivateTableViewDataType} as list)
pboard's declareTypes_owner_(dragTypes, me)
pboard's setString_forType_(theRow, MyPrivateTableViewDataType)
return true
end tableView_writeRowsWithIndexes_toPasteboard_


on tableView_acceptDrop_row_dropOperation_(tv, info, theToRow, proposedOp)
	-- do drop
	try
		-- get stuff from pasteboard
		set pb to info's draggingPasteboard()
		set fromRow to (pb's stringForType_(MyPrivateTableViewDataType)) as text as integer
		set toRow to theToRow as integer
		set rowObject to theArrayController's arrangedObjects()'s objectAtIndex_(fromRow)
		if toRow > fromRow then
			theArrayController's insertObject_atArrangedObjectIndex_(rowObject, toRow)
			theArrayController's setSelectionIndex_(toRow)
			theArrayController's removeObjectAtArrangedObjectIndex_(fromRow)
		else
			theArrayController's removeObjectAtArrangedObjectIndex_(fromRow)
			theArrayController's insertObject_atArrangedObjectIndex_(rowObject, toRow)
			theArrayController's setSelectionIndex_(toRow)
		end if
		return true
	on error
		return false
	end try
end tableView_acceptDrop_row_dropOperation_

Thanks a million Shane. I used the code and it works great!!

Do you maybe have a hint about getting rid of the table sort programmatically ( showing the columns again in their unsorted state)?

Try copying the variable that holds the data, then tell the array controller to remove everything then insert the data.

This is the way I used and it works:

set tempData to my theData -- theData is the variable holding the array data tell arrayController removeObjects_(my theData) set my theData to tempData -- if theData is bound to the arraycontroller -- or the following if variable is not bound --addObjects_(tempData) end tell
And I tried this even simpler way:

tell arrayController setSortDescriptors_({}) -- reset the sort descriptors to empty values, resets to unsorted order rearrangeObjects() -- rearrange the objects in the array end
Again thanks Shane.