KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > emf > edit > ui > dnd > EditingDomainViewerDropAdapter


1 /**
2  * <copyright>
3  *
4  * Copyright (c) 2002-2004 IBM Corporation and others.
5  * All rights reserved. This program and the accompanying materials
6  * are made available under the terms of the Eclipse Public License v1.0
7  * which accompanies this distribution, and is available at
8  * http://www.eclipse.org/legal/epl-v10.html
9  *
10  * Contributors:
11  * IBM - Initial API and implementation
12  *
13  * </copyright>
14  *
15  * $Id: EditingDomainViewerDropAdapter.java,v 1.4 2005/06/14 04:18:18 marcelop Exp $
16  */

17 package org.eclipse.emf.edit.ui.dnd;
18
19
20 import java.util.ArrayList JavaDoc;
21 import java.util.Collection JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Iterator JavaDoc;
24
25 import org.eclipse.jface.viewers.IStructuredSelection;
26 import org.eclipse.jface.viewers.Viewer;
27 import org.eclipse.swt.SWT;
28 import org.eclipse.swt.dnd.DND;
29 import org.eclipse.swt.dnd.DropTargetAdapter;
30 import org.eclipse.swt.dnd.DropTargetEvent;
31 import org.eclipse.swt.dnd.TransferData;
32 import org.eclipse.swt.graphics.Point;
33 import org.eclipse.swt.graphics.Rectangle;
34 import org.eclipse.swt.widgets.Control;
35 import org.eclipse.swt.widgets.Item;
36 import org.eclipse.swt.widgets.TableItem;
37 import org.eclipse.swt.widgets.TreeItem;
38 import org.eclipse.swt.widgets.Widget;
39
40 import org.eclipse.emf.common.command.Command;
41 import org.eclipse.emf.common.ui.viewer.ExtendedTableTreeViewer;
42 import org.eclipse.emf.edit.command.DragAndDropCommand;
43 import org.eclipse.emf.edit.command.DragAndDropFeedback;
44 import org.eclipse.emf.edit.domain.EditingDomain;
45
46
47 /**
48  * This implementation of a drop target listener is designed to turn a drag and drop
49  * operation into a {@link Command} based on the model objects of an
50  * {@link EditingDomain} and created by {@link DragAndDropCommand#create}. It is
51  * designed to do early data transfer so the the enablement and feedback of the
52  * drag and drop interaction can intimately depend on the state of the model objects
53  * involed. On some platforms, however, early data transfer is not available, so this
54  * feedback cannot be provided.
55  * <p>
56  * The base implementation of this class should be sufficient for most applications.
57  * Any change in behaviour is typically accomplished by overriding
58  * {@link org.eclipse.emf.edit.provider.ItemProviderAdapter}.createDragAndDropCommand
59  * to return a derived implementation of {@link DragAndDropCommand}.
60  * This is how one these adapters is typically hooked up:
61  * <pre>
62  * viewer.addDropSupport
63  * (DND.DROP_COPY | DND.DROP_MOVE | DND.DROP_LINK,
64  * new Transfer [] { LocalTransfer.getInstance() },
65  * EditingDomainViewerDropAdapter(viewer));
66  * </pre>
67  * <p>
68  * This implementation prefers to use a {@link LocalTransfer},
69  * which short-circuits the transfer process for simple transfers within the workbench,
70  * the method {@link #getDragSource} can be overriden to change the behaviour.
71  * The implementation also only handles an {@link IStructuredSelection},
72  * but the method {@link #extractDragSource} can be overriden to change the behaviour.
73  * <p>
74  * SWT's {@link DND#FEEDBACK_SCROLL auto-scroll} and {@link DND#FEEDBACK_EXPAND auto-expand}
75  * (hover) are enabled by default. The method {@link #getAutoFeedback} can be overridden
76  * to change this behaviour.
77  */

