This tutorial describes the usage of drag and drop in the Eclipse framework. The article assumes that you are familiar with SWT and Eclipse plug-in or Eclipse RCP development.
1. Using drag and drop in SWT
SWT supports drag and drop based on transfer types defined based on the org.eclipse.swt.dnd.Transfer
class.
Every widget element can define that it provides a certain transfer type as source.
It can also define that it accepts a transfer type as destination.
Eclipse provides several pre-defined transfer types.
To define your custom transfer type it is recommended to subclass the org.eclipse.swt.dnd.ByteArrayTransfer
class.
A drag source
is the provider of the drag and drop data as well as the originator of the drag and drop operation.
A drop target
receives data in a drag and drop operation.
The drop target receives the data provided by the drag source.
it can be another location in the same widget, a different widget, or a different application.
For example, you can drag text from your application and drop it on an email application, or you could drag an item in a tree and drop it below a different node in the same tree.
The following demonstrates how to provide text data from a label.
import org.eclipse.swt.dnd.*;
//Enable a label as a Drag Source
final Label dragLabel = new Label(shell, SWT.BORDER);
dragLabel.setText("text to be transferred");
// Allow data to be copied or moved from the drag source
int operations = DND.DROP_MOVE | DND.DROP_COPY;
DragSource source = new DragSource(dragLabel, operations);
// Provide data in Text format
Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
source.setTransfer(types);
source.addDragListener(new DragSourceListener() {
public void dragStart(DragSourceEvent event) {
// Only start the drag if there is actually text in the
// label - this text will be what is dropped on the target.
if (dragLabel.getText().length() == 0) {
event.doit = false;
}
}
public void dragSetData(DragSourceEvent event) {
// Provide the data of the requested type.
if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
event.data = dragLabel.getText();
}
}
public void dragFinished(DragSourceEvent event) {
// If a move operation has been performed, remove the data
// from the source
if (event.detail == DND.DROP_MOVE)
dragLabel.setText("");
}
}
});
import org.eclipse.swt.dnd.*;
2
3 // Enable a table as a Drop Target
4 final Table dropTable = new Table(shell, SWT.BORDER);
5 for (int i = 0; i < 10; i++) {
6 TableItem item = new TableItem(dropTable, SWT.NONE);
7 item.setText("item" + I);
8 }
9
10 // Allow data to be copied or moved to the drop target
11 operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
12 DropTarget target = new DropTarget(dropTable, operations);
13
14 // Receive data in Text or File format
15 final TextTransfer textTransfer = TextTransfer.getInstance();
16 final FileTransfer fileTransfer = FileTransfer.getInstance();
17 types = new Transfer[] {fileTransfer, textTransfer};
18 target.setTransfer(types);
19
20 target.addDropListener(new DropTargetListener() {
21 public void dragEnter(DropTargetEvent event) {
22 if (event.detail == DND.DROP_DEFAULT) {
23 if ((event.operations & DND.DROP_COPY) != 0) {
24 event.detail = DND.DROP_COPY;
25 } else {
26 event.detail = DND.DROP_NONE;
27 }
28 }
29 // will accept text but prefer to have files dropped
30 for (int i = 0; i < event.dataTypes.length; i++) {
31 if (fileTransfer.isSupportedType(event.dataTypes[i])){
32 event.currentDataType = event.dataTypes[i];
33 // files should only be copied
34 if (event.detail != DND.DROP_COPY) {
35 event.detail = DND.DROP_NONE;
36 }
37 break;
38 }
39 }
40 }
41 public void dragOver(DropTargetEvent event) {
42 event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
43 if (textTransfer.isSupportedType(event.currentDataType)) {
44 // NOTE: on unsupported platforms this will return null
45 Object o = textTransfer.nativeToJava(event.currentDataType);
46 String t = (String)o;
47 if (t != null) System.out.println(t);
48 }
50 }
51 public void dragOperationChanged(DropTargetEvent event) {
52 if (event.detail == DND.DROP_DEFAULT) {
53 if ((event.operations & DND.DROP_COPY) != 0) {
54 event.detail = DND.DROP_COPY;
55 } else {
56 event.detail = DND.DROP_NONE;
57 }
58 }
59 // allow text to be moved but files should only be copied
60 if (fileTransfer.isSupportedType(event.currentDataType)){
61 if (event.detail != DND.DROP_COPY) {
62 event.detail = DND.DROP_NONE;
63 }
64 }
65 }
66 public void dragLeave(DropTargetEvent event) {
67 }
68 public void dropAccept(DropTargetEvent event) {
69 }
70 public void drop(DropTargetEvent event) {
71 if (textTransfer.isSupportedType(event.currentDataType)) {
72 String text = (String)event.data;
73 TableItem item = new TableItem(dropTable, SWT.NONE);
74 item.setText(text);
75 }
76 if (fileTransfer.isSupportedType(event.currentDataType)){
77 String[] files = (String[])event.data;
78 for (int i = 0; i < files.length; i++) {
79 TableItem item = new TableItem(dropTable, SWT.NONE);
80 item.setText(files[i]);
81 }
82 }
83 }
84 });
2. Exercise: Drag and Drop in SWT
The methods moveAbove
and moveBelow
provide the option to re-arrange widgets within a layout.
The following example demonstrates the usage of drag and drop to re-arrange widgets in a layout.
It assumes that you have created several png file in an images
folder of the project.
Create the following class.
package com.vogella.swt.widgets.dnd;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DragSource;
import org.eclipse.swt.dnd.DragSourceAdapter;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DropTarget;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
public class PhotoShuffler {
public static void main(String[] args) {
// setup the SWT window
Display display = new Display();
final Shell shell = new Shell(display);
shell.setLayout(new FillLayout());
shell.setText("Photo Shuffler");
// initialize a parent composite with a grid layout manager
Composite parent = new Composite(shell, SWT.NONE);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 2;
gridLayout.marginWidth=20;
parent.setLayout(gridLayout);
// determine the path where the pictures are stored
String path = System.getProperty("user.dir") + "/images/";
// initialize an array with the photograph names
File imageDir= new File(path);
// loop over the photo array and establish all listeners
List<File> files = Arrays.stream(imageDir.listFiles())
.filter(f -> f.isFile() && f.getName().endsWith(".png"))
.collect(Collectors.toList());
for (File file : files) {
// labels serve as containers for the images
Label label = new Label(parent, SWT.NONE);
Image img = new Image(display,file.getAbsolutePath());
label.setImage(img);
// enable each label to be draggable
DragSource source = new DragSource(label, DND.DROP_NONE);
source.setTransfer(TextTransfer.getInstance()); // varargs are supported as of 4.7
// add a drag listener
source.addDragListener(new MyDragSourceListener(parent, source));
// enable each label to be a drop target
DropTarget target = new DropTarget(label, DND.DROP_NONE);
target.setTransfer(new Transfer[] { TextTransfer.getInstance() }); // varargs are not yet supported see https://git.eclipse.org/r/#/c/92236 // add a drop listener
target.addDropListener(new MyDropTargetListener(parent, target));
}
// show the SWT window
shell.pack();
shell.open();
while (!shell.isDisposed()) {
if (!display.readAndDispatch())
display.sleep();
}
// tear down the SWT window
display.dispose();
}
private static class MyDragSourceListener extends DragSourceAdapter {
private Composite parentComposite;
private DragSource source;
/**
* @param parentComposite - the composite that holds all pictures
* @param source - the drag source
*
*/
public MyDragSourceListener(Composite parentComposite, DragSource source) {
this.parentComposite = parentComposite;
this.source = source;
}
/**
* The method computes the position / index of the source control
* (label) in the children array of the parent composite. This index is
* passed to the drop target using the data field of the drag source
* event.
*/
public void dragSetData(DragSourceEvent event) {
for (int i = 0; i < parentComposite.getChildren().length; i++) {
if (parentComposite.getChildren()[i].equals(source.getControl())) {
event.data = new Integer(i).toString();
break;
}
}
}
}
public static class MyDropTargetListener extends DropTargetAdapter {
private Composite parentComposite;
private DropTarget target;
/**
* @param parentComposite - the composite that holds all pictures
* @param target - the drop target
*/
public MyDropTargetListener(Composite parentComposite, DropTarget target) {
this.parentComposite = parentComposite;
this.target = target;
}
/**
* This method moves the dragged picture to the new position and shifts the
* old picture to the right or left.
*/
public void drop(DropTargetEvent event) {
// retrieve the stored index
int sourceIndex = Integer.valueOf(event.data.toString());
// compute the index of target control
Control targetControl = target.getControl();
int targetIndex = -1;
for (int i = 0; i < parentComposite.getChildren().length; i++) {
if (parentComposite.getChildren()[i].equals(targetControl)) {
targetIndex = i;
break;
}
}
Control sourceControl = parentComposite.getChildren()[sourceIndex];
// do not do anything if the dragged photo is dropped at the same
// position
if (targetIndex == sourceIndex)
return;
// if dragged from left to right
// shift the old picture to the left
if (targetIndex > sourceIndex)
sourceControl.moveBelow(targetControl);
// if dragged from right to left
// shift the old picture to the right
else
sourceControl.moveAbove(targetControl);
// repaint the parent composite
parentComposite.requestLayout();
}
}
}
2.1. Adding a drag icon with drag shadow
For this exercise we are going to use the class DropShadow
from imageEffects.zip.
Please make sure to import it into your project.
The code is from the Simple Image Effects for SWT Eclipse guide.
We want to replace the drag icon with the dragged image itself and add a nice drop shadow. For this purpose we add an additional DragListener to the DragSource.
source.addDragListener(new DragSourceAdapter() {
@Override
public void dragStart(DragSourceEvent dragSourceEvent) {
// getting control widget - Label in this case
Label label = ((Label) ((DragSource) dragSourceEvent.getSource()).getControl());
// make the drag icon the dragged image itself
dragSourceEvent.image = label.getImage();
// add shadow to drag icon
dragSourceEvent.image = new Image(display,
DropShadow.dropShadow(dragSourceEvent.image.getImageData(),
display.getSystemColor(SWT.COLOR_BLACK),
10, 30, 100));
}
});
3. Exercise: Implement drag and drop
The following exercise demonstrates the usage of drag and drop for customer domain objects and SWT widgets. You drag and drop between two JFace viewers (a table and a tree).
3.1. Create new project
Create a new Eclipse RCP application called com.vogella.dnd.jface
with two views.
Create the following model class.
package com.vogella.dnd.jface.model;
public class Todo {
private String shortDescription;
private String longDescription;
public Todo(String shortDescription, String longDescription) {
this.shortDescription = shortDescription;
this.longDescription = longDescription;
}
public String getShortDescription() {
return shortDescription;
}
public void setShortDescription(String shortDescription) {
this.shortDescription = shortDescription;
}
public String getLongDescription() {
return longDescription;
}
public void setLongDescription(String longDescription) {
this.longDescription = longDescription;
}
}
Create the following content providers (Singletons).
package com.vogella.dnd.jface.model;
import java.util.ArrayList;
import java.util.List;
public enum ContentProvider {
INSTANCE;
public List<Todo> getModel(){
List<Todo> list = new ArrayList<Todo>();
Todo todo = new Todo("Java", "Learn the Closure proposal");
list.add(todo);
todo = new Todo("Eclipse", "Learn more about the RCP platform");
list.add(todo);
return list;
}
}
package com.vogella.dnd.jface.model;
import java.util.ArrayList;
import java.util.List;
public enum ContentProviderTree {
INSTANCE;
List<String> list = new ArrayList<String>();
private ContentProviderTree() {
list.add("Branch1");
list.add("Branch1");
}
public List<String> getModel(){
return list;
}
}
Create the following label and content provider for your JFace viewers.
package com.vogella.dnd.jface.viewers;
import java.util.List;
import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.Viewer;
import com.vogella.dnd.jface.model.Todo;
public class TableContentProvider implements IStructuredContentProvider {
@Override
public Object[] getElements(Object inputElement) {
List<Todo> list = (List<Todo>) inputElement;
return list.toArray();
}
}
package com.vogella.dnd.jface.viewers;
import java.util.List;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.Viewer;
public class TreeContentProvider implements ITreeContentProvider {
@Override
public Object[] getChildren(Object parentElement) {
return null;
}
@Override
public Object getParent(Object element) {
return null;
}
@Override
public boolean hasChildren(Object element) {
return false;
}
@Override
public Object[] getElements(Object inputElement) {
List<String> list = (List<String>) inputElement;
return list.toArray();
}
}
package com.vogella.dnd.jface.viewers;
import org.eclipse.jface.viewers.BaseLabelProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.swt.graphics.Image;
import com.vogella.dnd.jface.model.Todo;
public class TableLabelProvider extends BaseLabelProvider implements
ITableLabelProvider {
@Override
public Image getColumnImage(Object element, int columnIndex) {
return null;
}
@Override
public String getColumnText(Object element, int columnIndex) {
Todo todo = (Todo) element;
switch (columnIndex) {
case 0:
return todo.getShortDescription();
case 1:
return todo.getLongDescription();
}
return "Not supported";
}
}
package com.vogella.dnd.jface.viewers;
import org.eclipse.jface.viewers.LabelProvider;
public class TreeLabelProvider extends LabelProvider {
@Override
public String getText(Object element) {
String s = (String) element;
return s;
}
}
3.2. Drag and drop Listener
Create the following drag and drop listener. These are later assigned to your table and tree widgets.
package com.vogella.dnd.jface.dnd;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.dnd.DragSourceEvent;
import org.eclipse.swt.dnd.DragSourceListener;
import org.eclipse.swt.dnd.TextTransfer;
import com.vogella.dnd.jface.model.Todo;
public class MyDragListener implements DragSourceListener {
private final TableViewer viewer;
public MyDragListener(TableViewer viewer) {
this.viewer = viewer;
}
@Override
public void dragFinished(DragSourceEvent event) {
System.out.println("Finshed Drag");
}
@Override
public void dragSetData(DragSourceEvent event) {
// Here you do the convertion to the type which is expected.
IStructuredSelection selection = viewer.getStructuredSelection();
Todo firstElement = (Todo) selection.getFirstElement();
if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
event.data = firstElement.getShortDescription() + " " + firstElement.getLongDescription();
}
}
@Override
public void dragStart(DragSourceEvent event) {
System.out.println("Start Drag");
}
}
package com.vogella.dnd.jface.dnd;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TransferData;
import com.vogella.dnd.jface.model.ContentProviderTree;
public class MyDropListener extends ViewerDropAdapter {
private final Viewer viewer;
public MyDropListener(Viewer viewer) {
super(viewer);
this.viewer = viewer;
}
@Override
public void drop(DropTargetEvent event) {
int location = this.determineLocation(event);
String target = (String) determineTarget(event);
String translatedLocation ="";
switch (location){
case 1 :
translatedLocation = "Dropped before the target ";
break;
case 2 :
translatedLocation = "Dropped after the target ";
break;
case 3 :
translatedLocation = "Dropped on the target ";
break;
case 4 :
translatedLocation = "Dropped into nothing ";
break;
}
System.out.println(translatedLocation);
System.out.println("The drop was done on the element: " + target );
super.drop(event);
}
// This method performs the actual drop
// We simply add the String we receive to the model and trigger a refresh of the
// viewer by calling its setInput method.
@Override
public boolean performDrop(Object data) {
ContentProviderTree.INSTANCE.getModel().add( data.toString());
viewer.setInput(ContentProviderTree.INSTANCE.getModel());
return false;
}
@Override
public boolean validateDrop(Object target, int operation,
TransferData transferType) {
return true;
}
}
3.3. Adjusting your parts
Adjust your views. The first view shows a table and allows the user to drag an elements from it. The second view accepts drops.
package com.vogella.dnd.jface.view;
import jakarta.annotation.PostConstruct;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import com.vogella.dnd.jface.dnd.MyDragListener;
import com.vogella.dnd.jface.model.ContentProvider;
import com.vogella.dnd.jface.viewers.TableContentProvider;
import com.vogella.dnd.jface.viewers.TableLabelProvider;
public class TableView {
@PostConstruct
public void createPartControl(Composite parent) {
TableViewer viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
int operations = DND.DROP_COPY| DND.DROP_MOVE;
Transfer[] transferTypes = new Transfer[]{TextTransfer.getInstance()};
viewer.addDragSupport(operations, transferTypes , new MyDragListener(viewer));
viewer.setContentProvider(new TableContentProvider());
viewer.setLabelProvider(new TableLabelProvider());
viewer.setInput(ContentProvider.INSTANCE.getModel());
}
}
package com.vogella.dnd.jface.view;
import jakarta.annotation.PostConstruct;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.part.ViewPart;
import com.vogella.dnd.jface.dnd.MyDropListener;
import com.vogella.dnd.jface.model.ContentProviderTree;
import com.vogella.dnd.jface.viewers.TreeContentProvider;
import com.vogella.dnd.jface.viewers.TreeLabelProvider;
public class TreeView {
@PostConstruct
public void createPartControl(Composite parent) {
TreeViewer viewer = new TreeViewer(parent, SWT.MULTI | SWT.H_SCROLL
| SWT.V_SCROLL);
int operations = DND.DROP_COPY | DND.DROP_MOVE;
Transfer[] transferTypes = new Transfer[]{TextTransfer.getInstance()};
viewer.addDropSupport(operations, transferTypes, new MyDropListener(viewer));
viewer.setContentProvider(new TreeContentProvider());
viewer.setLabelProvider(new TreeLabelProvider());
viewer.setInput(ContentProviderTree.INSTANCE.getModel());
}
}
4. Eclipse drag and drop resources
4.1. vogella Java example code
If you need more assistance we offer Online Training and Onsite training as well as consulting