Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jface/util/DelegatingDropAdapter.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2003 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.jface.util;
12  
13  import java.util.ArrayList;
14  import java.util.Iterator;
15  import java.util.List;
16  
17  import org.eclipse.core.runtime.Platform;
18  import org.eclipse.swt.dnd.DND;
19  import org.eclipse.swt.dnd.DropTargetEvent;
20  import org.eclipse.swt.dnd.DropTargetListener;
21  import org.eclipse.swt.dnd.Transfer;
22  import org.eclipse.swt.dnd.TransferData;
23  
24  /**
25   * A <code>DelegatingDropAdapter</code> is a <code>DropTargetListener</code> that 
26   * maintains and delegates to a set of {@link TransferDropTargetListener}s. Each 
27   * <code>TransferDropTargetListener</code> can then be implemented as if it were 
28   * the DropTarget's only <code>DropTargetListener</code>.
29   * <p>
30   * On <code>dragEnter</code>, <code>dragOperationChanged</code>, <code>dragOver</code>
31   * and <code>drop</code>, a <i>current</i> listener is obtained from the set of all 
32   * <code>TransferDropTargetListeners</code>. The current listener is the first listener 
33   * to return <code>true</code> for 
34   * {@link TransferDropTargetListener#isEnabled(DropTargetEvent)}.
35   * The current listener is forwarded all <code>DropTargetEvents</code> until some other
36   * listener becomes the current listener, or the drop terminates.
37   * </p>
38   * <p>
39   * After adding all <code>TransferDropTargetListeners</code> to the 
40   * <code>DelegatingDropAdapter</code> the combined set of <code>Transfers</code> should 
41   * be set in the SWT <code>DropTarget</code>. <code>#getTransfers()</code> provides the 
42   * set of <code>Transfer</code> types of all <code>TransferDropTargetListeners</code>. 
43   * </p>
44   * <p>
45   * The following example snippet shows a <code>DelegatingDropAdapter</code> with two
46   * <code>TransferDropTargetListeners</code>. One supports dropping resources and 
47   * demonstrates how a listener can be disabled in the isEnabled method. 
48   * The other listener supports text transfer. 
49   * </p>
50   * <code><pre>
51   *    final TreeViewer viewer = new TreeViewer(shell, SWT.NONE);
52   *     DelegatingDropAdapter dropAdapter = new DelegatingDropAdapter();
53   *    dropAdapter.addDropTargetListener(new TransferDropTargetListener() {
54   *      public Transfer getTransfer() {
55   *        return ResourceTransfer.getInstance();
56   *      }
57   *      public boolean isEnabled(DropTargetEvent event) {
58   *        // disable drop listener if there is no viewer selection
59   *        if (viewer.getSelection().isEmpty())
60   *          return false;
61   *        return true;
62   *      }
63   *      public void dragEnter(DropTargetEvent event) {}
64   *      public void dragLeave(DropTargetEvent event) {}
65   *      public void dragOperationChanged(DropTargetEvent event) {}
66   *      public void dragOver(DropTargetEvent event) {}
67   *      public void drop(DropTargetEvent event) {
68   *        if (event.data == null)
69   *          return;
70   *        IResource[] resources = (IResource[]) event.data;
71   *        if (event.detail == DND.DROP_COPY) {
72   *          // copy resources
73   *        } else {
74   *          // move resources
75   *        }
76   *          
77   *      }
78   *      public void dropAccept(DropTargetEvent event) {}
79   *    });
80   *    dropAdapter.addDropTargetListener(new TransferDropTargetListener() {
81   *      public Transfer getTransfer() {
82   *        return TextTransfer.getInstance();
83   *      }
84   *      public boolean isEnabled(DropTargetEvent event) {
85   *        return true;
86   *      }
87   *      public void dragEnter(DropTargetEvent event) {}
88   *      public void dragLeave(DropTargetEvent event) {}
89   *      public void dragOperationChanged(DropTargetEvent event) {}
90   *      public void dragOver(DropTargetEvent event) {}
91   *      public void drop(DropTargetEvent event) {
92   *        if (event.data == null)
93   *          return;
94   *        System.out.println(event.data);
95   *      }
96   *      public void dropAccept(DropTargetEvent event) {}
97   *    });    
98   *    viewer.addDropSupport(DND.DROP_COPY | DND.DROP_MOVE, dropAdapter.getTransfers(), dropAdapter);
99   * </pre></code>
100  * <p>
101  * NOTE: This API is experimental and subject to change including removal.
102  * </p>
103  * @since 2.2
104  */
105 public class DelegatingDropAdapter implements DropTargetListener {
106   private List listeners = new ArrayList();
107   private TransferDropTargetListener currentListener;
108   private int originalDropType;
109 
110   /**
111    * Adds the given <code>TransferDropTargetListener</code>.
112    * 
113    * @param listener the new listener
114    */
115   public void addDropTargetListener(TransferDropTargetListener listener) {
116     listeners.add(listener);
117   }
118   /**
119    * The cursor has entered the drop target boundaries. The current listener is 
120    * updated, and <code>#dragEnter()</code> is forwarded to the current listener.
121    * 
122    * @param event the drop target event 
123    * @see DropTargetListener#dragEnter(DropTargetEvent)
124    */
125   public void dragEnter(DropTargetEvent event) {
126 //    if (Policy.DEBUG_DRAG_DROP)
127 //      System.out.println("Drag Enter: " + toString()); //$NON-NLS-1$
128     originalDropType = event.detail;
129     updateCurrentListener(event);
130   }
131   /**
132    * The cursor has left the drop target boundaries. The event is forwarded to the 
133    * current listener.
134    * 
135    * @param event the drop target event
136    * @see DropTargetListener#dragLeave(DropTargetEvent)
137    */
138   public void dragLeave(final DropTargetEvent event) {
139 //    if (Policy.DEBUG_DRAG_DROP)
140 //      System.out.println("Drag Leave: " + toString()); //$NON-NLS-1$
141     setCurrentListener(null, event);
142   }
143   /**
144    * The operation being performed has changed (usually due to the user changing 
145    * a drag modifier key while dragging). Updates the current listener and forwards 
146    * this event to that listener.
147    * 
148    * @param event the drop target event
149    * @see DropTargetListener#dragOperationChanged(DropTargetEvent)
150    */
151   public void dragOperationChanged(final DropTargetEvent event) {
152 //    if (Policy.DEBUG_DRAG_DROP)
153 //      System.out.println("Drag Operation Changed to: " + event.detail); //$NON-NLS-1$
154     originalDropType = event.detail;
155     TransferDropTargetListener oldListener = getCurrentListener();
156     updateCurrentListener(event);
157     final TransferDropTargetListener newListener = getCurrentListener();
158     // only notify the current listener if it hasn't changed based on the 
159     // operation change. otherwise the new listener would get a dragEnter 
160     // followed by a dragOperationChanged with the exact same event. 
161     if (newListener != null && newListener == oldListener) {
162       Platform.run(new SafeRunnable() {
163         public void run() throws Exception {
164           newListener.dragOperationChanged(event);
165         }
166       });
167     }
168   }
169   /**
170    * The cursor is moving over the drop target. Updates the current listener and 
171    * forwards this event to that listener. If no listener can handle the drag 
172    * operation the <code>event.detail</code> field is set to <code>DND.DROP_NONE</code> 
173    * to indicate an invalid drop.
174    *   
175    * @param event the drop target event
176    * @see DropTargetListener#dragOver(DropTargetEvent)
177    */
178   public void dragOver(final DropTargetEvent event) {
179     TransferDropTargetListener oldListener = getCurrentListener();
180     updateCurrentListener(event);
181     final TransferDropTargetListener newListener = getCurrentListener();
182   
183     // only notify the current listener if it hasn't changed based on the 
184     // drag over. otherwise the new listener would get a dragEnter 
185     // followed by a dragOver with the exact same event. 
186     if (newListener != null && newListener == oldListener) {
187       Platform.run(new SafeRunnable() {
188         public void run() throws Exception {
189           newListener.dragOver(event);
190         }
191       });
192     }
193   }
194   /**
195    * Forwards this event to the current listener, if there is one. Sets the
196    * current listener to <code>null</code> afterwards.
197    * 
198    * @param event the drop target event
199    * @see DropTargetListener#drop(DropTargetEvent)
200    */
201   public void drop(final DropTargetEvent event) {
202 //    if (Policy.DEBUG_DRAG_DROP)
203 //      System.out.println("Drop: " + toString()); //$NON-NLS-1$
204     updateCurrentListener(event);
205     if (getCurrentListener() != null) { 
206       Platform.run(new SafeRunnable() {
207         public void run() throws Exception {
208           getCurrentListener().drop(event);
209         }
210       });
211     }
212     setCurrentListener(null, event);
213   }
214   /**
215    * Forwards this event to the current listener if there is one.
216    * 
217    * @param event the drop target event
218    * @see DropTargetListener#dropAccept(DropTargetEvent)
219    */
220   public void dropAccept(final DropTargetEvent event) {
221 //    if (Policy.DEBUG_DRAG_DROP)
222 //      System.out.println("Drop Accept: " + toString()); //$NON-NLS-1$
223     if (getCurrentListener() != null) {
224       Platform.run(new SafeRunnable() {
225         public void run() throws Exception {
226           getCurrentListener().dropAccept(event);
227         }
228       });
229     }
230   }
231   /**
232    * Returns the listener which currently handles drop events.
233    * 
234    * @return the <code>TransferDropTargetListener</code> which currently 
235    *   handles drop events.
236    */
237   private TransferDropTargetListener getCurrentListener() {
238     return currentListener;
239   }
240   /**
241    * Returns the transfer data type supported by the given listener.
242    * Returns <code>null</code> if the listener does not support any of the   
243    * specified data types.
244    *  
245    * @param dataTypes available data types
246    * @param listener <code>TransferDropTargetListener</code> to use for testing 
247    *   supported data types.
248    * @return the transfer data type supported by the given listener or 
249    *   <code>null</code>.
250    */
251   private TransferData getSupportedTransferType(TransferData[] dataTypes, TransferDropTargetListener listener) {
252     for (int i = 0; i < dataTypes.length; i++) {
253       if (listener.getTransfer().isSupportedType(dataTypes[i])) {
254         return dataTypes[i];
255       }
256     }
257     return null;
258   }
259   /**
260    * Returns the combined set of <code>Transfer</code> types of all 
261    * <code>TransferDropTargetListeners</code>.
262    * 
263    * @return the combined set of <code>Transfer</code> types
264    */
265   public Transfer[] getTransfers() {
266     Transfer[] types = new Transfer[listeners.size()];
267     for (int i = 0; i < listeners.size(); i++) {
268       TransferDropTargetListener listener = (TransferDropTargetListener)listeners.get(i);
269       types[i] = listener.getTransfer();
270     }
271     return types;
272   }
273   /**
274    * Returns <code>true</code> if there are no listeners to delegate events to.
275    * 
276    * @return <code>true</code> if there are no <code>TransferDropTargetListeners</code>
277    *  <code>false</code> otherwise
278    */
279   public boolean isEmpty() {
280     return listeners.isEmpty();
281   }
282   /**
283    * Removes the given <code>TransferDropTargetListener</code>.
284    * Listeners should not be removed while a drag and drop operation is in progress.
285    * 
286    * @param listener the listener to remove
287    */
288   public void removeDropTargetListener(TransferDropTargetListener listener) {
289     if (currentListener == listener)
290       currentListener = null;
291     listeners.remove(listener);
292   }
293   /**
294    * Sets the current listener to <code>listener</code>. Sends the given 
295    * <code>DropTargetEvent</code> if the current listener changes.
296    * 
297    * @return <code>true</code> if the new listener is different than the previous
298    *  <code>false</code> otherwise
299    */
300   private boolean setCurrentListener(TransferDropTargetListener listener, final DropTargetEvent event) {
301     if (currentListener == listener) 
302       return false;
303     if (currentListener != null) {
304       Platform.run(new SafeRunnable() {
305         public void run() throws Exception {      
306           currentListener.dragLeave(event);
307         }
308       });
309     }
310     currentListener = listener;
311 //    if (Policy.DEBUG_DRAG_DROP)
312 //      System.out.println("Current drop listener: " + listener); //$NON-NLS-1$
313     if (currentListener != null) {
314       Platform.run(new SafeRunnable() {
315         public void run() throws Exception {      
316           currentListener.dragEnter(event);
317         }
318       });
319     }
320     return true;
321   }
322   /**
323    * Updates the current listener to one that can handle the drop. There can be many
324    * listeners and each listener may be able to handle many <code>TransferData</code> 
325    * types. The first listener found that can handle a drop of one of the given 
326    * <code>TransferData</code> types will be selected.
327    * If no listener can handle the drag operation the <code>event.detail</code> field
328    * is set to <code>DND.DROP_NONE</code> to indicate an invalid drop.
329    *
330    * @param event the drop target event
331    */
332   private void updateCurrentListener(DropTargetEvent event) {
333     int originalDetail = event.detail;
334     // revert the detail to the "original" drop type that the User indicated.
335     // this is necessary because the previous listener may have changed the detail 
336     // to something other than what the user indicated.
337     event.detail = originalDropType;
338   
339     Iterator iter = listeners.iterator();
340     while (iter.hasNext()) {
341       TransferDropTargetListener listener = (TransferDropTargetListener) iter.next();
342       TransferData dataType = getSupportedTransferType(event.dataTypes, listener); 
343       if (dataType != null) {
344         TransferData originalDataType = event.currentDataType;
345         // set the data type supported by the drop listener
346         event.currentDataType = dataType;
347         if (listener.isEnabled(event)) {
348           // if the listener stays the same, set its previously determined  
349           // event detail 
350           if (!setCurrentListener(listener, event))
351             event.detail = originalDetail;
352           return;
353         } else {
354           event.currentDataType = originalDataType;
355         }        
356       }      
357     }
358     setCurrentListener(null, event);
359     event.detail = DND.DROP_NONE;
360   }
361 }