Source code for this tutorial is available on github as a single zip archive, as a Team Project Set or you can browse the files online.
If you are just interested in the helper class, have a look at DnDSortingSupport.
Prerequisites:
To have something to work on I will start with a TableViewer containing some data stored in a java.util.List. It is a default TableViewer and therefore I expect you have something similar ready for your experiments.
Step 1: Add drag support
Drag and Drop support for SWT is implemented via DragSource and DropTarget instances. To define that we can drag data, we need to bind a DragSource to a Control.
DragSource dragSource = new DragSource(tableViewer.getControl(), DND.DROP_MOVE); dragSource.setTransfer(LocalSelectionTransfer.getTransfer()); dragSource.addDragListener(new DragSourceAdapter() { @Override public void dragStart(DragSourceEvent event) { event.doit = !tableViewer.getStructuredSelection().isEmpty(); } @Override public void dragSetData(DragSourceEvent event) { if (LocalSelectionTransfer.getTransfer().isSupportedType(event.dataType)) { LocalSelectionTransfer.getTransfer().setSelection(tableViewer.getStructuredSelection()); LocalSelectionTransfer.getTransfer().setSelectionSetTime(event.time & 0xFFFF); } } @Override public void dragFinished(DragSourceEvent event) { LocalSelectionTransfer.getTransfer().setSelection(null); LocalSelectionTransfer.getTransfer().setSelectionSetTime(0); } });
The first thing that happens on a drag is dragStart(). Technically the selection cannot be empty as we have to select something before we start the operation, so this implementation is merely to understand how we could deny the operation right from the start.
After the drop operation got accepted in the DropTarget (see below) we get asked to dragSetData() and define what data we are moving. setSelectionSetTime() is not needed by our DropTarget, so again this is for completeness only.
Finally we need to clean up after the operation is done.
Step 2: Add drop support
Implementation is similar like before, just now we need a DropTarget. Instead of writing our own DropTargetListener we may use a ViewerDropAdapter which covers most of the required work already.
DropTarget dropTarget = new DropTarget(tableViewer.getControl(), DND.DROP_MOVE); dropTarget.setTransfer(LocalSelectionTransfer.getTransfer()); dropTarget.addDropListener(new ViewerDropAdapter(tableViewer) { @Override public void dragEnter(DropTargetEvent event) { // make sure drag was triggered from current tableViewer if (event.widget instanceof DropTarget) { boolean isSameViewer = tableViewer.getControl().equals(((DropTarget) event.widget).getControl()); if (isSameViewer) { event.detail = DND.DROP_MOVE; setSelectionFeedbackEnabled(false); super.dragEnter(event); } else event.detail = DND.DROP_NONE; } else event.detail = DND.DROP_NONE; } @Override public boolean validateDrop(Object target, int operation, TransferData transferType) { return true; } @Override public boolean performDrop(Object target) { int location = determineLocation(getCurrentEvent()); if (location == LOCATION_BEFORE) { if (modelManipulator.insertBefore(getSelectedElement(), getCurrentTarget())) { tableViewer.refresh(); return true; } } else if (location == LOCATION_AFTER) { if (modelManipulator.insertAfter(getSelectedElement(), getCurrentTarget())) { tableViewer.refresh(); return true; } } return false; } private Object getSelectedElement() { return ((IStructuredSelection) LocalSelectionTransfer.getTransfer().getSelection()).getFirstElement(); } });
validateDrop() will be queried multiple times. We might check that we do not drop the table element on itself, but we spared this check for the current example.
performDrop() finally implements the drop operation. To keep the helper class generic I used an interface that allows to insert elements before or after another element. An implementation of it needs to be passed to the helper class.
public interface IModelManipulator { boolean insertBefore(Object source, Object target); boolean insertAfter(Object source, Object target); }The helper class comes with an implementation for java.util.List, which you may reuse.