/******************************************************************************* * Copyright (c) 2011 Oak Ridge National Laboratory. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html ******************************************************************************/ package org.csstudio.ui.util.dnd; import java.io.Serializable; import java.lang.reflect.Array; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Set; import org.csstudio.ui.util.AdapterUtil; 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.TextTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.widgets.Control; import static org.csstudio.ui.util.ReflectUtil.*; /** * General purpose utility to allowing Drag-and-Drop "Drag" of any * adaptable or {@link Serializable} object. * * <p>As an example, assume a TableViewer or TreeViewer where the input * contains Serializable objects like ProcessVariable. * This would allow dragging the first of the * currently selected elements: * <pre> * ...Viewer viewer = ... * new ControlSystemDragSource(viewer.getControl()) * { * public Object getSelection() * { * final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); * return selection.getFirstElement(); * } * }; * </pre> * * <p>In principle this would allow dagging any number of selected PVs * out of the viewer: * <pre> * new ControlSystemDragSource(viewer.getControl()) * { * public Object getSelection() * { * final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); * final Object[] objs = selection.toArray(); * return objs; * } * }; * </pre> * * <p>.. but note that it will fail. The data needs to be serialized as the actual array * type, not an Object array: * <pre> * new ControlSystemDragSource(viewer.getControl()) * { * public Object getSelection() * { * final IStructuredSelection selection = (IStructuredSelection) viewer.getSelection(); * final Object[] objs = selection.toArray(); * final ProcessVariable[] pvs = Arrays.copyOf(objs, objs.length, ProcessVariable[].class); * return pvs; * } * }; * </pre> * * * @author Gabriele Carcassi * @author Kay Kasemir */ @SuppressWarnings("nls") abstract public class ControlSystemDragSource { final private DragSource source; /** Initialize 'drag' source * @param control Control from which the selection may be dragged */ public ControlSystemDragSource(final Control control) { source = new DragSource(control, DND.DROP_COPY); source.addDragListener(new DragSourceAdapter() { @Override public void dragStart(DragSourceEvent event) { Object selection = getSelection(); // No selection, don't start the drag if (selection == null) { event.doit = false; return; } // Calculate the transfer types: source.setTransfer(supportedTransfers(selection)); } @Override public void dragSetData(final DragSourceEvent event) { // Drag has been performed, provide data Object selection = getSelection(); for (Transfer transfer : supportedTransfers(selection)) { if (transfer.isSupportedType(event.dataType)) { if (transfer instanceof SerializableItemTransfer) { SerializableItemTransfer objectTransfer = (SerializableItemTransfer) transfer; event.data = AdapterUtil.convert(selection, objectTransfer.getClassName()); return; } else if (transfer instanceof TextTransfer) { // TextTransfer needs String if (selection.getClass().isArray()) event.data = Arrays.toString((Object[]) selection); else event.data = selection.toString(); return; } } } } }); } private static Collection<String> toArrayClasses(Collection<String> classes) { Collection<String> arrayClasses = new ArrayList<String>(); for (String clazz : classes) { arrayClasses.add(toArrayClass(clazz)); } return arrayClasses; } private static String toArrayClass(String className) { return "[L" + className + ";"; } private static List<String> arrayClasses(String[] classes) { List<String> arrayClasses = new ArrayList<String>(); for (String clazz : classes) { if (isArray(clazz)) { arrayClasses.add(clazz); } } return arrayClasses; } private static List<String> simpleClasses(String[] classes) { List<String> arrayClasses = new ArrayList<String>(); for (String clazz : classes) { if (!isArray(clazz)) { arrayClasses.add(clazz); } } return arrayClasses; } private static List<Transfer> supportedSingleTransfers(Class<?> clazz) { if (clazz.isArray()) throw new RuntimeException("Something wrong: you are asking for single transfers for an array"); String[] types = AdapterUtil.getAdaptableTypes(clazz); List<Transfer> supportedTransfers = new ArrayList<Transfer>(); if (Serializable.class.isAssignableFrom(clazz)) { @SuppressWarnings("unchecked") Class<? extends Serializable> serializableClass = (Class<? extends Serializable>) clazz; supportedTransfers.add(SerializableItemTransfer.getTransfer(serializableClass)); } supportedTransfers.addAll(SerializableItemTransfer.getTransfers(simpleClasses(types))); supportedTransfers.add(TextTransfer.getInstance()); return supportedTransfers; } private static List<Transfer> supportedArrayTransfers(Class<?> arrayClass) { if (!arrayClass.isArray()) throw new RuntimeException("Something wrong: you are asking for array transfers for an single object"); String[] types = AdapterUtil.getAdaptableTypes(arrayClass.getComponentType()); List<Transfer> supportedTransfers = new ArrayList<Transfer>(); if (Serializable.class.isAssignableFrom(arrayClass.getComponentType())) { supportedTransfers.add(SerializableItemTransfer.getTransfer(arrayClass.getName())); } supportedTransfers.addAll(SerializableItemTransfer.getTransfers(toArrayClasses(simpleClasses(types)))); supportedTransfers.addAll(SerializableItemTransfer.getTransfers(arrayClasses(types))); supportedTransfers.add(TextTransfer.getInstance()); return supportedTransfers; } private static Transfer[] supportedTransfers(Object selection) { Class<?> singleClass; Class<?> arrayClass; if (selection instanceof Object[]) { // Selection is an array arrayClass = selection.getClass(); if (Array.getLength(selection) == 0) { // if empty, no transfers return new Transfer[0]; } else if (Array.getLength(selection) == 1) { // if size one, set single selection singleClass = selection.getClass().getComponentType(); } else { // no single selection singleClass = null; } } else { // If it's a single value, the single selection is the // object and the array is an array with just one element singleClass = selection.getClass(); arrayClass = Array.newInstance(selection.getClass(), 0).getClass(); } Set<Transfer> supportedTransfers = new HashSet<Transfer>(); // Add single type support, if needed if (singleClass != null) { supportedTransfers.addAll(supportedSingleTransfers(singleClass)); } // Add array type support supportedTransfers.addAll(supportedArrayTransfers(arrayClass)); return supportedTransfers.toArray(new Transfer[supportedTransfers.size()]); } /** To be implemented by derived class: * Provide the control system items that should be 'dragged' * from this drag source * * @return the selection (can be single object or array) */ abstract public Object getSelection(); }