001: package net.refractions.udig.internal.ui;
002:
003: import java.lang.reflect.Method;
004: import java.util.ArrayList;
005: import java.util.Collection;
006: import java.util.Collections;
007: import java.util.List;
008: import java.util.Queue;
009: import java.util.Set;
010: import java.util.concurrent.ConcurrentLinkedQueue;
011: import java.util.concurrent.CopyOnWriteArraySet;
012:
013: import net.refractions.udig.ui.IDropAction;
014: import net.refractions.udig.ui.IDropHandlerListener;
015: import net.refractions.udig.ui.ProgressMonitorTaskNamer;
016: import net.refractions.udig.ui.UDIGDragDropUtilities;
017: import net.refractions.udig.ui.ViewerDropLocation;
018: import net.refractions.udig.ui.internal.Messages;
019:
020: import org.eclipse.core.runtime.IProgressMonitor;
021: import org.eclipse.core.runtime.IStatus;
022: import org.eclipse.core.runtime.Status;
023: import org.eclipse.core.runtime.jobs.Job;
024: import org.eclipse.jface.viewers.IStructuredSelection;
025: import org.eclipse.swt.dnd.DND;
026: import org.eclipse.swt.dnd.DropTargetAdapter;
027: import org.eclipse.swt.dnd.DropTargetEvent;
028: import org.eclipse.swt.dnd.Transfer;
029: import org.eclipse.swt.dnd.TransferData;
030:
031: /**
032: * A drop event handler which delegates to an extension point in order to perform drag and drop
033: * actions.
034: * <p>
035: * The drop handler interacts with three objects.
036: * <ol>
037: * <li>The <b>type</b> object, which is the entity being dropped.
038: * <li>The <b>destination</b> object, which is where the drop is occuring. This is usually a view
039: * or an editor.
040: * <li>The <b>target</b> object (optional), which is the specific object within the destination.
041: * </ol>
042: * </p>
043: * <p>
044: * A UDIGDropHandler is delegated to by another DropTargetAdapter. Depending on the type of drop
045: * adapter, location, and target may not be set.
046: * </p>
047: *
048: * @author Justin Deoliveira,Refractions Research Inc.,jdeolive@refractions.net
049: * @since 0.6.0
050: */
051: public class UDIGDropHandler extends DropTargetAdapter {
052:
053: /** job queue shared by all drop handlers * */
054: private static final CompositeDropActionJob actionJob = new CompositeDropActionJob();
055:
056: /** the target object * */
057: private Object target;
058:
059: /** the location of the drop * */
060: private ViewerDropLocation location = ViewerDropLocation.ON;
061:
062: private Set<IDropHandlerListener> listeners = new CopyOnWriteArraySet<IDropHandlerListener>();
063:
064: public UDIGDropHandler() {
065: }
066:
067: /**
068: * Gets the Object the Drop event targets. For example in LayersView it would be the layer it
069: * hit.
070: * <p>
071: * This is defined by the extension and should not be called by non-framework code
072: * </p>
073: */
074: public void setTarget(Object target) {
075: this .target = target;
076: }
077:
078: /**
079: * Gets the Object the Drop event targets. For example in LayersView it would be the layer it
080: * hit. The target is dependent on the dropListener.
081: */
082: public Object getTarget() {
083: return target;
084: }
085:
086: /**
087: * a constant describing the position of the mouse relative to the target (before, on, or after
088: * the target.
089: * <p>
090: * This is set by the framework
091: * </p>
092: *
093: * @param location one of the <code>LOCATION_* </code> constants defined in this type
094: * @see org.eclipse.jface.viewers.ViewerDropAdapter#getCurrentLocation()
095: */
096:
097: public void setViewerLocation(ViewerDropLocation location) {
098: this .location = location;
099: }
100:
101: /**
102: * Returns a constant describing the position of the mouse relative to the target (before, on,
103: * or after the target.
104: *
105: * @return one of the <code>LOCATION_* </code> constants defined in this type
106: * @see org.eclipse.jface.viewers.ViewerDropAdapter#getCurrentLocation()
107: */
108: public ViewerDropLocation getViewerLocation() {
109: return location;
110: }
111:
112: @Override
113: public void dragLeave(DropTargetEvent event) {
114: super .dragLeave(event);
115: }
116:
117: @Override
118: public void dragOver(DropTargetEvent event) {
119: event.detail = DND.DROP_COPY;
120:
121: if ((event.operations & DND.DROP_LINK) == DND.DROP_LINK) {
122: event.detail = DND.DROP_LINK;
123: } else if ((event.operations & DND.DROP_MOVE) == DND.DROP_MOVE) {
124: event.detail = DND.DROP_MOVE;
125: } else if ((event.operations & DND.DROP_COPY) == DND.DROP_COPY) {
126: event.detail = DND.DROP_COPY;
127: }
128: }
129:
130: @Override
131: public void dropAccept(DropTargetEvent event) {
132: super .dropAccept(event);
133: }
134:
135: private Object getJavaObject(Transfer transfer, TransferData data) {
136: try {
137: if (transfer instanceof UDIGTransfer) {
138: return ((UDIGTransfer) transfer).nativeToJava(data);
139: }
140: Method m = transfer.getClass().getMethod(
141: "nativeToJava", new Class[] { TransferData.class }); //$NON-NLS-1$
142: return m.invoke(transfer, new Object[] { data });
143: } catch (Throwable t) {
144: return null;
145: }
146: }
147:
148: @Override
149: public void dragEnter(DropTargetEvent event) {
150: if (UiPlugin.isDebugging(Trace.DND)) {
151: System.out
152: .println("UDIGDropHandler.dragEnter: Setting event.detail to COPY"); //$NON-NLS-1$
153: }
154: event.detail = DND.DROP_COPY;
155: }
156:
157: @Override
158: public void dragOperationChanged(DropTargetEvent event) {
159: if (UiPlugin.isDebugging(Trace.DND)) {
160: System.out
161: .println("UDIGDropHandler.dragOperationChanged: Setting event.detail to COPY"); //$NON-NLS-1$
162: }
163: event.detail = DND.DROP_COPY;
164: }
165:
166: @Override
167: public void drop(DropTargetEvent event) {
168: Set<Transfer> t = UDIGDragDropUtilities.getTransfers();
169: for (Transfer transfer : t) {
170: if (event.data != null)
171: break;
172: TransferData[] types = transfer.getSupportedTypes();
173: for (TransferData data : types) {
174: if (transfer.isSupportedType(data)) {
175: event.data = getJavaObject(transfer, data);
176: break;
177: }
178: }
179: }
180: performDrop(event.data, event);
181: }
182:
183: /**
184: * Find drop actions for data.
185: *
186: * @param data data dropped
187: * @param event drop event. Maybe null.
188: */
189: public void performDrop(Object data, DropTargetEvent event) {
190: if (UiPlugin.isDebugging(Trace.DND)) {
191: System.out
192: .println("PerformDrop called on " + data + "(" + data.getClass() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
193: }
194: // do a check for a multi object and seperate out
195: Class type = data.getClass();
196: Object[] objects = null;
197: if (type.isArray()) {
198: objects = (Object[]) data;
199: } else if (data instanceof Collection) {
200: objects = ((Collection) data).toArray();
201: } else if (data instanceof IStructuredSelection) {
202: objects = ((IStructuredSelection) data).toArray();
203: } else {
204: objects = new Object[] { data };
205: }
206:
207: CompositeDropActionJob actionJob = getActionJob();
208: List<IDropAction> actions = UDIGDNDProcessor.process(objects,
209: UDIGDropHandler.this , event);
210:
211: if (!actions.isEmpty()) {
212: UiPlugin
213: .trace(
214: Trace.DND,
215: getClass(),
216: data
217: + " dropped on " + getTarget() + " " + actions.size() + " found to process drop", null); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
218: UiPlugin
219: .trace(
220: Trace.DND,
221: getClass(),
222: "Actions for drop are: " + actions.toString(), null); //$NON-NLS-1$
223: actionJob.addActions(this , actions);
224: } else {
225: UiPlugin
226: .trace(
227: Trace.DND,
228: getClass(),
229: data
230: + " dropped on " + getTarget() + " found no actions for processing it", null); //$NON-NLS-1$//$NON-NLS-2$
231: notifyNoDropAction(data);
232: }
233: }
234:
235: private void notifyNoDropAction(Object object) {
236: Set<IDropHandlerListener> set = listeners;
237: for (IDropHandlerListener listener : set) {
238: listener.noAction(object);
239: }
240: }
241:
242: public static CompositeDropActionJob getActionJob() {
243: return actionJob;
244: }
245:
246: public static class CompositeDropActionJob extends Job {
247:
248: private final Queue<DropActionRunnable> queue;
249:
250: public CompositeDropActionJob() {
251: super (Messages.UDIGDropHandler_jobName);
252:
253: queue = new ConcurrentLinkedQueue<DropActionRunnable>();
254: setUser(true);
255: }
256:
257: /**
258: * Returns the drag and drop job queue. It contains all the actions that have not been
259: *
260: * @return
261: */
262: public synchronized List<DropActionRunnable> getJobQueue() {
263: return Collections
264: .unmodifiableList(new ArrayList<DropActionRunnable>(
265: queue));
266: }
267:
268: /**
269: * Adds an action to be executed by drop handler.
270: *
271: * @param handler
272: * @param actions
273: */
274: synchronized void addActions(UDIGDropHandler handler,
275: List<IDropAction> actions) {
276: for (IDropAction action : actions) {
277: queue.offer(new DropActionRunnable(handler, action));
278: }
279: if (!actions.isEmpty())
280: actionJob.schedule();
281: }
282:
283: @Override
284: public boolean belongsTo(Object family) {
285: return family == CompositeDropActionJob.class;
286: }
287:
288: @Override
289: protected IStatus run(IProgressMonitor monitor2) {
290: monitor2.beginTask(
291: Messages.UDIGDropHandler_performing_task,
292: IProgressMonitor.UNKNOWN);
293: DropActionRunnable next;
294: while (!Thread.currentThread().isInterrupted()) {
295: next = null;
296:
297: synchronized (this ) {
298: next = queue.poll();
299:
300: if (next == null
301: || Thread.currentThread().isInterrupted()) {
302: return Status.OK_STATUS;
303: }
304: }
305:
306: IProgressMonitor monitor = new ProgressMonitorTaskNamer(
307: monitor2, 10);
308:
309: monitor2
310: .setTaskName(Messages.UDIGDropHandler_performing_task
311: + ": " + next.action.getName()); //$NON-NLS-2$
312:
313: // run the next job
314: next.run(monitor);
315: }
316:
317: return Status.OK_STATUS;
318: }
319: }
320:
321: private static class DropActionRunnable {
322:
323: IDropAction action;
324: UDIGDropHandler handler;
325:
326: public DropActionRunnable(UDIGDropHandler handler,
327: IDropAction action) {
328: super ();
329:
330: this .handler = handler;
331: this .action = action;
332: }
333:
334: protected IStatus run(IProgressMonitor monitor) {
335: notifyStart(action);
336: try {
337: action.perform(monitor);
338: notifyDone(action, null);
339: return Status.OK_STATUS;
340: } catch (Throwable t) {
341: String msg = Messages.UDIGDropHandler_error;
342: String ns = action.getElement()
343: .getNamespaceIdentifier();
344:
345: Status s = new Status(IStatus.WARNING, ns, 0, msg, t);
346: UiPlugin.getDefault().getLog().log(s);
347: notifyDone(action, t);
348: return Status.CANCEL_STATUS;
349: }
350: }
351:
352: private void notifyDone(IDropAction action, Throwable t) {
353: Set<IDropHandlerListener> set = handler.listeners;
354: for (IDropHandlerListener listener : set) {
355: listener.done(action, t);
356: }
357: }
358:
359: private void notifyStart(IDropAction action) {
360: Set<IDropHandlerListener> set = handler.listeners;
361: for (IDropHandlerListener listener : set) {
362: listener.starting(action);
363: }
364: }
365: }
366:
367: /**
368: * Remove listener from set of listeners.
369: *
370: * @param listener listener to remove
371: */
372: public void removeListener(IDropHandlerListener listener) {
373: listeners.remove(listener);
374: }
375:
376: /**
377: * Add listener to set of drop listeners. A listener can only be added once.
378: *
379: * @param listener listener to add.
380: */
381: public void addListener(IDropHandlerListener listener) {
382: listeners.add(listener);
383: }
384:
385: }
|