KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > internal > dnd > DragUtil


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.ui.internal.dnd;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Iterator JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.jface.util.Geometry;
18 import org.eclipse.swt.SWT;
19 import org.eclipse.swt.graphics.Point;
20 import org.eclipse.swt.graphics.Rectangle;
21 import org.eclipse.swt.widgets.Control;
22 import org.eclipse.swt.widgets.Display;
23 import org.eclipse.swt.widgets.Event;
24 import org.eclipse.swt.widgets.Listener;
25 import org.eclipse.swt.widgets.Shell;
26 import org.eclipse.swt.widgets.Tracker;
27 import org.eclipse.ui.PlatformUI;
28 import org.eclipse.ui.internal.DragCursors;
29
30 /**
31  * Provides the methods for attaching drag-and-drop listeners to SWT controls.
32  */

33 public class DragUtil {
34     private static final String JavaDoc DROP_TARGET_ID = "org.eclipse.ui.internal.dnd.dropTarget"; //$NON-NLS-1$
35

36     /**
37      * The location where all drags will end. If this is non-null, then
38      * all user input is ignored in drag/drop. If null, we use user input
39      * to determine where objects should be dropped.
40      */

41     private static TestDropLocation forcedDropTarget = null;
42     
43     /**
44      * List of IDragOverListener
45      */

46     private static List JavaDoc defaultTargets = new ArrayList JavaDoc();
47
48     /**
49      * Sets the drop target for the given control. It is possible to add one or more
50      * targets for a "null" control. This becomes a default target that is used if no
51      * other targets are found (for example, when dragging objects off the application
52      * window).
53      *
54      * @param control the control that should be treated as a drag target, or null
55      * to indicate the default target
56      * @param target the drag target to handle the given control
57      */

58     public static void addDragTarget(Control control, IDragOverListener target) {
59         if (control == null) {
60             defaultTargets.add(target);
61         } else {
62             List JavaDoc targetList = getTargetList(control);
63
64             if (targetList == null) {
65                 targetList = new ArrayList JavaDoc(1);
66             }
67             targetList.add(target);
68             control.setData(DROP_TARGET_ID, targetList);
69         }
70     }
71
72     /**
73      * Return the list of 'IDragOverListener' elements associated with
74      * the given control. If there's a 'global' listener then always
75      * return it.
76      *
77      * @param control
78      * @return
79      */

80     private static List JavaDoc getTargetList(Control control) {
81         List JavaDoc result = (List JavaDoc) control.getData(DROP_TARGET_ID);
82         return result;
83     }
84
85     /**
86      * Removes a drop target from the given control.
87      *
88      * @param control
89      * @param target
90      */

91     public static void removeDragTarget(Control control,
92             IDragOverListener target) {
93         if (control == null) {
94             defaultTargets.remove(target);
95         } else {
96             List JavaDoc targetList = getTargetList(control);
97             if (targetList != null) {
98                 targetList.remove(target);
99                 if (targetList.isEmpty()) {
100                     control.setData(DROP_TARGET_ID, null);
101                 }
102             }
103         }
104     }
105
106     /**
107      * Shorthand method. Returns the bounding rectangle for the given control, in
108      * display coordinates. Note that all 'Shell' controls are expected to be 'top level'
109      * so DO NOT do the origin offset for them.
110      *
111      * @param draggedItem
112      * @param boundsControl
113      * @return
114      */

115     public static Rectangle getDisplayBounds(Control boundsControl) {
116         Control parent = boundsControl.getParent();
117         if (parent == null || boundsControl instanceof Shell) {
118             return boundsControl.getBounds();
119         }
120
121         return Geometry.toDisplay(parent, boundsControl.getBounds());
122     }
123
124     public static boolean performDrag(final Object JavaDoc draggedItem,
125             Rectangle sourceBounds, Point initialLocation, boolean allowSnapping) {
126
127         IDropTarget target = dragToTarget(draggedItem, sourceBounds,
128                 initialLocation, allowSnapping);
129
130         if (target == null) {
131             return false;
132         }
133
134         target.drop();
135
136         // If the target can handle a 'finished' notification then send one
137
if (target!= null && target instanceof IDropTarget2) {
138             ((IDropTarget2)target).dragFinished(true);
139         }
140
141         return true;
142     }
143
144     /**
145      * Drags the given item to the given location (in display coordinates). This
146      * method is intended for use by test suites.
147      *
148      * @param draggedItem object being dragged
149      * @param finalLocation location being dragged to
150      * @return true iff the drop was accepted
151      */

152     public static boolean dragTo(Display display, Object JavaDoc draggedItem,
153             Point finalLocation, Rectangle dragRectangle) {
154         Control currentControl = SwtUtil.findControl(display, finalLocation);
155
156         IDropTarget target = getDropTarget(currentControl, draggedItem,
157                 finalLocation, dragRectangle);
158
159         if (target == null) {
160             return false;
161         }
162
163         target.drop();
164
165         return true;
166     }
167
168     /**
169      * Forces all drags to end at the given position (display coordinates). Intended
170      * for use by test suites. If this method is called, then all subsequent calls
171      * to performDrag will terminate immediately and behave as though the object were
172      * dragged to the given location. Calling this method with null cancels this
173      * behavior and causes performDrag to behave normally.
174      *
175      * @param forcedLocation location where objects will be dropped (or null to
176      * cause drag/drop to behave normally).
177      */

178     public static void forceDropLocation(TestDropLocation forcedLocation) {
179         forcedDropTarget = forcedLocation;
180     }
181     
182     /**
183      * Drags the given item, given an initial bounding rectangle in display coordinates.
184      * Due to a quirk in the Tracker class, changing the tracking rectangle when using the
185      * keyboard will also cause the mouse cursor to move. Since "snapping" causes the tracking
186      * rectangle to change based on the position of the mouse cursor, it is impossible to do
187      * drag-and-drop with the keyboard when snapping is enabled.
188      *
189      * @param draggedItem object being dragged
190      * @param sourceBounds initial bounding rectangle for the dragged item
191      * @param initialLocation initial position of the mouse cursor
192      * @param allowSnapping true iff the rectangle should snap to the drop location. This must
193      * be false if the user might be doing drag-and-drop using the keyboard.
194      *
195      * @return
196      */

197     static IDropTarget dragToTarget(final Object JavaDoc draggedItem,
198             final Rectangle sourceBounds, final Point initialLocation,
199             final boolean allowSnapping) {
200         final Display display = Display.getCurrent();
201
202         // Testing...immediately 'drop' onto the test target
203
if (forcedDropTarget != null) {
204             Point location = forcedDropTarget.getLocation();
205
206             Control currentControl = SwtUtil.findControl(forcedDropTarget.getShells(), location);
207             return getDropTarget(currentControl, draggedItem, location,
208                     sourceBounds);
209         }
210
211         // Create a tracker. This is just an XOR rect on the screen.
212
// As it moves we notify the drag listeners.
213
final Tracker tracker = new Tracker(display, SWT.NULL);
214         tracker.setStippled(true);
215
216         tracker.addListener(SWT.Move, new Listener() {
217             public void handleEvent(final Event event) {
218                 display.syncExec(new Runnable JavaDoc() {
219                     public void run() {
220                         // Get the curslor location as a point
221
Point location = new Point(event.x, event.y);
222
223                         // Select a drop target; use the global one by default
224
IDropTarget target = null;
225                             
226                         Control targetControl = display.getCursorControl();
227
228                         // Get the drop target for this location
229
target = getDropTarget(targetControl,
230                                 draggedItem, location,
231                                 tracker.getRectangles()[0]);
232
233                         // Set up the tracker feedback based on the target
234
Rectangle snapTarget = null;
235                         if (target != null) {
236                             snapTarget = target.getSnapRectangle();
237
238                             tracker.setCursor(target.getCursor());
239                         } else {
240                             tracker.setCursor(DragCursors
241                                     .getCursor(DragCursors.INVALID));
242                         }
243
244                         // If snapping then reset the tracker's rectangle based on the current drop target
245
if (allowSnapping) {
246                             if (snapTarget == null) {
247                                 snapTarget = new Rectangle(sourceBounds.x
248                                         + location.x - initialLocation.x,
249                                         sourceBounds.y + location.y
250                                                 - initialLocation.y,
251                                         sourceBounds.width, sourceBounds.height);
252                             }
253
254                             // Try to prevent flicker: don't change the rectangles if they're already in
255
// the right location
256
Rectangle[] currentRectangles = tracker.getRectangles();
257
258                             if (!(currentRectangles.length == 1 && currentRectangles[0]
259                                     .equals(snapTarget))) {
260                                 tracker.setRectangles(new Rectangle[] { snapTarget });
261                             }
262                         }
263                     }
264                 });
265             }
266         });
267
268         // Setup...when the drag starts we might already be over a valid target, check this...
269
// If there is a 'global' target then skip the check
270
IDropTarget target = null;
271         Control startControl = display.getCursorControl();
272         
273         if (startControl != null && allowSnapping) {
274             target = getDropTarget(startControl,
275                 draggedItem, initialLocation,
276                 sourceBounds);
277         }
278
279         // Set up an initial tracker rectangle
280
Rectangle startRect = sourceBounds;
281         if (target != null) {
282             Rectangle rect = target.getSnapRectangle();
283             
284             if (rect != null) {
285                 startRect = rect;
286             }
287
288             tracker.setCursor(target.getCursor());
289         }
290         
291         if (startRect != null) {
292             tracker.setRectangles(new Rectangle[] { Geometry.copy(startRect)});
293         }
294
295         // Tracking Loop...tracking is preformed on the 'SWT.Move' listener registered
296
// against the tracker.
297

298         // HACK:
299
// Some control needs to capture the mouse during the drag or other
300
// controls will interfere with the cursor
301
Shell shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
302         if (shell != null) {
303             shell.setCapture(true);
304         }
305
306         // Run tracker until mouse up occurs or escape key pressed.
307
boolean trackingOk = tracker.open();
308
309         // HACK:
310
// Release the mouse now
311
if (shell != null) {
312             shell.setCapture(false);
313         }
314
315         // Done tracking...
316

317         // Get the current drop target
318
IDropTarget dropTarget = null;
319         Point finalLocation = display.getCursorLocation();
320         Control targetControl = display.getCursorControl();
321         dropTarget = getDropTarget(targetControl, draggedItem,
322                 finalLocation, tracker.getRectangles()[0]);
323
324         // Cleanup...
325
tracker.dispose();
326         
327         // if we're going to perform a 'drop' then delay the issuing of the 'finished'
328
// callback until after it's done...
329
if (trackingOk) {
330             return dropTarget;
331         }
332         else if (dropTarget!= null && dropTarget instanceof IDropTarget2) {
333             // If the target can handle a 'finished' notification then send one
334
((IDropTarget2)dropTarget).dragFinished(false);
335         }
336         
337         return null;
338     }
339     
340     /**
341      * Given a list of IDragOverListeners and a description of what is being dragged, it returns
342      * a IDropTarget for the current drop.
343      *
344      * @param toSearch
345      * @param mostSpecificControl
346      * @param draggedObject
347      * @param position
348      * @param dragRectangle
349      * @return
350      */

351     private static IDropTarget getDropTarget(List JavaDoc toSearch,
352             Control mostSpecificControl, Object JavaDoc draggedObject, Point position,
353             Rectangle dragRectangle) {
354         if (toSearch == null) {
355             return null;
356         }
357
358         Iterator JavaDoc iter = toSearch.iterator();
359         while (iter.hasNext()) {
360             IDragOverListener next = (IDragOverListener) iter.next();
361
362             IDropTarget dropTarget = next.drag(mostSpecificControl,
363                     draggedObject, position, dragRectangle);
364
365             if (dropTarget != null) {
366                 return dropTarget;
367             }
368         }
369
370         return null;
371     }
372
373     /**
374      * Returns the drag target for the given control or null if none.
375      *
376      * @param toSearch
377      * @param e
378      * @return
379      */

380     public static IDropTarget getDropTarget(Control toSearch,
381             Object JavaDoc draggedObject, Point position, Rectangle dragRectangle) {
382         // Search for a listener by walking the control's parent hierarchy
383
for (Control current = toSearch; current != null; current = current
384                 .getParent()) {
385             IDropTarget dropTarget = getDropTarget(getTargetList(current),
386                     toSearch, draggedObject, position, dragRectangle);
387
388             if (dropTarget != null) {
389                 return dropTarget;
390             }
391
392             // Don't look to parent shells for drop targets
393
if (current instanceof Shell) {
394                 break;
395             }
396         }
397
398         // No controls could handle this event -- check for default targets
399
return getDropTarget(defaultTargets, toSearch, draggedObject, position,
400                 dragRectangle);
401     }
402
403     /**
404      * Returns the location of the given event, in display coordinates
405      * @return
406      */

407     public static Point getEventLoc(Event event) {
408         Control ctrl = (Control) event.widget;
409         return ctrl.toDisplay(new Point(event.x, event.y));
410     }
411
412 }
413
Popular Tags