78 public class EditingDomainViewerDropAdapter extends DropTargetAdapter
79 {
80   /**
81    * This indicates whether the current platform is motif, which needs
82    * special treatment, since it cannot do early data transfer, but doesn't
83    * cleanly return null either.
84    */

85   protected final static boolean IS_MOTIF = "motif".equals(SWT.getPlatform());
86
87   /**
88    * This is the viewer for which this is a drop target listener.
89    */

90   protected Viewer viewer;
91
92   /**
93    * This is the domain in which drag and drop commands will be executed.
94    */

95   protected EditingDomain domain;
96
97   /**
98    * This is the collection of source objects being dragged.
99    */

100   protected Collection JavaDoc source;
101
102   /**
103    * This is the command created during dragging which provides the feedback and will carry out the action upon completion.
104    */

105   protected Command command;
106  
107   /**
108    * This records the object for which the {@link #command} was created.
109    */

110   protected Object JavaDoc commandTarget;
111
112   /**
113    * This keeps track of the original operation that the user requested, before
114    * we started changing the event.detail. We always try to create the command
115    * using this.
116    */

117   protected int originalOperation;
118
119   /**
120    * This keeps track of the information used to create {@link #command}, but
121    * does not need to be disposed. This allows us to dispose of the command
122    * in dragLeave, and then, if we need to execute it, recreate it in drop.
123    */

124   protected DragAndDropCommandInformation dragAndDropCommandInformation;
125
126   /**
127    * This creates an instance with the given domain and viewer.
128    */

129   public EditingDomainViewerDropAdapter(EditingDomain domain, Viewer viewer)
130   {
131     this.viewer = viewer;
132     this.domain = domain;
133   }
134
135   /**
136    * This is called when the mouse first enters or starts dragging in the viewer.
137    */

138   public void dragEnter(DropTargetEvent event)
139   {
140     // Remember the requested operation.
141
originalOperation = event.detail;
142
143     helper(event);
144   }
145
146   /**
147    * This is called when the mouse leaves or stops dragging in the viewer,
148    * whether the operation was aborted or is about to do a dropAccept and drop.
149    * The event argument is uninitialized, so it is impossible to distinguish
150    * between the two cases. So, we do the clean-up now and recreate the
151    * command later, if necessary.
152    */

153   public void dragLeave(DropTargetEvent event)
154   {
155     // Clean up the command if there is one. If we need it again in drop,
156
// we'll recreate it from dragAndDropCommandInformation.
157
//
158
if (command != null)
159     {
160       command.dispose();
161       command = null;
162       commandTarget = null;
163     }
164
165     // Clear the source data. We won't need this again, since, if it was
166
// available, it's already in the command.
167
//
168
source = null;
169   }
170
171   /**
172    * This is called when the operation has changed in some way, typically because the user changes keyboard modifiers.
173    */

174   public void dragOperationChanged(DropTargetEvent event)
175   {
176     // Remember the requested operation.
177
originalOperation = event.detail;
178
179     helper(event);
180   }
181
182   /**
183    * This is called repeatedly, as the mouse moves over the viewer.
184    */

185   public void dragOver(DropTargetEvent event)
186   {
187     helper(event);
188   }
189
190   /**
191    * This is called when the mouse is released over the viewer to initiate a
192    * drop, between dragLeave and drop.
193    */

194   public void dropAccept(DropTargetEvent event)
195   {
196     helper(event);
197   }
198
199   /**
200    * This is called to indicate that the drop action should be invoked.
201    */

202   public void drop(DropTargetEvent event)
203   {
204     // A command was created if the source was available early, and the
205
// information used to create it was cached...
206
//
207
if (dragAndDropCommandInformation != null)
208     {
209       // Recreate the command.
210
//
211
command = dragAndDropCommandInformation.createCommand();
212     }
213     else
214     {
215       // Otherwise, the source should be available now as event.data, and we
216
// can create the command.
217
//
218
source = extractDragSource(event.data);
219       Object JavaDoc target = extractDropTarget(event.item);
220       command = DragAndDropCommand.create(domain, target, getLocation(event), event.operations, originalOperation, source);
221     }
222
223     // If the command can execute...
224
//
225
if (command.canExecute())
226     {
227       // Execute it.
228
//
229
domain.getCommandStack().execute(command);
230     }
231     else
232     {
233       // Otherwise, let's call the whole thing off.
234
//
235
event.detail = DND.DROP_NONE;
236       command.dispose();
237     }
238
239     // Clean up the state.
240
//
241
command = null;
242     commandTarget = null;
243     source = null;
244   }
245
246   /**
247    * This method is called the same way for each of the
248    * {@link org.eclipse.swt.dnd.DropTargetListener} methods, except for leave
249    * and drop. If the source is available early, it creates or revalidates
250    * the {@link DragAndDropCommand}, and updates the event's detail (operation)
251    * and feedback (drag under effect), appropriately.
252    */

253   protected void helper(DropTargetEvent event)
254   {
255     // If we can't do anything else, we'll provide the default select feedback
256
// and enable auto-scroll and auto-expand effects.
257
event.feedback = DND.FEEDBACK_SELECT | getAutoFeedback();
258
259     // If we don't already have it, try to get the source early. We can't give
260
// feedback if it's not available yet (this is platform-dependent).
261
//
262
if (source == null)
263     {
264       source = getDragSource(event);
265       if (source == null) return;
266     }
267
268     // Get the target object from the item widget and the mouse location in it.
269
//
270
Object JavaDoc target = extractDropTarget(event.item);
271     float location = getLocation(event);
272
273     // Determine if we can create a valid command at the current location.
274
//
275
boolean valid = false;
276
277     // If we don't have a previous cached command...
278
//
279
if (command == null)
280     {
281       // We'll need to keep track of the information we use to create the
282
// command, so that we can recreate it in drop.
283
dragAndDropCommandInformation = new DragAndDropCommandInformation(domain, target, location, event.operations, originalOperation, source);
284
285       // Remember the target; create the command and test if it is executable.
286
//
287
commandTarget = target;
288       command = dragAndDropCommandInformation.createCommand();
289       valid = command.canExecute();
290     }
291     else
292     {
293       // Check if the cached command can provide DND feedback/revalidation.
294
//
295
if (target == commandTarget && command instanceof DragAndDropFeedback)
296       {
297         // If so, revalidate the command.
298
//
299
valid = ((DragAndDropFeedback)command).validate(target, location, event.operations, originalOperation, source);
300
301         // Keep track of any changes to the command information.
302
dragAndDropCommandInformation = new DragAndDropCommandInformation(domain, target, location, event.operations, originalOperation, source);
303       }
304       else
305       {
306         // If not, dispose the current command and create a new one.
307
//
308
dragAndDropCommandInformation = new DragAndDropCommandInformation(domain, target, location, event.operations, originalOperation, source);
309         commandTarget = target;
310         command.dispose();
311         command = dragAndDropCommandInformation.createCommand();
312         valid = command.canExecute();
313       }
314     }
315
316     // If this command can provide detailed drag and drop feedback...
317
//
318
if (command instanceof DragAndDropFeedback)
319     {
320       // Use it for the operation and drag under effect.
321
//
322
DragAndDropFeedback dragAndDropFeedback = (DragAndDropFeedback)command;
323       event.detail = dragAndDropFeedback.getOperation();
324       event.feedback = dragAndDropFeedback.getFeedback() | getAutoFeedback();
325     }
326     else if (!valid)
327     {
328       // There is no executable command, so we'd better nix the whole deal.
329
//
330
event.detail = DND.DROP_NONE;
331     }
332   }
333
334   /**
335    * This returns the bitwise OR'ed flags for desired auto-feedback effects.
336    * Drag under effect DND constants are always OR'ed with this to enable them.
337    * This implementation enables {@link DND#FEEDBACK_SCROLL auto-scroll} and
338    * {@link DND#FEEDBACK_EXPAND auto-expand} (hover).
339    */

340   protected int getAutoFeedback()
341   {
342     return DND.FEEDBACK_SCROLL | DND.FEEDBACK_EXPAND;
343   }
344
345   /**
346    * This attempts to extract the drag source from the event early, i.e.,
347    * before the drop method. This implementation tries to use a
348    * {@link org.eclipse.emf.edit.ui.dnd.LocalTransfer}. If the data is not yet
349    * available (e.g. on platforms other than win32), it just returns null.
350    */

351   protected Collection JavaDoc getDragSource(DropTargetEvent event)
352   {
353     // Check whether the current data type can be transfered locally.
354
//
355
LocalTransfer localTransfer = LocalTransfer.getInstance();
356     if (!localTransfer.isSupportedType(event.currentDataType))
357     {
358       // Iterate over the data types to see if there is a datatype that supports a local transfer.
359
//
360
TransferData [] dataTypes = event.dataTypes;
361       for (int i = 0; i < dataTypes.length; ++i)
362       {
363         TransferData transferData = dataTypes[i];
364
365         // If the local tansfer supports this datatype, switch to that data type
366
//
367
if (localTransfer.isSupportedType(transferData))
368         {
369           event.currentDataType = transferData;
370         }
371       }
372
373       return null;
374     }
375     else
376     {
377       // Motif kludge: we would get something random instead of null.
378
//
379
if (IS_MOTIF) return null;
380
381       // Transfer the data and, if non-null, extract it.
382
//
383
Object JavaDoc object = localTransfer.nativeToJava(event.currentDataType);
384       return object == null ? null : extractDragSource(object);
385     }
386   }
387
388   /**
389    * This extracts a collection of dragged source objects from the given object retrieved from the transfer agent.
390    * This default implementation converts a structured selection into a collection of elements.
391    */

392   protected Collection JavaDoc extractDragSource(Object JavaDoc object)
393   {
394     // Transfer the data and convert the structured selection to a collection of objects.
395
//
396
if (object instanceof IStructuredSelection)
397     {
398       Collection JavaDoc result = new ArrayList JavaDoc();
399       for (Iterator JavaDoc elements = ((IStructuredSelection)object).iterator(); elements.hasNext(); )
400       {
401         result.add(elements.next());
402       }
403       return result;
404     }
405     else
406     {
407       return Collections.EMPTY_LIST;
408     }
409   }
410
411   /**
412    * This extracts an object from the given item widget, providing the special
413    * support required by an
414    * {@link org.eclipse.emf.common.ui.viewer.ExtendedTableTreeViewer.ExtendedTableTreeItem}.
415    */

416   protected static Object JavaDoc extractDropTarget(Widget item)
417   {
418     if (item == null) return null;
419     return item.getData(ExtendedTableTreeViewer.ITEM_ID) instanceof Item ?
420       ((Item)item.getData(ExtendedTableTreeViewer.ITEM_ID)).getData() :
421       item.getData();
422   }
423
424
425   /**
426    * This returns the location of the mouse in the vertical direction, relative
427    * to the item widget, from 0 (top) to 1 (bottom).
428    */

429   protected static float getLocation(DropTargetEvent event)
430   {
431     if (event.item instanceof TreeItem)
432     {
433       TreeItem treeItem = (TreeItem)event.item;
434       Control control = treeItem.getParent();
435       Point point = control.toControl(new Point(event.x, event.y));
436       Rectangle bounds = treeItem.getBounds();
437       return (float)(point.y - bounds.y) / (float)bounds.height;
438     }
439     else if (event.item instanceof TableItem)
440     {
441       TableItem tableItem = (TableItem)event.item;
442       Control control = tableItem.getParent();
443       Point point = control.toControl(new Point(event.x, event.y));
444       Rectangle bounds = tableItem.getBounds(0);
445       return (float)(point.y - bounds.y) / (float)bounds.height;
446     }
447     else
448     {
449       return 0.0F;
450     }
451   }
452   
453   /**
454    * This holds all of the information used to create a
455    * {@link DragAndDropCommand}, but does not need to be disposed.
456    */

457   protected static class DragAndDropCommandInformation
458   {
459     protected EditingDomain domain;
460     protected Object JavaDoc target;
461     protected float location;
462     protected int operations;
463     protected int operation;
464     protected Collection JavaDoc source;
465     public DragAndDropCommandInformation
466       (EditingDomain domain, Object JavaDoc target, float location, int operations, int operation, Collection JavaDoc source)
467     {
468       this.domain = domain;
469       this.target = target;
470       this.location = location;
471       this.operations = operations;
472       this.operation = operation;
473       this.source = new ArrayList JavaDoc(source);
474     }
475
476     public Command createCommand()
477     {
478       return DragAndDropCommand.create(domain, target, location, operations, operation, source);
479     }
480   }
481 }
482
Popular Tags