2

I am trying to incorporate a drag-and-drop feature with our product. I have created a new custom view which has a tree structure and am interested in dropping the content from this tree to an already existing tree structure within the application itself.

I have used the same custom transfer type which the product is expecting. However, while debugging I found out that neither the DragSourceEvent's data or datatype are getting set. Both are null values. Moreover my dragSetData is not getting called as well.

Requesting you to provide me some suggestions....

Baz
  • 36,440
  • 11
  • 68
  • 94
Pavan Dittakavi
  • 3,013
  • 5
  • 27
  • 47

2 Answers2

0

The data of the event is only set after the drop gets the data. This should happen automatically, but it didn't work for me at first, too.

After some research I came up with a workaround, although I am not sure if this is intended by the framework. I extended the org.eclipse.jface.viewers.ViewerDropAdapter in my solution, but it should also work with extending org.eclipse.swt.dnd.DropTargetAdapter:

public class MyViewerDropAdapter extends ViewerDropAdapter {
    // implement the abstract methods

    // the next two methods are the important part: 
    // override the non-abstract methods dragEnter and dragOperationChanged
    // if your user just moves the mouse without pressing a key, event.detail
    // equals DND.DROP_DEFAULT. In this case you have to change detail to
    // your default operation (in this case DROP_COPY)
    @Override
    public void dragEnter(DropTargetEvent event) {
       if (event.detail == DND.DROP_DEFAULT) {
         if ((event.operations & DND.DROP_COPY) != 0) {
          event.detail = DND.DROP_COPY;
         } else {
          event.detail = DND.DROP_NONE;
         }
        }
      super.dragEnter(event);
    }

    // the same for this method. It will be called, when the user
    // presses the CTRL or SHIFT button (on windows) while dragging.
    // We need it here to set the DROP_DEFAULT back to DROP_COPY.
    // Otherwise your default will go back to DROP_NONE after the user
    // released the key.
    @Override
   public void dragOperationChanged(DropTargetEvent event) {
      if (event.detail == DND.DROP_DEFAULT) {
       if ((event.operations & DND.DROP_COPY) != 0) {
        event.detail = DND.DROP_COPY;
       } else {
        event.detail = DND.DROP_NONE;
       }
      }
   }
}

When you then add the drop support to your tree viewer, be sure to set the DND.DROP_DEFAULT operation in addition to the others (not mentioned in the API doc afaik):

myTreeViewer.addDropSupport(DND.DROP_COPY | DND.DROP_DEFAULT,
            new Transfer[] { myByteTransfer.getInstance() },
            new MyViewerDropAdapter(myTreeViewer));

After I did this, dragSetData got called and everything worked as expected.

A console output shows you the order of some of the events:

Start Drag
dragEnter called
dragSetData called
drop called
performDrop called
Finshed Drag
lightwalker
  • 3
  • 1
  • 3
0

Solution

If we want for user to drag entries without using meta keys we should always allow system-default drag operation both on drag and drop ends. On Windows this happen to be a DND.DRAG_MOVE operation (and I believe the same it true for other platforms).

source.addDragSupport(DND.DROP_MOVE, new Transfer[] { DragSelectionListener.getTransfer() }, new DragSelectionListener(viewer));
target.addDropSupport(DND.DROP_MOVE, new Transfer[]{ DropListListener.getTransfer() }, new DropListListener(viewer));

Explanation

Three sets of operations should intersect:

  • operations that drag source allow (are set with StructuredViewer.addDragSupport())
  • operations that drag target support (are set with StructuredViewer.addDropSupport())
  • operation selected by user (depends on which meta keys were pressed at the moment of drop)

only then drop target will validate the drop received and drag source would be requested for data.

Investigation

The handling of meta-keys is done in org.eclipse.swt.dnd.DropTarget.setEventData() the line

operations[0] = osToOp(operations[0]) & style;
if (operations[0] == DND.DROP_NONE) return false;

intersects style from droptarget with value which came from system drop operation is based on dragsource but stripped of DND.DRAG_DEFAULT. If those two do not intersect, operation is aborted.

Further down this set is compared with the one calculated from meta keys pressed. Operation could be aborted again.

if ((operation & operations[0]) == 0) operation = DND.DROP_NONE;

This behaviour can be controlled passing DND.DROP_DEFAULT to addDropSupport(), but that is even worse as the user selected operation would be then compared to DND.DROP_MOVE, which would be filtered out earlier if not used as an argument of addDropSupport().

I think the DND.DROP_DEFAULT handling is broken and should not be relied upon. It's use if effectively prevented by first condition.

Basilevs
  • 22,440
  • 15
  • 57
  • 102