comparison dwtx/jface/viewers/ViewerDropAdapter.d @ 10:b6c35faf97c8

Viewers
author Frank Benoit <benoit@tionex.de>
date Mon, 31 Mar 2008 00:47:19 +0200
parents
children ea8ff534f622
comparison
equal deleted inserted replaced
9:6c14e54dfc11 10:b6c35faf97c8
1 /*******************************************************************************
2 * Copyright (c) 2000, 2007 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 * Port to the D programming language:
11 * Frank Benoit <benoit@tionex.de>
12 *******************************************************************************/
13 module dwtx.jface.viewers.ViewerDropAdapter;
14
15 import dwtx.jface.viewers.Viewer;
16 import dwtx.jface.viewers.IStructuredSelection;
17 import dwtx.jface.viewers.ISelection;
18
19 import dwt.dnd.DND;
20 import dwt.dnd.DropTargetAdapter;
21 import dwt.dnd.DropTargetEvent;
22 import dwt.dnd.TransferData;
23 import dwt.graphics.Point;
24 import dwt.graphics.Rectangle;
25 import dwt.widgets.Item;
26 import dwt.widgets.TableItem;
27 import dwt.widgets.TreeItem;
28
29 import dwt.dwthelper.utils;
30
31 /**
32 * This adapter class provides generic drag-and-drop support for a viewer.
33 * <p>
34 * Subclasses must implement the following methods:
35 * <ul>
36 * <li><code>validateDrop</code> - identifies valid drop targets in viewer</li>
37 * <li><code>performDrop</code> - carries out a drop into a viewer</li>
38 * </ul>
39 * The <code>setFeedbackEnabled</code> method can be called to turn on and off
40 * visual insertion feedback (on by default).
41 * </p>
42 */
43 public abstract class ViewerDropAdapter : DropTargetAdapter {
44
45 /**
46 * Constant describing the position of the cursor relative
47 * to the target object. This means the mouse is positioned
48 * slightly before the target.
49 * @see #getCurrentLocation()
50 */
51 public static const int LOCATION_BEFORE = 1;
52
53 /**
54 * Constant describing the position of the cursor relative
55 * to the target object. This means the mouse is positioned
56 * slightly after the target.
57 * @see #getCurrentLocation()
58 */
59 public static const int LOCATION_AFTER = 2;
60
61 /**
62 * Constant describing the position of the cursor relative
63 * to the target object. This means the mouse is positioned
64 * directly on the target.
65 * @see #getCurrentLocation()
66 */
67 public static const int LOCATION_ON = 3;
68
69 /**
70 * Constant describing the position of the cursor relative
71 * to the target object. This means the mouse is not positioned
72 * over or near any valid target.
73 * @see #getCurrentLocation()
74 */
75 public static const int LOCATION_NONE = 4;
76
77 /**
78 * The viewer to which this drop support has been added.
79 */
80 private Viewer viewer;
81
82 /**
83 * The current operation.
84 */
85 private int currentOperation = DND.DROP_NONE;
86
87 /**
88 * The last valid operation.
89 */
90 private int lastValidOperation = DND.DROP_NONE;
91
92 /**
93 * The data item currently under the mouse.
94 */
95 private Object currentTarget;
96
97 /**
98 * Information about the position of the mouse relative to the
99 * target (before, on, or after the target. Location is one of
100 * the <code>LOCATION_* </code> constants defined in this type.
101 */
102 private int currentLocation;
103
104 /**
105 * A flag that allows adapter users to turn the insertion
106 * feedback on or off. Default is <code>true</code>.
107 */
108 private bool feedbackEnabled = true;
109
110 /**
111 * A flag that allows adapter users to turn auto scrolling
112 * and expanding on or off. Default is <code>true</code>.
113 */
114 private bool scrollExpandEnabled = true;
115
116 /**
117 * A flag that allows adapter users to turn selection feedback
118 * on or off. Default is <code>true</code>.
119 */
120 private bool selectFeedbackEnabled = true;
121
122 /**
123 * Creates a new drop adapter for the given viewer.
124 *
125 * @param viewer the viewer
126 */
127 protected this(Viewer viewer) {
128 this.viewer = viewer;
129 }
130
131 /**
132 * Returns the position of the given event's coordinates relative to its target.
133 * The position is determined to be before, after, or on the item, based on
134 * some threshold value.
135 *
136 * @param event the event
137 * @return one of the <code>LOCATION_* </code>constants defined in this class
138 */
139 protected int determineLocation(DropTargetEvent event) {
140 if (!( cast(Item)event.item )) {
141 return LOCATION_NONE;
142 }
143 Item item = cast(Item) event.item;
144 Point coordinates = new Point(event.x, event.y);
145 coordinates = viewer.getControl().toControl(coordinates);
146 if (item !is null) {
147 Rectangle bounds = getBounds(item);
148 if (bounds is null) {
149 return LOCATION_NONE;
150 }
151 if ((coordinates.y - bounds.y) < 5) {
152 return LOCATION_BEFORE;
153 }
154 if ((bounds.y + bounds.height - coordinates.y) < 5) {
155 return LOCATION_AFTER;
156 }
157 }
158 return LOCATION_ON;
159 }
160
161 /**
162 * Returns the target item of the given drop event.
163 *
164 * @param event the event
165 * @return The target of the drop, may be <code>null</code>.
166 */
167 protected Object determineTarget(DropTargetEvent event) {
168 return event.item is null ? null : event.item.getData();
169 }
170
171 /* (non-Javadoc)
172 * Method declared on DropTargetAdapter.
173 * The mouse has moved over the drop target. If the
174 * target item has changed, notify the action and check
175 * that it is still enabled.
176 */
177 private void doDropValidation(DropTargetEvent event) {
178 //update last valid operation
179 if (event.detail !is DND.DROP_NONE) {
180 lastValidOperation = event.detail;
181 }
182 //valid drop and set event detail accordingly
183 if (validateDrop(currentTarget, event.detail, event.currentDataType)) {
184 currentOperation = lastValidOperation;
185 } else {
186 currentOperation = DND.DROP_NONE;
187 }
188 event.detail = currentOperation;
189 }
190
191 /* (non-Javadoc)
192 * Method declared on DropTargetAdapter.
193 * The drag has entered this widget's region. See
194 * if the drop should be allowed.
195 */
196 public void dragEnter(DropTargetEvent event) {
197 currentTarget = determineTarget(event);
198 doDropValidation(event);
199 }
200
201 /* (non-Javadoc)
202 * Method declared on DropTargetAdapter.
203 * The drop operation has changed, see if the action
204 * should still be enabled.
205 */
206 public void dragOperationChanged(DropTargetEvent event) {
207 currentTarget = determineTarget(event);
208 doDropValidation(event);
209 }
210
211 /* (non-Javadoc)
212 * Method declared on DropTargetAdapter.
213 * The mouse has moved over the drop target. If the
214 * target item has changed, notify the action and check
215 * that it is still enabled.
216 */
217 public void dragOver(DropTargetEvent event) {
218 //use newly revealed item as target if scrolling occurs
219 Object target = determineTarget(event);
220
221 //set the location feedback
222 int oldLocation = currentLocation;
223 currentLocation = determineLocation(event);
224 setFeedback(event, currentLocation);
225
226 //see if anything has really changed before doing validation.
227 if (target !is currentTarget || currentLocation !is oldLocation) {
228 currentTarget = target;
229 doDropValidation(event);
230 }
231 }
232
233 /* (non-Javadoc)
234 * Method declared on DropTargetAdapter.
235 * The user has dropped something on the desktop viewer.
236 */
237 public void drop(DropTargetEvent event) {
238 currentLocation = determineLocation(event);
239
240 //perform the drop behavior
241 if (!performDrop(event.data)) {
242 event.detail = DND.DROP_NONE;
243 }
244 currentOperation = event.detail;
245 }
246
247 /* (non-Javadoc)
248 * Method declared on DropTargetAdapter.
249 * Last chance for the action to disable itself
250 */
251 public void dropAccept(DropTargetEvent event) {
252 if (!validateDrop(currentTarget, event.detail, event.currentDataType)) {
253 event.detail = DND.DROP_NONE;
254 }
255 }
256
257 /**
258 * Returns the bounds of the given DWT tree or table item.
259 *
260 * @param item the DWT Item
261 * @return the bounds, or <code>null</code> if it is not a known type of item
262 */
263 protected Rectangle getBounds(Item item) {
264 if ( auto i = cast(TreeItem)item ) {
265 return i.getBounds();
266 }
267 if (auto i = cast(TableItem)item ) {
268 return i.getBounds(0);
269 }
270 return null;
271 }
272
273 /**
274 * Returns a constant describing the position of the mouse relative to the
275 * target (before, on, or after the target.
276 *
277 * @return one of the <code>LOCATION_* </code> constants defined in this type
278 */
279 protected int getCurrentLocation() {
280 return currentLocation;
281 }
282
283 /**
284 * Returns the current operation.
285 *
286 * @return a <code>DROP_*</code> constant from class <code>DND</code>
287 *
288 * @see DND#DROP_COPY
289 * @see DND#DROP_MOVE
290 * @see DND#DROP_LINK
291 * @see DND#DROP_NONE
292 */
293 protected int getCurrentOperation() {
294 return currentOperation;
295 }
296
297 /**
298 * Returns the target object currently under the mouse.
299 *
300 * @return the current target object
301 */
302 protected Object getCurrentTarget() {
303 return currentTarget;
304 }
305
306 /**
307 * Returns whether visible insertion feedback should be presented to the user.
308 * <p>
309 * Typical insertion feedback is the horizontal insertion bars that appear
310 * between adjacent items while dragging.
311 * </p>
312 *
313 * @return <code>true</code> if visual feedback is desired, and <code>false</code> if not
314 */
315 public bool getFeedbackEnabled() {
316 return feedbackEnabled;
317 }
318
319 /**
320 * Returns the object currently selected by the viewer.
321 *
322 * @return the selected object, or <code>null</code> if either no object or
323 * multiple objects are selected
324 */
325 protected Object getSelectedObject() {
326 ISelection selection = viewer.getSelection();
327 if ( null !is cast(IStructuredSelection) selection && !selection.isEmpty()) {
328 IStructuredSelection structured = cast(IStructuredSelection) selection;
329 return structured.getFirstElement();
330 }
331 return null;
332 }
333
334 /**
335 * @return the viewer to which this drop support has been added.
336 */
337 protected Viewer getViewer() {
338 return viewer;
339 }
340
341 /**
342 * @deprecated this method should not be used. Exception handling has been
343 * removed from DropTargetAdapter methods overridden by this class.
344 * Handles any exception that occurs during callback, including
345 * rethrowing behavior.
346 * <p>
347 * [Issue: Implementation prints stack trace and eats exception to avoid
348 * crashing VA/J.
349 * Consider conditionalizing the implementation to do one thing in VAJ
350 * and something more reasonable in other operating environments.
351 * ]
352 * </p>
353 *
354 * @param exception the exception
355 * @param event the event
356 */
357 protected void handleException(Exception exception, DropTargetEvent event) {
358 // Currently we never rethrow because VA/Java crashes if an DWT
359 // callback throws anything. Generally catching Throwable is bad, but in
360 // this cases it's better than hanging the image.
361 ExceptionPrintStackTrace( exception );
362 event.detail = DND.DROP_NONE;
363 }
364
365 /**
366 * Performs any work associated with the drop.
367 * <p>
368 * Subclasses must implement this method to provide drop behavior.
369 * </p>
370 *
371 * @param data the drop data
372 * @return <code>true</code> if the drop was successful, and
373 * <code>false</code> otherwise
374 */
375 public abstract bool performDrop(Object data);
376
377 /* (non-Javadoc)
378 * Method declared on DropTargetAdapter.
379 * The mouse has moved over the drop target. If the
380 * target item has changed, notify the action and check
381 * that it is still enabled.
382 */
383 private void setFeedback(DropTargetEvent event, int location) {
384 if (feedbackEnabled) {
385 switch (location) {
386 case LOCATION_BEFORE:
387 event.feedback = DND.FEEDBACK_INSERT_BEFORE;
388 break;
389 case LOCATION_AFTER:
390 event.feedback = DND.FEEDBACK_INSERT_AFTER;
391 break;
392 case LOCATION_ON:
393 default:
394 event.feedback = DND.FEEDBACK_SELECT;
395 break;
396 }
397 }
398
399 // Explicitly inhibit SELECT feedback if desired
400 if (!selectFeedbackEnabled) {
401 event.feedback &= ~DND.FEEDBACK_SELECT;
402 }
403
404 if (scrollExpandEnabled) {
405 event.feedback |= DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL;
406 }
407 }
408
409 /**
410 * Sets whether visible insertion feedback should be presented to the user.
411 * <p>
412 * Typical insertion feedback is the horizontal insertion bars that appear
413 * between adjacent items while dragging.
414 * </p>
415 *
416 * @param value
417 * <code>true</code> if visual feedback is desired, and
418 * <code>false</code> if not
419 */
420 public void setFeedbackEnabled(bool value) {
421 feedbackEnabled = value;
422 }
423
424 /**
425 * Sets whether selection feedback should be provided during dragging.
426 *
427 * @param value <code>true</code> if selection feedback is desired, and
428 * <code>false</code> if not
429 *
430 * @since 3.2
431 */
432 public void setSelectionFeedbackEnabled(bool value) {
433 selectFeedbackEnabled = value;
434 }
435
436 /**
437 * Sets whether auto scrolling and expanding should be provided during dragging.
438 *
439 * @param value <code>true</code> if scrolling and expanding is desired, and
440 * <code>false</code> if not
441 * @since 2.0
442 */
443 public void setScrollExpandEnabled(bool value) {
444 scrollExpandEnabled = value;
445 }
446
447 /**
448 * Validates dropping on the given object. This method is called whenever some
449 * aspect of the drop operation changes.
450 * <p>
451 * Subclasses must implement this method to define which drops make sense.
452 * </p>
453 *
454 * @param target the object that the mouse is currently hovering over, or
455 * <code>null</code> if the mouse is hovering over empty space
456 * @param operation the current drag operation (copy, move, etc.)
457 * @param transferType the current transfer type
458 * @return <code>true</code> if the drop is valid, and <code>false</code>
459 * otherwise
460 */
461 public abstract bool validateDrop(Object target, int operation,
462 TransferData transferType);
463 }