001: /*******************************************************************************
002: * Copyright (c) 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.ui.dnd;
011:
012: import org.eclipse.swt.dnd.DND;
013: import org.eclipse.swt.dnd.DropTargetAdapter;
014: import org.eclipse.swt.dnd.DropTargetEvent;
015: import org.eclipse.swt.dnd.TransferData;
016: import org.eclipse.swt.graphics.Point;
017: import org.eclipse.swt.graphics.Rectangle;
018: import org.eclipse.swt.widgets.Item;
019: import org.eclipse.swt.widgets.TableItem;
020: import org.eclipse.swt.widgets.TreeItem;
021:
022: import org.eclipse.jface.viewers.ISelection;
023: import org.eclipse.jface.viewers.IStructuredSelection;
024: import org.eclipse.jface.viewers.Viewer;
025:
026: /**
027: * This adapter class provides generic drag-and-drop support for a viewer.
028: * <p>
029: * Subclasses must implement the following methods:
030: * <ul>
031: * <li><code>validateDrop</code> - identifies valid drop targets in viewer</li>
032: * <li><code>performDrop</code> - carries out a drop into a viewer</li>
033: * </ul>
034: * The <code>setFeedbackEnabled</code> method can be called to turn on and off
035: * visual insertion feedback (on by default).
036: * </p>
037: * <p>
038: * THIS IS (ALMOST) A ONE-TO-ONE COPY OF PLATFORMS ViewerDropAdapter
039: * REFERE THERE FIRST IF YOU NEED TO FIX SOMETHING.
040: * </p>
041: */
042: public abstract class JdtViewerDropAdapter extends DropTargetAdapter {
043:
044: /**
045: * Constant describing the position of the cursor relative
046: * to the target object. This means the mouse is positioned
047: * slightly before the target.
048: * @see #getCurrentLocation()
049: */
050: public static final int LOCATION_BEFORE = 1;
051:
052: /**
053: * Constant describing the position of the cursor relative
054: * to the target object. This means the mouse is positioned
055: * slightly after the target.
056: * @see #getCurrentLocation()
057: */
058: public static final int LOCATION_AFTER = 2;
059:
060: /**
061: * Constant describing the position of the cursor relative
062: * to the target object. This means the mouse is positioned
063: * directly on the target.
064: * @see #getCurrentLocation()
065: */
066: public static final int LOCATION_ON = 3;
067:
068: /**
069: * Constant describing the position of the cursor relative
070: * to the target object. This means the mouse is not positioned
071: * over or near any valid target.
072: * @see #getCurrentLocation()
073: */
074: public static final int LOCATION_NONE = 4;
075:
076: /**
077: * The viewer to which this drop support has been added.
078: */
079: private Viewer viewer;
080:
081: /**
082: * The current operation.
083: */
084: private int currentOperation = DND.DROP_NONE;
085:
086: /**
087: * The last valid operation.
088: */
089: private int lastValidOperation = DND.DROP_NONE;
090:
091: /**
092: * The data item currently under the mouse.
093: */
094: private Object currentTarget;
095:
096: /**
097: * Information about the position of the mouse relative to the
098: * target (before, on, or after the target. Location is one of
099: * the <code>LOCATION_* </code> constants defined in this type.
100: */
101: private int currentLocation;
102:
103: /**
104: * A flag that allows adapter users to turn the insertion
105: * feedback on or off. Default is <code>true</code>.
106: */
107: private boolean feedbackEnabled = true;
108:
109: /**
110: * A flag that allows adapter users to turn auto scrolling
111: * on or off. Default is <code>true</code>.
112: */
113: private boolean scrollEnabled = true;
114:
115: /**
116: * A flag that allows adapter users to turn auto
117: * expanding on or off. Default is <code>true</code>.
118: */
119: private boolean expandEnabled = true;
120:
121: /**
122: * A flag that allows adapter users to turn selection feedback
123: * on or off. Default is <code>true</code>.
124: */
125: private boolean selectFeedbackEnabled = true;
126:
127: /**
128: * Creates a new drop adapter for the given viewer.
129: *
130: * @param viewer the viewer
131: */
132: protected JdtViewerDropAdapter(Viewer viewer) {
133: this .viewer = viewer;
134: }
135:
136: /**
137: * Returns the position of the given event's coordinates relative to its target.
138: * The position is determined to be before, after, or on the item, based on
139: * some threshold value.
140: *
141: * @param event the event
142: * @return one of the <code>LOCATION_* </code>constants defined in this class
143: */
144: protected int determineLocation(DropTargetEvent event) {
145: if (!(event.item instanceof Item)) {
146: return LOCATION_NONE;
147: }
148: Item item = (Item) event.item;
149: Point coordinates = new Point(event.x, event.y);
150: coordinates = viewer.getControl().toControl(coordinates);
151: if (item != null) {
152: Rectangle bounds = getBounds(item);
153: if (bounds == null) {
154: return LOCATION_NONE;
155: }
156: if ((coordinates.y - bounds.y) < 5) {
157: return LOCATION_BEFORE;
158: }
159: if ((bounds.y + bounds.height - coordinates.y) < 5) {
160: return LOCATION_AFTER;
161: }
162: }
163: return LOCATION_ON;
164: }
165:
166: /**
167: * Returns the target item of the given drop event.
168: *
169: * @param event the event
170: * @return The target of the drop, may be <code>null</code>.
171: */
172: protected Object determineTarget(DropTargetEvent event) {
173: return event.item == null ? null : event.item.getData();
174: }
175:
176: /* (non-Javadoc)
177: * Method declared on DropTargetAdapter.
178: * The mouse has moved over the drop target. If the
179: * target item has changed, notify the action and check
180: * that it is still enabled.
181: */
182: private void doDropValidation(DropTargetEvent event) {
183: currentOperation = determineOperation(currentTarget,
184: lastValidOperation, event.currentDataType,
185: event.operations);
186: event.detail = currentOperation;
187: setFeedback(event, currentLocation);
188: }
189:
190: /* (non-Javadoc)
191: * Method declared on DropTargetAdapter.
192: * The drag has entered this widget's region. See
193: * if the drop should be allowed.
194: */
195: public void dragEnter(DropTargetEvent event) {
196: currentTarget = determineTarget(event);
197: currentLocation = determineLocation(event);
198: lastValidOperation = event.detail;
199: doDropValidation(event);
200: }
201:
202: /* (non-Javadoc)
203: * Method declared on DropTargetAdapter.
204: * The drop operation has changed, see if the action
205: * should still be enabled.
206: */
207: public void dragOperationChanged(DropTargetEvent event) {
208: currentTarget = determineTarget(event);
209: lastValidOperation = event.detail;
210: doDropValidation(event);
211: }
212:
213: /* (non-Javadoc)
214: * Method declared on DropTargetAdapter.
215: * The mouse has moved over the drop target. If the
216: * target item has changed, notify the action and check
217: * that it is still enabled.
218: */
219: public void dragOver(DropTargetEvent event) {
220: //use newly revealed item as target if scrolling occurs
221: Object target = determineTarget(event);
222:
223: //set the location feedback
224: int oldLocation = currentLocation;
225: currentLocation = determineLocation(event);
226: setFeedback(event, currentLocation);
227:
228: //see if anything has really changed before doing validation.
229: if (target != currentTarget || currentLocation != oldLocation) {
230: currentTarget = target;
231: doDropValidation(event);
232: }
233: }
234:
235: /* (non-Javadoc)
236: * Method declared on DropTargetAdapter.
237: * The user has dropped something on the desktop viewer.
238: */
239: public void drop(DropTargetEvent event) {
240: currentLocation = determineLocation(event);
241:
242: //perform the drop behavior
243: if (!performDrop(event.data)) {
244: event.detail = DND.DROP_NONE;
245: }
246: currentOperation = event.detail;
247: }
248:
249: /* (non-Javadoc)
250: * Method declared on DropTargetAdapter.
251: * Last chance for the action to disable itself
252: */
253: public void dropAccept(DropTargetEvent event) {
254: event.detail = determineOperation(currentTarget, event.detail,
255: event.currentDataType, event.operations);
256: }
257:
258: /**
259: * Returns the bounds of the given SWT tree or table item.
260: *
261: * @param item the SWT Item
262: * @return the bounds, or <code>null</code> if it is not a known type of item
263: */
264: protected Rectangle getBounds(Item item) {
265: if (item instanceof TreeItem) {
266: return ((TreeItem) item).getBounds();
267: }
268: if (item instanceof TableItem) {
269: return ((TableItem) item).getBounds(0);
270: }
271: return null;
272: }
273:
274: /**
275: * Returns a constant describing the position of the mouse relative to the
276: * target (before, on, or after the target.
277: *
278: * @return one of the <code>LOCATION_* </code> constants defined in this type
279: */
280: protected int getCurrentLocation() {
281: return currentLocation;
282: }
283:
284: /**
285: * Returns the current operation.
286: *
287: * @return a <code>DROP_*</code> constant from class <code>DND</code>
288: *
289: * @see DND#DROP_COPY
290: * @see DND#DROP_MOVE
291: * @see DND#DROP_LINK
292: * @see DND#DROP_NONE
293: */
294: protected int getCurrentOperation() {
295: return currentOperation;
296: }
297:
298: /**
299: * Returns the target object currently under the mouse.
300: *
301: * @return the current target object
302: */
303: protected Object getCurrentTarget() {
304: return currentTarget;
305: }
306:
307: /**
308: * Returns whether visible insertion feedback should be presented to the user.
309: * <p>
310: * Typical insertion feedback is the horizontal insertion bars that appear
311: * between adjacent items while dragging.
312: * </p>
313: *
314: * @return <code>true</code> if visual feedback is desired, and <code>false</code> if not
315: */
316: public boolean getFeedbackEnabled() {
317: return feedbackEnabled;
318: }
319:
320: /**
321: * Returns the object currently selected by the viewer.
322: *
323: * @return the selected object, or <code>null</code> if either no object or
324: * multiple objects are selected
325: */
326: protected Object getSelectedObject() {
327: ISelection selection = viewer.getSelection();
328: if (selection instanceof IStructuredSelection
329: && !selection.isEmpty()) {
330: IStructuredSelection structured = (IStructuredSelection) selection;
331: return structured.getFirstElement();
332: }
333: return null;
334: }
335:
336: /**
337: * @return the viewer to which this drop support has been added.
338: */
339: protected Viewer getViewer() {
340: return viewer;
341: }
342:
343: /**
344: * @deprecated this method should not be used. Exception handling has been
345: * removed from DropTargetAdapter methods overridden by this class.
346: * Handles any exception that occurs during callback, including
347: * rethrowing behavior.
348: * <p>
349: * [Issue: Implementation prints stack trace and eats exception to avoid
350: * crashing VA/J.
351: * Consider conditionalizing the implementation to do one thing in VAJ
352: * and something more reasonable in other operating environments.
353: * ]
354: * </p>
355: *
356: * @param exception the exception
357: * @param event the event
358: */
359: protected void handleException(Throwable exception,
360: DropTargetEvent event) {
361: // Currently we never rethrow because VA/Java crashes if an SWT
362: // callback throws anything. Generally catching Throwable is bad, but in
363: // this cases it's better than hanging the image.
364: exception.printStackTrace();
365: event.detail = DND.DROP_NONE;
366: }
367:
368: /**
369: * Performs any work associated with the drop.
370: * <p>
371: * Subclasses must implement this method to provide drop behavior.
372: * </p>
373: *
374: * @param data the drop data
375: * @return <code>true</code> if the drop was successful, and
376: * <code>false</code> otherwise
377: */
378: public abstract boolean performDrop(Object data);
379:
380: /* (non-Javadoc)
381: * Method declared on DropTargetAdapter.
382: * The mouse has moved over the drop target. If the
383: * target item has changed, notify the action and check
384: * that it is still enabled.
385: */
386: private void setFeedback(DropTargetEvent event, int location) {
387: if (feedbackEnabled) {
388: switch (location) {
389: case LOCATION_BEFORE:
390: event.feedback = DND.FEEDBACK_INSERT_BEFORE;
391: break;
392: case LOCATION_AFTER:
393: event.feedback = DND.FEEDBACK_INSERT_AFTER;
394: break;
395: case LOCATION_ON:
396: default:
397: event.feedback = DND.FEEDBACK_SELECT;
398: break;
399: }
400: }
401:
402: // Explicitly inhibit SELECT feedback if desired
403: if (!selectFeedbackEnabled) {
404: event.feedback &= ~DND.FEEDBACK_SELECT;
405: }
406:
407: if (expandEnabled) {
408: event.feedback |= DND.FEEDBACK_EXPAND;
409: }
410: if (scrollEnabled) {
411: event.feedback |= DND.FEEDBACK_SCROLL;
412: }
413: }
414:
415: /**
416: * Sets whether visible insertion feedback should be presented to the user.
417: * <p>
418: * Typical insertion feedback is the horizontal insertion bars that appear
419: * between adjacent items while dragging.
420: * </p>
421: *
422: * @param value
423: * <code>true</code> if visual feedback is desired, and
424: * <code>false</code> if not
425: */
426: public void setFeedbackEnabled(boolean value) {
427: feedbackEnabled = value;
428: }
429:
430: /**
431: * Sets whether selection feedback should be provided during dragging.
432: *
433: * @param value <code>true</code> if selection feedback is desired, and
434: * <code>false</code> if not
435: *
436: * @since 3.2
437: */
438: public void setSelectionFeedbackEnabled(boolean value) {
439: selectFeedbackEnabled = value;
440: }
441:
442: /**
443: * Sets whether auto scrolling and expanding should be provided during dragging.
444: *
445: * @param value <code>true</code> if scrolling and expanding is desired, and
446: * <code>false</code> if not
447: * @since 2.0
448: */
449: public void setScrollExpandEnabled(boolean value) {
450: expandEnabled = value;
451: scrollEnabled = value;
452: }
453:
454: /**
455: * Sets whether auto expanding should be provided during dragging.
456: *
457: * @param value <code>true</code> if expanding is desired, and
458: * <code>false</code> if not
459: * @since 3.4
460: */
461: public void setExpandEnabled(boolean value) {
462: expandEnabled = value;
463: }
464:
465: /**
466: * Sets whether auto scrolling should be provided during dragging.
467: *
468: * @param value <code>true</code> if scrolling is desired, and
469: * <code>false</code> if not
470: * @since 3.4
471: */
472: public void setScrollEnabled(boolean value) {
473: scrollEnabled = value;
474: }
475:
476: /**
477: * Validates dropping on the given object. This method is called whenever some
478: * aspect of the drop operation changes.
479: * <p>
480: * Subclasses must implement this method to define which drops make sense.
481: * </p>
482: *
483: * @param target the object that the mouse is currently hovering over, or
484: * <code>null</code> if the mouse is hovering over empty space
485: * @param operation the current drag operation (copy, move, etc.)
486: * @param transferType the current transfer type
487: * @return <code>true</code> if the drop is valid, and <code>false</code>
488: * otherwise
489: */
490: public abstract boolean validateDrop(Object target, int operation,
491: TransferData transferType);
492:
493: /**
494: * Determine the operation which should be executed given the target and the operation
495: * requested by the user. This method is called whenever some aspect of the drop operation
496: * changes. The operation is one of DND#DROP_DEFAULT, DND#DROP_COPY, DND#DROP_MOVE, DND#DROP_LINK.
497: * <p>
498: * The method returns the operation valid in the given context. The result is one of
499: * DND#DROP_NONE, DND#DROP_COPY, DND#DROP_MOVE, DND#DROP_LINK, DND#DROP_DEFAULT.
500: * </p>
501: * <p>
502: * Subclasses can overwrite this method to define which operation does make
503: * sense in the given context.
504: * </p>
505: *
506: * @param target the object that the mouse is currently hovering over, or
507: * <code>null</code> if the mouse is hovering over empty space
508: * @param operation the current drag operation (copy, move, etc.)
509: * @param transferType the current transfer type
510: * @param operations a bitwise OR'ing of the operations that the DragSource can support
511: * @return the operation which will be executed if no modifier key is pressed
512: * by the user
513: *
514: * @see DND#DROP_NONE
515: * @see DND#DROP_MOVE
516: * @see DND#DROP_COPY
517: * @see DND#DROP_LINK
518: */
519: protected int determineOperation(Object target, int operation,
520: TransferData transferType, int operations) {
521: if (!validateDrop(target, operation, transferType)) {
522: return DND.DROP_NONE;
523: }
524:
525: return operation;
526: }
527: }
|