fixed bug #1115300, #1115292
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / ui / dnd / JdtViewerDropAdapter.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 net.sourceforge.phpdt.internal.ui.dnd;
12
13 import org.eclipse.swt.dnd.DND;
14 import org.eclipse.swt.dnd.DropTargetEvent;
15 import org.eclipse.swt.dnd.DropTargetListener;
16 import org.eclipse.swt.graphics.Point;
17 import org.eclipse.swt.graphics.Rectangle;
18 import org.eclipse.swt.widgets.Item;
19 import org.eclipse.swt.widgets.TableItem;
20 import org.eclipse.swt.widgets.TreeItem;
21
22 import org.eclipse.jface.util.Assert;
23 import org.eclipse.jface.viewers.StructuredViewer;
24
25 /**
26  * A drag and drop adapter to be used together with structured viewers.
27  * The adapater delegates the <code>dragEnter</code>, <code>dragOperationChanged
28  * </code>, <code>dragOver</code> and <code>dropAccept</code> method to the
29  * <code>validateDrop</code> method. Furthermore it adds location feedback.
30  */
31 public class JdtViewerDropAdapter implements DropTargetListener {
32
33         /**
34          * Constant describing the position of the mouse cursor relative 
35          * to the target object.  This means the mouse is positioned
36          * slightly before the target.
37          */
38         protected static final int LOCATION_BEFORE= 1;
39         
40         /**
41          * Constant describing the position of the mouse cursor relative 
42          * to the target object.  This means the mouse is positioned
43          * slightly after the target.
44          */
45         protected static final int LOCATION_AFTER= 2;
46         
47         /**
48          * Constant describing the position of the mouse cursor relative 
49          * to the target object.  This means the mouse is positioned
50          * directly on the target.
51          */
52         protected static final int LOCATION_ON= 3;
53         
54         /**
55          * Constant describing the position of the mouse cursor relative 
56          * to the target object.  This means the mouse is not positioned
57          * over or near any valid target.
58          */
59         protected static final int LOCATION_NONE= 4;
60         
61         /**
62          * The threshold used to determine if the mouse is before or after
63          * an item.
64          */
65         private static final int LOCATION_EPSILON= 5; 
66         
67         /**
68          * Style to enable location feedback.
69          */
70         public static final int INSERTION_FEEDBACK= 1 << 1; 
71
72         private StructuredViewer fViewer;
73         private int fFeedback;
74         private boolean fShowInsertionFeedback;
75         private int fRequestedOperation;
76         private int fLastOperation;
77         protected int fLocation;
78         protected Object fTarget;
79
80         public JdtViewerDropAdapter(StructuredViewer viewer, int feedback) {
81                 fViewer= viewer;
82                 Assert.isNotNull(fViewer);
83                 fFeedback= feedback;
84                 fLastOperation= -1;
85         }
86
87         /**
88          * Controls whether the drop adapter shows insertion feedback or not.
89          * 
90          * @param showInsertionFeedback <code>true</code> if the drop adapter is supposed
91          *      to show insertion feedback. Otherwise <code>false</code>
92          */
93         public void showInsertionFeedback(boolean showInsertionFeedback) {
94                 fShowInsertionFeedback= showInsertionFeedback;
95         }
96         
97         /**
98          * Returns the viewer this adapter is working on.
99          */
100         protected StructuredViewer getViewer() {
101                 return fViewer;
102         } 
103         
104         //---- Hooks to override -----------------------------------------------------
105         
106         /**
107          * The actual drop has occurred. Calls <code>drop(Object target, DropTargetEvent event)
108          * </code>.
109          * @see DropTargetListener#drop(org.eclipse.swt.dnd.DropTargetEvent)
110          */      
111         public void drop(DropTargetEvent event) {
112                 drop(fTarget, event);
113         }
114         
115         /**
116          * The actual drop has occurred.
117          * @param target the drop target in form of a domain element.
118          * @param event the drop traget event
119          */      
120         public void drop(Object target, DropTargetEvent event) {
121         }
122         
123         /**
124          * Checks if the drop is valid. The method calls <code>validateDrop
125          * (Object target, DropTargetEvent event). Implementors can alter the 
126          * <code>currentDataType</code> field and the <code>detail</code> field 
127          * to give feedback about drop acceptence.
128          */
129         public void validateDrop(DropTargetEvent event) {
130                 validateDrop(fTarget, event, fRequestedOperation);
131         }
132         
133         /**
134          * Checks if the drop on the current target is valid. The method
135          * can alter the <code>currentDataType</code> field and the <code>
136          * detail</code> field to give feedback about drop acceptence.
137          * @param target the drop target in form of a domain element.
138          * @param event the drop traget event
139          * @param operation the operation requested by the user.
140          */
141         public void validateDrop(Object target, DropTargetEvent event, int operation) {
142         }
143         
144         public void dragEnter(DropTargetEvent event) {
145                 dragOperationChanged(event);
146         }
147         
148         public void dragLeave(DropTargetEvent event) {
149                 fTarget= null;
150                 fLocation= LOCATION_NONE;
151         }
152         
153         public void dragOperationChanged(DropTargetEvent event) {
154                 fRequestedOperation= event.detail;
155                 fTarget= computeTarget(event);
156                 fLocation= computeLocation(event);
157                 validateDrop(event);
158                 fLastOperation= event.detail;
159                 computeFeedback(event);
160         }
161         
162         public void dragOver(DropTargetEvent event) {
163                 Object oldTarget= fTarget;
164                 fTarget= computeTarget(event);
165                 
166                 //set the location feedback
167                 int oldLocation= fLocation;
168                 fLocation= computeLocation(event);
169                 if (oldLocation != fLocation || oldTarget != fTarget || fLastOperation != event.detail) {
170                         validateDrop(event);
171                         fLastOperation= event.detail;
172                 } else {
173                         event.detail= fLastOperation;
174                 }
175                 computeFeedback(event);
176         }
177         
178         public void dropAccept(DropTargetEvent event) {
179                 fTarget= computeTarget(event);
180                 validateDrop(event);
181                 fLastOperation= event.detail;
182         }
183         
184         /**
185          * Returns the data held by <code>event.item</code>. Inside a viewer
186          * this corresponds to the items data model element.
187          */
188         protected Object computeTarget(DropTargetEvent event) {
189                 return event.item == null ? null : event.item.getData();
190         }
191         
192         /**
193          * Returns the position of the given coordinates relative to the given target.
194          * The position is determined to be before, after, or on the item, based on 
195          * some threshold value. The return value is one of the LOCATION_* constants 
196          * defined in this class.
197          */
198         final protected int computeLocation(DropTargetEvent event) {
199                 if (!(event.item instanceof Item))
200                         return LOCATION_NONE;
201                 
202                 Item item= (Item) event.item;
203                 Point coordinates= fViewer.getControl().toControl(new Point(event.x, event.y));
204                 Rectangle bounds= getBounds(item);
205                 if (bounds == null) {
206                         return LOCATION_NONE;
207                 }
208                 if ((coordinates.y - bounds.y) < LOCATION_EPSILON) {
209                         return LOCATION_BEFORE;
210                 }
211                 if ((bounds.y + bounds.height - coordinates.y) < LOCATION_EPSILON) {
212                         return LOCATION_AFTER;
213                 }
214                 return LOCATION_ON;
215         }
216
217         /**
218          * Returns the bounds of the given item, or <code>null</code> if it is not a 
219          * valid type of item.
220          */
221         private Rectangle getBounds(Item item) {
222                 if (item instanceof TreeItem)
223                         return ((TreeItem) item).getBounds();
224                         
225                 if (item instanceof TableItem)
226                         return ((TableItem) item).getBounds(0);
227                         
228                 return null;
229         }
230
231         /**
232          * Sets the drag under feedback corresponding to the value of <code>fLocation</code>
233          * and the <code>INSERTION_FEEDBACK</code> style bit.
234          */
235         protected void computeFeedback(DropTargetEvent event) {
236                 if (!fShowInsertionFeedback && fLocation != LOCATION_NONE) {
237                         event.feedback= DND.FEEDBACK_SELECT;
238                 } else {
239                         if (fLocation == LOCATION_BEFORE) {
240                                 event.feedback= DND.FEEDBACK_INSERT_BEFORE;
241                         } else if (fLocation == LOCATION_AFTER) {
242                                 event.feedback= DND.FEEDBACK_INSERT_AFTER;
243                         }
244                 }
245                 event.feedback|= fFeedback;
246         }
247         
248         /**
249          * Sets the drop operation to </code>DROP_NODE<code>.
250          */
251         protected void clearDropOperation(DropTargetEvent event) {
252                 event.detail= DND.DROP_NONE;
253         }
254         
255         /**
256          * Returns the requested drop operation.
257          */
258         protected int getRequestedOperation() {
259                 return fRequestedOperation;
260         } 
261 }