Spec-Zone .ru
спецификации, руководства, описания, API
001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.  Oracle designates this
008 * particular file as subject to the "Classpath" exception as provided
009 * by Oracle in the LICENSE file that accompanied this code.
010 *
011 * This code is distributed in the hope that it will be useful, but WITHOUT
012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
014 * version 2 for more details (a copy is included in the LICENSE file that
015 * accompanied this code).
016 *
017 * You should have received a copy of the GNU General Public License version
018 * 2 along with this work; if not, write to the Free Software Foundation,
019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020 *
021 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
022 * or visit www.oracle.com if you need additional information or have any
023 * questions.
024 */
025
026package javafx.scene.input;
027
028import java.util.EnumSet;
029import java.util.Set;
030
031import javafx.event.Event;
032import javafx.event.EventTarget;
033import javafx.event.EventType;
034import javafx.geometry.Point3D;
035
036import com.sun.javafx.scene.input.InputEventUtils;
037import java.io.IOException;
038
039// PENDING_DOC_REVIEW
040/**
041 * Drag events replace mouse events during drag-and-drop gesture.
042 * The difference between press-drag-release and drag-and-drop gestures
043 * is described at {@link javafx.scene.input.MouseEvent MouseEvent}.
044 * <p>
045 * Drag and drop gesture can be started by calling {@code startDragAndDrop()}
046 * (on a node or scene) inside of a {@link MouseEvent#DRAG_DETECTED DRAG_DETECTED} event handler.
047 * The data to be transfered to drop target are placed to a {@code dragBoard}
048 * at this moment.
049 * <p>
050 * Drag entered/exited events behave similarly to mouse entered/exited
051 * events, please see {@code MouseEvent} overview.
052 * <p>
053 *
054 * <br><h4>Drag sources: initiating a drag and drop gesture</h4>
055 *
056 * When a drag gesture is detected, an application can decide whether to
057 * start a drag and drop gesture or continue with a press-drag-release gesture.
058 * <p>
059 * The default drag detection mechanism uses mouse movements with a pressed
060 * button in combination with hysteresis. This behavior can be
061 * augmented by the application. Each {@code MOUSE_PRESSED} and
062 * {@code MOUSE_DRAGGED} event has a {@code dragDetect} flag that determines
063 * whether a drag gesture has been detected. The default value of this flag
064 * depends on the default detection mechanism and can be modified by calling
065 * {@code setDragDetect()} inside of an event handler. When processing of
066 * one of these events ends with the {@code dragDetect} flag set to true,
067 * a {@code DRAG_DETECTED} {@code MouseEvent} is sent to the potential gesture
068 * source (the object on which a mouse button has been pressed). This event
069 * notifies about the gesture detection.
070 * <p>
071 * Inside a {@code DRAG_DETECTED} event handler, if the
072 * {@code startDragAndDrop()} method is called on a node or scene and a dragged
073 * data is made available to the returned {@code Dragboard}, the object on which
074 * {@code startDragAndDrop()} has been called is considred a gesture source
075 * and the drag and drop gesture is started. The {@code Dragboard} has system
076 * clipboard functionality but is specifically used for drag and drop data
077 * transfer.
078 * <p>
079 * The {@code startDragAndDrop()} method takes a set of {@code TransferMode}s
080 * supported by the gesture source. For instance passing only
081 * {@code TransferMode.COPY} indicates that the gesture source allows only
082 * copying of the data, not moving or referencing.
083 * <p>
084 * Following example shows a simple drag and drop source:
085 * <code><pre>
086Rectangle rect = new Rectangle(100, 100);
087rect.setOnDragDetected(new EventHandler<MouseEvent>() {
088    &#64;Override public void handle(MouseEvent event) {
089        Dragboard db = startDragAndDrop(TransferMode.ANY);
090        ClipboardContent content = new ClipboardContent();
091        content.putString("Hello!");
092        db.setContent(content);
093        event.consume();
094    }
095});
096 * </pre></code>
097 *
098 * <br><h4>Potential drop targets</h4>
099 *
100 * <p>
101 * After the drag and drop gesture has been started, any object
102 * ({@code Node}, {@code Scene}) over which the mouse is dragged is
103 * a potential drop target.
104 * <p>
105 * When the mouse is dragged into the boundaries of potential drop target,
106 * the potential target gets a {@code DRAG_ENTERED} event. When the mouse is
107 * dragged outside of the potential target's bounds, it gets a
108 * {@code DRAG_EXITED} event. There are also the bubbling
109 * {@code DRAG_ENTERED_TARGET} and {@code DRAG_EXITED_TARGET} variants. They
110 * behave similarly to mouse entered/exited events, please see
111 * {@code MouseEvent} overview.
112 * <p>
113 * A potential drop target can decide to change its appearance to
114 * let the user know that the dragged data can be dropped on it. This can be
115 * done in a {@code DRAG_OVER} event handler, based on the position of the
116 * mouse. Another option is to change the potential target's appearance in
117 * a {@code DRAG_ENTERED} and {@code DRAG_EXITED} handlers.
118 * <p>
119 * In {@code DRAG_OVER} event handler a potential drop target has the ability
120 * to make it known that it is an actual target. This is done by calling
121 * {@code acceptTransferModes(TransferMode...)} on the event,
122 * passing transfer modes it is willing to accept.
123 * If it <i>is not called</i> during the event delivery or if none of the
124 * passed transfer modes is supported by gesture source, then the potential
125 * drop target <i>is not considered to be an actual drop target</i>.
126 * <p>
127 * When deciding weather to accept the event by calling {@code acceptTransferModes(TransferMode...)},
128 * the type of data available on the {@code Dragboard} should be considered.
129 * Access to the {@code Dragboard} is provided by the {@code getDragboard()}
130 * method.
131 * <p>
132 * When accepting an event, the potential gesture target decides which
133 * {@code TransferMode} is accepted for the operation. To make the decision,
134 * {@code DragBoard.getTransferModes()} (set of transfer modes supported by
135 * the gesture source) and {@code DragEvent.getTransferMode()} (default
136 * transfer mode issued by platform, driven by key modifiers) can be used.
137 * It is poosible to pass more transfer modes into the
138 * {@code acceptTransferModes(TransferMode...)} method. In this case
139 * it makes the decision in behalf of the
140 * application (it chooses the default mode if it's supported by gesture source
141 * and accepted by gesture target, otherwise it chooses the most common mode
142 * of the supported and accepted ones).
143 * The {@code DRAG_DROPPED} event's {@code getTransferMode()} later reports the
144 * transfer mode accepted by the {@code DRAG_OVER} event handler.
145 * <p>
146 * A drag and drop gesture ends when the mouse button is released.
147 * If this happens over a gesture target that accepted previous {@code DRAG_OVER}
148 * events with a transfer mode supported by gesture source,
149 * a {@code DRAG_DROPPED} event is sent to the gesture target.
150 * In its handler, the gesture target can access the data on the dragboard.
151 * After data has been transferred (or decided not to transfer), the gesture
152 * needs to be completed by calling {@code setDropCompleted(Boolean)} on the event.
153 * The {@code Boolean} argument indicates if the data has been transferred
154 * successfully or not. If it is not called, the gesture is considered
155 * unsuccessful.
156 *
157 * <p>
158 * Following example shows a simple drag and drop target for text data:
159 * <code><pre>
160Rectangle rect = new Rectangle(100, 100);
161
162rect.setOnDragOver(new EventHandler<DragEvent>() {
163    &#64;Override public void handle(DragEvent event) {
164        Dragboard db = event.getDragboard();
165        if (db.hasString()) {
166            event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
167        }
168        event.consume();
169    }
170});
171
172rect.setOnDragDropped(new EventHandler<DragEvent>() {
173    &#64;Override public void handle(DragEvent event) {
174        Dragboard db = event.getDragboard();
175        boolean success = false;
176        if (db.hasString()) {
177            System.out.println("Dropped: " + db.getString());
178            success = true;
179        }
180        event.setDropCompleted(success);
181        event.consume();
182    }
183});
184 * </pre></code>
185 *
186 * <br><h4>Drag sources: finalizing drag and drop gesture</h4>
187 *
188 * <p>
189 * After the gesture has been finished, whether by successful or unsuccessful
190 * data transfer or being canceled, the {@code DRAG_DONE} event is sent to 
191 * the gesture source. The {@code getTransferMode()} method of the event
192 * indicates to the gesture source how the transfer of data was completed.
193 * If the transfer mode has the value {@code MOVE}, then this allows the source
194 * to clear out its data. Clearing the source's data gives the appropriate
195 * appearance to a user that the data has been moved by the drag and drop 
196 * gesture. If it has the value {@code null}, then the drag and drop gesture
197 * ended without any data being transferred.  This could happen as a result of
198 * a mouse release event over a node that is not a drop target, or the user
199 * pressing the ESC key to cancel the drag and drop gesture, or by
200 * the gesture target reporting an unsuccessful data transfer.
201 * </p>
202 */
203public final class DragEvent extends InputEvent {
204
205    private static final long serialVersionUID = 20121107L;
206    
207    /**
208     * Common supertype for all drag event types.
209     */
210    public static final EventType<DragEvent> ANY =
211            new EventType<DragEvent>(InputEvent.ANY, "DRAG");
212
213    /**
214     * This event occurs when drag gesture enters a node. It's the
215     * bubbling variant, which is delivered also to all parents of the
216     * entered node (unless it was consumed). When notifications about
217     * entering some of node's children are not desired,
218     * {@code DRAG_ENTERED} event handler should be used.
219     *
220     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
221     * which is similar
222     */
223    public static final EventType<DragEvent> DRAG_ENTERED_TARGET =
224            new EventType<DragEvent>(DragEvent.ANY, "DRAG_ENTERED_TARGET");
225
226    /**
227     * This event occurs when drag gesture enters a node.
228     * This event type is delivered only to the entered node,
229     * if parents want to filter it or get the bubbling event,
230     * they need to use {@code DRAG_ENTERED_TARGET}.
231     *
232     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
233     * which is similar
234     */
235    public static final EventType<DragEvent> DRAG_ENTERED =
236            new EventType<DragEvent>(DragEvent.DRAG_ENTERED_TARGET, "DRAG_ENTERED");
237
238    /**
239     * This event occurs when drag gesture exits a node. It's the
240     * bubbling variant, which is delivered also to all parents of the
241     * eixited node (unless it was consumed). When notifications about
242     * exiting some of node's children are not desired,
243     * {@code DRAG_EXITED} event handler should be used.
244     *
245     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
246     * which is similar
247     */
248    public static final EventType<DragEvent> DRAG_EXITED_TARGET =
249            new EventType<DragEvent>(DragEvent.ANY, "DRAG_EXITED_TARGET");
250
251    /**
252     * This event occurs when drag gesture exits a node.
253     * This event type is delivered only to the exited node,
254     * if parents want to filter it or get the bubbling event,
255     * they need to use {@code DRAG_EXITED_TARGET}.
256     *
257     * @see MouseEvent MouseEvent for more information about mouse entered/exited handling
258     * which is similar
259     */
260    public static final EventType<DragEvent> DRAG_EXITED =
261            new EventType<DragEvent>(DragEvent.DRAG_EXITED_TARGET, "DRAG_EXITED");
262
263    /**
264     * This event occurs when drag gesture progresses within this node.
265     */
266    public static final EventType<DragEvent> DRAG_OVER =
267            new EventType<DragEvent>(DragEvent.ANY, "DRAG_OVER");
268
269    // Do we want DRAG_TRANSFER_MODE_CHANGED event?
270//    /**
271//     * This event occurs on a potential drag-and-drop target when the user
272//     * takes action to change the intended {@code TransferMode}.
273//     * The user can change the intended {@link TransferMode} by holding down
274//     * or releasing key modifiers.
275//     */
276//    public static final EventType<DragEvent> DRAG_TRANSFER_MODE_CHANGED =
277//            new EventType<DragEvent>(DragEvent.ANY, "DRAG_TRANSFER_MODE_CHANGED");
278
279    /**
280     * This event occurs when the mouse button is released during drag and drop
281     * gesture on a drop target. Transfer of data from the
282     * {@link DragEvent}'s {@link DragEvent#dragboard dragboard} should happen
283     * in handler of this event.
284     */
285    public static final EventType<DragEvent> DRAG_DROPPED =
286            new EventType<DragEvent>(DragEvent.ANY, "DRAG_DROPPED");
287
288    /**
289     * This event occurs on drag-and-drop gesture source after its data has
290     * been dropped on a drop target. The {@code transferMode} of the
291     * event shows what just happened at the drop target.
292     * If {@code transferMode} has the value {@code MOVE}, then the source can
293     * clear out its data. Clearing the source's data gives the appropriate
294     * appearance to a user that the data has been moved by the drag and drop
295     * gesture. A {@code transferMode} that has the value {@code NONE}
296     * indicates that no data was transferred during the drag and drop gesture.
297     */
298    public static final EventType<DragEvent> DRAG_DONE =
299            new EventType<DragEvent>(DragEvent.ANY, "DRAG_DONE");
300
301    /**
302     * Creates a copy of the given drag event with the given fields substituted.
303     * @param source the new source of the copied event
304     * @param target the new target of the copied event
305     * @param gestureSource the new gesture source.
306     * @param gestureTarget the new gesture target.
307     * @param eventType the new eventType
308     * @return the event copy with the fields
309     */
310    public DragEvent copyFor(Object source, EventTarget target,
311            Object gestureSource, Object gestureTarget,
312            EventType<DragEvent> eventType) {
313
314        DragEvent copyEvent = copyFor(source, target, eventType);
315        recomputeCoordinatesToSource(copyEvent, source);
316        copyEvent.gestureSource = gestureSource;
317        copyEvent.gestureTarget = gestureTarget;
318        return copyEvent;
319    }
320
321    /**
322     * Constructs new DragEvent event.
323     * For DRAG_DROPPED and DRAG_DONE event types, the {@code accepted} state
324     * and {@code acceptedTransferMode} are set according to the passed
325     * {@code transferMode}.
326     * @param source the source of the event. Can be null.
327     * @param target the target of the event. Can be null.
328     * @param eventType The type of the event.
329     * @param dragboard the dragboard of the event.
330     * @param x The x with respect to the scene.
331     * @param y The y with respect to the scene.
332     * @param screenX The x coordinate relative to screen.
333     * @param screenY The y coordinate relative to screen.
334     * @param transferMode the transfer mode of the event.
335     * @param gestureSource the source of the DnD gesture of the event.
336     * @param gestureTarget the target of the DnD gesture of the event.
337     * @param pickResult pick result. Can be null, in this case a 2D pick result
338     *                   without any further values is constructed
339     *                   based on the scene coordinates and the target
340     */
341    public DragEvent(Object source, EventTarget target, EventType<DragEvent> eventType, Dragboard dragboard,
342            double x, double y,
343            double screenX, double screenY, TransferMode transferMode,
344            Object gestureSource, Object gestureTarget, PickResult pickResult) {
345        super(source, target, eventType);
346        this.gestureSource = gestureSource;
347        this.gestureTarget = gestureTarget;
348        this.x = x;
349        this.y = y;
350        this.screenX = screenX;
351        this.screenY = screenY;
352        this.sceneX = x;
353        this.sceneY = y;
354        this.transferMode = transferMode;
355        this.dragboard = dragboard;
356
357        if (eventType == DragEvent.DRAG_DROPPED
358                || eventType == DragEvent.DRAG_DONE) {
359            state.accepted = transferMode != null;
360            state.acceptedTrasferMode = transferMode;
361        }
362
363        this.pickResult = pickResult != null ? pickResult : new PickResult(
364                eventType == DRAG_DONE ? null : target, x, y);
365        final Point3D p = InputEventUtils.recomputeCoordinates(this.pickResult, null);
366        this.x = p.getX();
367        this.y = p.getY();
368        this.z = p.getZ();
369    }
370
371    /**
372     * Constructs new DragEvent event with empty source and target.
373     * @param eventType The type of the event.
374     * @param dragboard the dragboard of the event.
375     * @param x The x with respect to the scene.
376     * @param y The y with respect to the scene.
377     * @param screenX The x coordinate relative to screen.
378     * @param screenY The y coordinate relative to screen.
379     * @param transferMode the transfer mode of the event.
380     * @param gestureSource the source of the DnD gesture of the event.
381     * @param gestureTarget the target of the DnD gesture of the event.
382     * @param pickResult pick result. Can be null, in this case a 2D pick result
383     *                   without any further values is constructed
384     *                   based on the scene coordinates
385     */
386    public DragEvent(EventType<DragEvent> eventType, Dragboard dragboard,
387            double x, double y,
388            double screenX, double screenY, TransferMode transferMode,
389            Object gestureSource, Object gestureTarget, PickResult pickResult) {
390        this(null, null, eventType, dragboard, x, y, screenX, screenY, transferMode,
391                gestureSource, gestureTarget, pickResult);
392    }
393
394    /**
395     * Fills the given event by this event's coordinates recomputed to the given
396     * source object
397     * @param newEvent Event whose coordinates are to be filled
398     * @param newSource Source object to compute coordinates for
399     */
400    private void recomputeCoordinatesToSource(DragEvent newEvent, Object newSource) {
401
402        if (newEvent.getEventType() == DRAG_DONE) {
403            // DRAG_DONE contains all zeros, doesn't make sense to recompute it
404            return;
405        }
406
407        final Point3D newCoordinates = InputEventUtils.recomputeCoordinates(
408                pickResult, newSource);
409
410        newEvent.x = newCoordinates.getX();
411        newEvent.y = newCoordinates.getY();
412        newEvent.z = newCoordinates.getZ();
413    }
414    
415    @Override
416    public DragEvent copyFor(Object newSource, EventTarget newTarget) {
417        DragEvent e = (DragEvent) super.copyFor(newSource, newTarget);
418        recomputeCoordinatesToSource(e, newSource);
419        return e;
420    }
421
422    /**
423     * Creates a copy of the given drag event with the given fields substituted.
424     * @param source the new source of the copied event
425     * @param target the new target of the copied event
426     * @param eventType the new eventType
427     * @return the event copy with the fields
428     */
429    public DragEvent copyFor(Object source, EventTarget target, EventType<DragEvent> type) {
430        DragEvent e = (DragEvent) copyFor(source, target);
431        e.eventType = type;
432        return e;
433    }
434
435    @Override
436    public EventType<DragEvent> getEventType() {
437        return (EventType<DragEvent>) super.getEventType();
438    }
439
440    /**
441     * Horizontal x position of the event relative to the
442     * origin of the MouseEvent's node.
443     */
444    private transient double x;
445
446    /**
447     * Horizontal position of the event relative to the
448     * origin of the DragEvent's source.
449     * 
450     * @return horizontal position of the event relative to the
451     * origin of the DragEvent's source.
452     */
453    public final double getX() {
454        return x;
455    }
456
457    /**
458     * Vertical y position of the event relative to the
459     * origin of the MouseEvent's node.
460     */
461    private transient double y;
462
463    /**
464     * Vertical position of the event relative to the
465     * origin of the DragEvent's source.
466     * 
467     * @return vertical position of the event relative to the
468     * origin of the DragEvent's source.
469     */
470    public final double getY() {
471        return y;
472    }
473
474    /**
475     * Depth z position of the event relative to the
476     * origin of the MouseEvent's node.
477     */
478    private transient double z;
479
480    /**
481     * Depth position of the event relative to the
482     * origin of the MouseEvent's source.
483     *
484     * @return depth position of the event relative to the
485     * origin of the MouseEvent's source.
486     */
487    public final double getZ() {
488        return z;
489    }
490
491    /**
492     * Absolute horizontal x position of the event.
493     */
494    private final double screenX;
495
496    /**
497     * Returns absolute horizontal position of the event.
498     * @return absolute horizontal position of the event
499     */
500    public final double getScreenX() {
501        return screenX;
502    }
503
504    /**
505     * Absolute vertical y position of the event.
506     */
507    private final double screenY;
508
509    /**
510     * Returns absolute vertical position of the event.
511     * @return absolute vertical position of the event
512     */
513    public final double getScreenY() {
514        return screenY;
515    }
516
517    /**
518     * Horizontal x position of the event relative to the
519     * origin of the {@code Scene} that contains the DragEvent's node.
520     * If the node is not in a {@code Scene}, then the value is relative to
521     * the boundsInParent of the root-most parent of the DragEvent's node.
522     */
523    private final double sceneX;
524
525    /**
526     * Returns horizontal position of the event relative to the
527     * origin of the {@code Scene} that contains the DragEvent's source.
528     * If the node is not in a {@code Scene}, then the value is relative to
529     * the boundsInParent of the root-most parent of the DragEvent's node.
530     * Note that in 3D scene, this represents the flat coordinates after
531     * applying the projection transformations.
532     * 
533     * @return horizontal position of the event relative to the
534     * origin of the {@code Scene} that contains the DragEvent's source
535     */
536    public final double getSceneX() {
537        return sceneX;
538    }
539
540    /**
541     * Vertical y position of the event relative to the
542     * origin of the {@code Scene} that contains the DragEvent's node.
543     * If the node is not in a {@code Scene}, then the value is relative to
544     * the boundsInParent of the root-most parent of the DragEvent's node.
545     */
546    private final double sceneY;
547
548    /**
549     * Returns vertical position of the event relative to the
550     * origin of the {@code Scene} that contains the DragEvent's source.
551     * If the node is not in a {@code Scene}, then the value is relative to
552     * the boundsInParent of the root-most parent of the DragEvent's node.
553     * Note that in 3D scene, this represents the flat coordinates after
554     * applying the projection transformations.
555     * 
556     * @return vertical position of the event relative to the
557     * origin of the {@code Scene} that contains the DragEvent's source
558     */
559    public final double getSceneY() {
560        return sceneY;
561    }
562
563    /**
564     * Information about the pick if the picked {@code Node} is a
565     * {@code Shape3D} node and its pickOnBounds is false.
566     */
567    private PickResult pickResult;
568
569    /**
570     * Returns information about the pick.
571     *
572     * @return new PickResult object that contains information about the pick
573     */
574    public final PickResult getPickResult() {
575        return pickResult;
576    }
577
578    /**
579     * The source object of the drag and drop gesture.
580     * Gesture source is the object that started drag and drop operation.
581     * The value {@code null} is valid in the case that the gesture comes
582     * from another application.
583     */
584    public final Object getGestureSource() { return gestureSource; }
585    private Object gestureSource;
586
587    /**
588     * The target object of the drag and drop gesture.
589     * Gesture target is the object that accepts drag events.
590     * The value {@code null} is valid in the case that the drag and drop
591     * gesture has been canceled or completed without a transfer taking place
592     * or there is currently no event target accepting the drag events.
593     */
594    public final Object getGestureTarget() { return gestureTarget; }
595    private Object gestureTarget;
596
597    /**
598     * Data transfer mode. Before the data transfer is is performed,
599     * this is the default transfer mode set by system according to
600     * input events such as the user holding some modifiers.
601     * In time of data transfer (in DRAG_DROPPED event) it determines
602     * the transfer mode accepted by previous DRAG_OVER handler.
603     * After the data transfer (in DRAG_DONE event)
604     * it determines the actual mode of the transfer done.
605     */
606    public final TransferMode getTransferMode() { return transferMode; }
607    private TransferMode transferMode;
608
609    private final State state = new State();
610
611    /**
612     * Indicates if this event has been accepted.
613     * @see #acceptTransferModes
614     * @defaultValue false
615     */
616    public final boolean isAccepted() { return state.accepted; }
617
618    /**
619     * Gets transfer mode accepted by potential target.
620     * @return transfer mode accepted by potential target.
621     */
622    public final TransferMode getAcceptedTransferMode() {
623        return state.acceptedTrasferMode;
624    }
625
626    /**
627     * The object that accepted the drag.
628     * @return the object that accepted the drag.
629     */
630    public final Object getAcceptingObject() {
631        return state.acceptingObject;
632    }
633
634    /**
635     * A dragboard that is available to transfer data.
636     * Data can be placed onto this dragboard in handler of the
637     * {@code DRAG_DETECTED} mouse event. Data can be copied from this
638     * dragboard in handler of the {@code DRAG_DROPPED} event.
639     */
640    public final Dragboard getDragboard() {
641        return dragboard;
642    }
643    private transient Dragboard dragboard;
644
645    /**
646     * Chooses a transfer mode for the operation
647     * @param supported Transfer modes supported by gesture source
648     * @param accepted Transfer modes accepted by gesture
649     * @param proposed Transfer mode proposed by platform
650     * @return The chosen transfer mode, null if none would work
651     */
652    private static TransferMode chooseTransferMode(Set<TransferMode> supported,
653            TransferMode[] accepted, TransferMode proposed) {
654
655        TransferMode result = null;
656        Set<TransferMode> intersect = EnumSet.noneOf(TransferMode.class);
657
658        for (TransferMode tm : InputEventUtils.safeTransferModes(accepted)) {
659            if (supported.contains(tm)) {
660                intersect.add(tm);
661            }
662        }
663
664        if (intersect.contains(proposed)) {
665            result = proposed;
666        } else {
667            if (intersect.contains(TransferMode.MOVE)) {
668                result = TransferMode.MOVE;
669            } else if (intersect.contains(TransferMode.COPY)) {
670                result = TransferMode.COPY;
671            } else if (intersect.contains(TransferMode.LINK)) {
672                result = TransferMode.LINK;
673            }
674        }
675
676        return result;
677    }
678
679    /**
680     * Accepts this {@code DragEvent}, choosing the transfer mode for the
681     * drop operation.
682     * Used to indicate that the potential drop target
683     * that receives this event is a drop target from {@code DRAG_OVER}
684     * event handler.
685     * <p>
686     * It accepts one of the transfer modes that are both passed into this
687     * method and supported by the gesture source. It accepts the default
688     * transfer mode if possible, otherwise the most common one of the
689     * acceptable modes.
690     */
691    public void acceptTransferModes(TransferMode... transferModes) {
692
693        if (dragboard == null || dragboard.getTransferModes() == null ||
694                transferMode == null) {
695            state.accepted = false;
696            return;
697        }
698
699        TransferMode tm = chooseTransferMode(dragboard.getTransferModes(),
700                transferModes, transferMode);
701
702        if (tm == null && getEventType() == DRAG_DROPPED) {
703            throw new IllegalStateException("Accepting unsupported transfer "
704                    + "modes inside DRAG_DROPPED handler");
705        }
706
707        state.accepted = tm != null;
708        state.acceptedTrasferMode = tm;
709        state.acceptingObject = state.accepted ? source : null;
710    }
711
712    /**
713     * Indicates that transfer handling of this {@code DragEvent} was completed
714     * successfully during a {@code DRAG_DROPPED} event handler.
715     * No {@link #dragboard} access can happen after this call.
716     *
717     * @param isTransferDone {@code true} indicates that the transfer was successful.
718     * @throws IllegalStateException if this is not a DRAG_DROPPED event
719     */
720    public void setDropCompleted(boolean isTransferDone) {
721        if (getEventType() != DRAG_DROPPED) {
722            throw new IllegalStateException("setDropCompleted can be called " +
723                    "only from DRAG_DROPPED handler");
724        }
725
726        state.dropCompleted = isTransferDone;
727    }
728
729    /**
730     * Whether {@code setDropCompleted(true)} has been called on this event.
731     * @return true if {@code setDropCompleted(true)} has been called
732     */
733    public boolean isDropCompleted() {
734        return state.dropCompleted;
735    }
736
737    private void readObject(java.io.ObjectInputStream in)
738            throws IOException, ClassNotFoundException {
739        in.defaultReadObject();
740        x = sceneX;
741        y = sceneY;
742    }
743
744    /**
745     * These properties need to live in a separate object shared among all the
746     * copied events to make sure that the values are propagated to the
747     * original event.
748     */
749    private static class State {
750        /**
751         * Whether this event has been accepted.
752         */
753        boolean accepted = false;
754
755        /**
756         * Whether drop completed successfully.
757         */
758        boolean dropCompleted = false;
759
760        /**
761         * Transfer mode accepted by the potential gesture target.
762         */
763        TransferMode acceptedTrasferMode = null;
764
765        /**
766         * Object that accepted this event.
767         */
768        Object acceptingObject = null;
769    }
770
771}