1 /*
2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.awt.dnd;
27 
28 import java.awt.Component;
29 import java.awt.Cursor;
30 import java.awt.GraphicsEnvironment;
31 import java.awt.HeadlessException;
32 import java.awt.Image;
33 import java.awt.Point;
34 import java.awt.Toolkit;
35 import java.awt.datatransfer.FlavorMap;
36 import java.awt.datatransfer.SystemFlavorMap;
37 import java.awt.datatransfer.Transferable;
38 import java.io.IOException;
39 import java.io.ObjectInputStream;
40 import java.io.ObjectOutputStream;
41 import java.io.Serializable;
42 import java.security.AccessController;
43 import java.util.EventListener;
44 
45 import sun.awt.AWTAccessor;
46 import sun.awt.AWTAccessor.DragSourceContextAccessor;
47 import sun.awt.dnd.SunDragSourceContextPeer;
48 import sun.security.action.GetIntegerAction;
49 
50 
51 /**
52  * The {@code DragSource} is the entity responsible
53  * for the initiation of the Drag
54  * and Drop operation, and may be used in a number of scenarios:
55  * <UL>
56  * <LI>1 default instance per JVM for the lifetime of that JVM.
57  * <LI>1 instance per class of potential Drag Initiator object (e.g
58  * TextField). [implementation dependent]
59  * <LI>1 per instance of a particular
60  * {@code Component}, or application specific
61  * object associated with a {@code Component}
62  * instance in the GUI. [implementation dependent]
63  * <LI>Some other arbitrary association. [implementation dependent]
64  *</UL>
65  *
66  * Once the {@code DragSource} is
67  * obtained, a {@code DragGestureRecognizer} should
68  * also be obtained to associate the {@code DragSource}
69  * with a particular
70  * {@code Component}.
71  * <P>
72  * The initial interpretation of the user's gesture,
73  * and the subsequent starting of the drag operation
74  * are the responsibility of the implementing
75  * {@code Component}, which is usually
76  * implemented by a {@code DragGestureRecognizer}.
77  *<P>
78  * When a drag gesture occurs, the
79  * {@code DragSource}'s
80  * startDrag() method shall be
81  * invoked in order to cause processing
82  * of the user's navigational
83  * gestures and delivery of Drag and Drop
84  * protocol notifications. A
85  * {@code DragSource} shall only
86  * permit a single Drag and Drop operation to be
87  * current at any one time, and shall
88  * reject any further startDrag() requests
89  * by throwing an {@code IllegalDnDOperationException}
90  * until such time as the extant operation is complete.
91  * <P>
92  * The startDrag() method invokes the
93  * createDragSourceContext() method to
94  * instantiate an appropriate
95  * {@code DragSourceContext}
96  * and associate the {@code DragSourceContextPeer}
97  * with that.
98  * <P>
99  * If the Drag and Drop System is
100  * unable to initiate a drag operation for
101  * some reason, the startDrag() method throws
102  * a {@code java.awt.dnd.InvalidDnDOperationException}
103  * to signal such a condition. Typically this
104  * exception is thrown when the underlying platform
105  * system is either not in a state to
106  * initiate a drag, or the parameters specified are invalid.
107  * <P>
108  * Note that during the drag, the
109  * set of operations exposed by the source
110  * at the start of the drag operation may not change
111  * until the operation is complete.
112  * The operation(s) are constant for the
113  * duration of the operation with respect to the
114  * {@code DragSource}.
115  *
116  * @since 1.2
117  */
118 
119 public class DragSource implements Serializable {
120 
121     private static final long serialVersionUID = 6236096958971414066L;
122 
123     /*
124      * load a system default cursor
125      */
126 
load(String name)127     private static Cursor load(String name) {
128         if (GraphicsEnvironment.isHeadless()) {
129             return null;
130         }
131 
132         try {
133             return (Cursor)Toolkit.getDefaultToolkit().getDesktopProperty(name);
134         } catch (Exception e) {
135             e.printStackTrace();
136 
137             throw new RuntimeException("failed to load system cursor: " + name + " : " + e.getMessage());
138         }
139     }
140 
141 
142     /**
143      * The default {@code Cursor} to use with a copy operation indicating
144      * that a drop is currently allowed. {@code null} if
145      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
146      *
147      * @see java.awt.GraphicsEnvironment#isHeadless
148      */
149     public static final Cursor DefaultCopyDrop =
150         load("DnD.Cursor.CopyDrop");
151 
152     /**
153      * The default {@code Cursor} to use with a move operation indicating
154      * that a drop is currently allowed. {@code null} if
155      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
156      *
157      * @see java.awt.GraphicsEnvironment#isHeadless
158      */
159     public static final Cursor DefaultMoveDrop =
160         load("DnD.Cursor.MoveDrop");
161 
162     /**
163      * The default {@code Cursor} to use with a link operation indicating
164      * that a drop is currently allowed. {@code null} if
165      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
166      *
167      * @see java.awt.GraphicsEnvironment#isHeadless
168      */
169     public static final Cursor DefaultLinkDrop =
170         load("DnD.Cursor.LinkDrop");
171 
172     /**
173      * The default {@code Cursor} to use with a copy operation indicating
174      * that a drop is currently not allowed. {@code null} if
175      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
176      *
177      * @see java.awt.GraphicsEnvironment#isHeadless
178      */
179     public static final Cursor DefaultCopyNoDrop =
180         load("DnD.Cursor.CopyNoDrop");
181 
182     /**
183      * The default {@code Cursor} to use with a move operation indicating
184      * that a drop is currently not allowed. {@code null} if
185      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
186      *
187      * @see java.awt.GraphicsEnvironment#isHeadless
188      */
189     public static final Cursor DefaultMoveNoDrop =
190         load("DnD.Cursor.MoveNoDrop");
191 
192     /**
193      * The default {@code Cursor} to use with a link operation indicating
194      * that a drop is currently not allowed. {@code null} if
195      * {@code GraphicsEnvironment.isHeadless()} returns {@code true}.
196      *
197      * @see java.awt.GraphicsEnvironment#isHeadless
198      */
199     public static final Cursor DefaultLinkNoDrop =
200         load("DnD.Cursor.LinkNoDrop");
201 
202     private static final DragSource dflt =
203         (GraphicsEnvironment.isHeadless()) ? null : new DragSource();
204 
205     /**
206      * Internal constants for serialization.
207      */
208     static final String dragSourceListenerK = "dragSourceL";
209     static final String dragSourceMotionListenerK = "dragSourceMotionL";
210 
211     /**
212      * Gets the {@code DragSource} object associated with
213      * the underlying platform.
214      *
215      * @return the platform DragSource
216      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
217      *            returns true
218      * @see java.awt.GraphicsEnvironment#isHeadless
219      */
getDefaultDragSource()220     public static DragSource getDefaultDragSource() {
221         if (GraphicsEnvironment.isHeadless()) {
222             throw new HeadlessException();
223         } else {
224             return dflt;
225         }
226     }
227 
228     /**
229      * Reports
230      * whether or not drag
231      * {@code Image} support
232      * is available on the underlying platform.
233      *
234      * @return if the Drag Image support is available on this platform
235      */
236 
isDragImageSupported()237     public static boolean isDragImageSupported() {
238         Toolkit t = Toolkit.getDefaultToolkit();
239 
240         Boolean supported;
241 
242         try {
243             supported = (Boolean)Toolkit.getDefaultToolkit().getDesktopProperty("DnD.isDragImageSupported");
244 
245             return supported.booleanValue();
246         } catch (Exception e) {
247             return false;
248         }
249     }
250 
251     /**
252      * Creates a new {@code DragSource}.
253      *
254      * @exception HeadlessException if GraphicsEnvironment.isHeadless()
255      *            returns true
256      * @see java.awt.GraphicsEnvironment#isHeadless
257      */
DragSource()258     public DragSource() throws HeadlessException {
259         if (GraphicsEnvironment.isHeadless()) {
260             throw new HeadlessException();
261         }
262     }
263 
264     /**
265      * Start a drag, given the {@code DragGestureEvent}
266      * that initiated the drag, the initial
267      * {@code Cursor} to use,
268      * the {@code Image} to drag,
269      * the offset of the {@code Image} origin
270      * from the hotspot of the {@code Cursor} at
271      * the instant of the trigger,
272      * the {@code Transferable} subject data
273      * of the drag, the {@code DragSourceListener},
274      * and the {@code FlavorMap}.
275      *
276      * @param trigger        the {@code DragGestureEvent} that initiated the drag
277      * @param dragCursor     the initial {@code Cursor} for this drag operation
278      *                       or {@code null} for the default cursor handling;
279      *                       see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a>
280      *                       for more details on the cursor handling mechanism during drag and drop
281      * @param dragImage      the image to drag or {@code null}
282      * @param imageOffset    the offset of the {@code Image} origin from the hotspot
283      *                       of the {@code Cursor} at the instant of the trigger
284      * @param transferable   the subject data of the drag
285      * @param dsl            the {@code DragSourceListener}
286      * @param flavorMap      the {@code FlavorMap} to use, or {@code null}
287      *
288      * @throws java.awt.dnd.InvalidDnDOperationException
289      *    if the Drag and Drop
290      *    system is unable to initiate a drag operation, or if the user
291      *    attempts to start a drag while an existing drag operation
292      *    is still executing
293      */
294 
startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap)295     public void startDrag(DragGestureEvent   trigger,
296                           Cursor             dragCursor,
297                           Image              dragImage,
298                           Point              imageOffset,
299                           Transferable       transferable,
300                           DragSourceListener dsl,
301                           FlavorMap          flavorMap) throws InvalidDnDOperationException {
302 
303         SunDragSourceContextPeer.setDragDropInProgress(true);
304 
305         try {
306             if (flavorMap != null) this.flavorMap = flavorMap;
307 
308             DragSourceContext dsc = createDragSourceContext(trigger, dragCursor,
309                                                             dragImage,
310                                                             imageOffset,
311                                                             transferable, dsl);
312 
313             if (dsc == null) {
314                 throw new InvalidDnDOperationException();
315             }
316             DragSourceContextAccessor acc = AWTAccessor.getDragSourceContextAccessor();
317             acc.getPeer(dsc).startDrag(dsc, dsc.getCursor(), dragImage, imageOffset); // may throw
318         } catch (RuntimeException e) {
319             SunDragSourceContextPeer.setDragDropInProgress(false);
320             throw e;
321         }
322     }
323 
324     /**
325      * Start a drag, given the {@code DragGestureEvent}
326      * that initiated the drag, the initial
327      * {@code Cursor} to use,
328      * the {@code Transferable} subject data
329      * of the drag, the {@code DragSourceListener},
330      * and the {@code FlavorMap}.
331      *
332      * @param trigger        the {@code DragGestureEvent} that
333      * initiated the drag
334      * @param dragCursor     the initial {@code Cursor} for this drag operation
335      *                       or {@code null} for the default cursor handling;
336      *                       see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a>
337      *                       for more details on the cursor handling mechanism during drag and drop
338      * @param transferable   the subject data of the drag
339      * @param dsl            the {@code DragSourceListener}
340      * @param flavorMap      the {@code FlavorMap} to use or {@code null}
341      *
342      * @throws java.awt.dnd.InvalidDnDOperationException
343      *    if the Drag and Drop
344      *    system is unable to initiate a drag operation, or if the user
345      *    attempts to start a drag while an existing drag operation
346      *    is still executing
347      */
348 
startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl, FlavorMap flavorMap)349     public void startDrag(DragGestureEvent   trigger,
350                           Cursor             dragCursor,
351                           Transferable       transferable,
352                           DragSourceListener dsl,
353                           FlavorMap          flavorMap) throws InvalidDnDOperationException {
354         startDrag(trigger, dragCursor, null, null, transferable, dsl, flavorMap);
355     }
356 
357     /**
358      * Start a drag, given the {@code DragGestureEvent}
359      * that initiated the drag, the initial {@code Cursor}
360      * to use,
361      * the {@code Image} to drag,
362      * the offset of the {@code Image} origin
363      * from the hotspot of the {@code Cursor}
364      * at the instant of the trigger,
365      * the subject data of the drag, and
366      * the {@code DragSourceListener}.
367      *
368      * @param trigger           the {@code DragGestureEvent} that initiated the drag
369      * @param dragCursor     the initial {@code Cursor} for this drag operation
370      *                       or {@code null} for the default cursor handling;
371      *                       see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a>
372      *                       for more details on the cursor handling mechanism during drag and drop
373      * @param dragImage         the {@code Image} to drag or {@code null}
374      * @param dragOffset        the offset of the {@code Image} origin from the hotspot
375      *                          of the {@code Cursor} at the instant of the trigger
376      * @param transferable      the subject data of the drag
377      * @param dsl               the {@code DragSourceListener}
378      *
379      * @throws java.awt.dnd.InvalidDnDOperationException
380      *    if the Drag and Drop
381      *    system is unable to initiate a drag operation, or if the user
382      *    attempts to start a drag while an existing drag operation
383      *    is still executing
384      */
385 
startDrag(DragGestureEvent trigger, Cursor dragCursor, Image dragImage, Point dragOffset, Transferable transferable, DragSourceListener dsl)386     public void startDrag(DragGestureEvent   trigger,
387                           Cursor             dragCursor,
388                           Image              dragImage,
389                           Point              dragOffset,
390                           Transferable       transferable,
391                           DragSourceListener dsl) throws InvalidDnDOperationException {
392         startDrag(trigger, dragCursor, dragImage, dragOffset, transferable, dsl, null);
393     }
394 
395     /**
396      * Start a drag, given the {@code DragGestureEvent}
397      * that initiated the drag, the initial
398      * {@code Cursor} to
399      * use,
400      * the {@code Transferable} subject data
401      * of the drag, and the {@code DragSourceListener}.
402      *
403      * @param trigger        the {@code DragGestureEvent} that initiated the drag
404      * @param dragCursor     the initial {@code Cursor} for this drag operation
405      *                       or {@code null} for the default cursor handling;
406      *                       see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> class
407      *                       for more details on the cursor handling mechanism during drag and drop
408      * @param transferable      the subject data of the drag
409      * @param dsl               the {@code DragSourceListener}
410      *
411      * @throws java.awt.dnd.InvalidDnDOperationException
412      *    if the Drag and Drop
413      *    system is unable to initiate a drag operation, or if the user
414      *    attempts to start a drag while an existing drag operation
415      *    is still executing
416      */
417 
startDrag(DragGestureEvent trigger, Cursor dragCursor, Transferable transferable, DragSourceListener dsl)418     public void startDrag(DragGestureEvent   trigger,
419                           Cursor             dragCursor,
420                           Transferable       transferable,
421                           DragSourceListener dsl) throws InvalidDnDOperationException {
422         startDrag(trigger, dragCursor, null, null, transferable, dsl, null);
423     }
424 
425     /**
426      * Creates the {@code DragSourceContext} to handle the current drag
427      * operation.
428      * <p>
429      * To incorporate a new {@code DragSourceContext}
430      * subclass, subclass {@code DragSource} and
431      * override this method.
432      * <p>
433      * If {@code dragImage} is {@code null}, no image is used
434      * to represent the drag over feedback for this drag operation, but
435      * {@code NullPointerException} is not thrown.
436      * <p>
437      * If {@code dsl} is {@code null}, no drag source listener
438      * is registered with the created {@code DragSourceContext},
439      * but {@code NullPointerException} is not thrown.
440      *
441      * @param dgl           The {@code DragGestureEvent} that triggered the
442      *                      drag
443      * @param dragCursor     The initial {@code Cursor} for this drag operation
444      *                       or {@code null} for the default cursor handling;
445      *                       see <a href="DragSourceContext.html#defaultCursor">DragSourceContext</a> class
446      *                       for more details on the cursor handling mechanism during drag and drop
447      * @param dragImage     The {@code Image} to drag or {@code null}
448      * @param imageOffset   The offset of the {@code Image} origin from the
449      *                      hotspot of the cursor at the instant of the trigger
450      * @param t             The subject data of the drag
451      * @param dsl           The {@code DragSourceListener}
452      *
453      * @return the {@code DragSourceContext}
454      *
455      * @throws NullPointerException if {@code dscp} is {@code null}
456      * @throws NullPointerException if {@code dgl} is {@code null}
457      * @throws NullPointerException if {@code dragImage} is not
458      *    {@code null} and {@code imageOffset} is {@code null}
459      * @throws NullPointerException if {@code t} is {@code null}
460      * @throws IllegalArgumentException if the {@code Component}
461      *         associated with the trigger event is {@code null}.
462      * @throws IllegalArgumentException if the {@code DragSource} for the
463      *         trigger event is {@code null}.
464      * @throws IllegalArgumentException if the drag action for the
465      *         trigger event is {@code DnDConstants.ACTION_NONE}.
466      * @throws IllegalArgumentException if the source actions for the
467      *         {@code DragGestureRecognizer} associated with the trigger
468      *         event are equal to {@code DnDConstants.ACTION_NONE}.
469      */
470 
createDragSourceContext(DragGestureEvent dgl, Cursor dragCursor, Image dragImage, Point imageOffset, Transferable t, DragSourceListener dsl)471     protected DragSourceContext createDragSourceContext(DragGestureEvent dgl,
472                                                         Cursor dragCursor,
473                                                         Image dragImage,
474                                                         Point imageOffset,
475                                                         Transferable t,
476                                                         DragSourceListener dsl) {
477         return new DragSourceContext(dgl, dragCursor, dragImage, imageOffset, t, dsl);
478     }
479 
480     /**
481      * This method returns the
482      * {@code FlavorMap} for this {@code DragSource}.
483      *
484      * @return the {@code FlavorMap} for this {@code DragSource}
485      */
486 
getFlavorMap()487     public FlavorMap getFlavorMap() { return flavorMap; }
488 
489     /**
490      * Creates a new {@code DragGestureRecognizer}
491      * that implements the specified
492      * abstract subclass of
493      * {@code DragGestureRecognizer}, and
494      * sets the specified {@code Component}
495      * and {@code DragGestureListener} on
496      * the newly created object.
497      *
498      * @param <T> the type of {@code DragGestureRecognizer} to create
499      * @param recognizerAbstractClass the requested abstract type
500      * @param actions                 the permitted source drag actions
501      * @param c                       the {@code Component} target
502      * @param dgl        the {@code DragGestureListener} to notify
503      *
504      * @return the new {@code DragGestureRecognizer} or {@code null}
505      *    if the {@code Toolkit.createDragGestureRecognizer} method
506      *    has no implementation available for
507      *    the requested {@code DragGestureRecognizer}
508      *    subclass and returns {@code null}
509      */
510 
511     public <T extends DragGestureRecognizer> T
createDragGestureRecognizer(Class<T> recognizerAbstractClass, Component c, int actions, DragGestureListener dgl)512         createDragGestureRecognizer(Class<T> recognizerAbstractClass,
513                                     Component c, int actions,
514                                     DragGestureListener dgl)
515     {
516         return Toolkit.getDefaultToolkit().createDragGestureRecognizer(recognizerAbstractClass, this, c, actions, dgl);
517     }
518 
519 
520     /**
521      * Creates a new {@code DragGestureRecognizer}
522      * that implements the default
523      * abstract subclass of {@code DragGestureRecognizer}
524      * for this {@code DragSource},
525      * and sets the specified {@code Component}
526      * and {@code DragGestureListener} on the
527      * newly created object.
528      *
529      * For this {@code DragSource}
530      * the default is {@code MouseDragGestureRecognizer}.
531      *
532      * @param c       the {@code Component} target for the recognizer
533      * @param actions the permitted source actions
534      * @param dgl     the {@code DragGestureListener} to notify
535      *
536      * @return the new {@code DragGestureRecognizer} or {@code null}
537      *    if the {@code Toolkit.createDragGestureRecognizer} method
538      *    has no implementation available for
539      *    the requested {@code DragGestureRecognizer}
540      *    subclass and returns {@code null}
541      */
542 
createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl)543     public DragGestureRecognizer createDefaultDragGestureRecognizer(Component c, int actions, DragGestureListener dgl) {
544         return Toolkit.getDefaultToolkit().createDragGestureRecognizer(MouseDragGestureRecognizer.class, this, c, actions, dgl);
545     }
546 
547     /**
548      * Adds the specified {@code DragSourceListener} to this
549      * {@code DragSource} to receive drag source events during drag
550      * operations initiated with this {@code DragSource}.
551      * If a {@code null} listener is specified, no action is taken and no
552      * exception is thrown.
553      *
554      * @param dsl the {@code DragSourceListener} to add
555      *
556      * @see      #removeDragSourceListener
557      * @see      #getDragSourceListeners
558      * @since 1.4
559      */
addDragSourceListener(DragSourceListener dsl)560     public void addDragSourceListener(DragSourceListener dsl) {
561         if (dsl != null) {
562             synchronized (this) {
563                 listener = DnDEventMulticaster.add(listener, dsl);
564             }
565         }
566     }
567 
568     /**
569      * Removes the specified {@code DragSourceListener} from this
570      * {@code DragSource}.
571      * If a {@code null} listener is specified, no action is taken and no
572      * exception is thrown.
573      * If the listener specified by the argument was not previously added to
574      * this {@code DragSource}, no action is taken and no exception
575      * is thrown.
576      *
577      * @param dsl the {@code DragSourceListener} to remove
578      *
579      * @see      #addDragSourceListener
580      * @see      #getDragSourceListeners
581      * @since 1.4
582      */
removeDragSourceListener(DragSourceListener dsl)583     public void removeDragSourceListener(DragSourceListener dsl) {
584         if (dsl != null) {
585             synchronized (this) {
586                 listener = DnDEventMulticaster.remove(listener, dsl);
587             }
588         }
589     }
590 
591     /**
592      * Gets all the {@code DragSourceListener}s
593      * registered with this {@code DragSource}.
594      *
595      * @return all of this {@code DragSource}'s
596      *         {@code DragSourceListener}s or an empty array if no
597      *         such listeners are currently registered
598      *
599      * @see      #addDragSourceListener
600      * @see      #removeDragSourceListener
601      * @since    1.4
602      */
getDragSourceListeners()603     public DragSourceListener[] getDragSourceListeners() {
604         return getListeners(DragSourceListener.class);
605     }
606 
607     /**
608      * Adds the specified {@code DragSourceMotionListener} to this
609      * {@code DragSource} to receive drag motion events during drag
610      * operations initiated with this {@code DragSource}.
611      * If a {@code null} listener is specified, no action is taken and no
612      * exception is thrown.
613      *
614      * @param dsml the {@code DragSourceMotionListener} to add
615      *
616      * @see      #removeDragSourceMotionListener
617      * @see      #getDragSourceMotionListeners
618      * @since 1.4
619      */
addDragSourceMotionListener(DragSourceMotionListener dsml)620     public void addDragSourceMotionListener(DragSourceMotionListener dsml) {
621         if (dsml != null) {
622             synchronized (this) {
623                 motionListener = DnDEventMulticaster.add(motionListener, dsml);
624             }
625         }
626     }
627 
628     /**
629      * Removes the specified {@code DragSourceMotionListener} from this
630      * {@code DragSource}.
631      * If a {@code null} listener is specified, no action is taken and no
632      * exception is thrown.
633      * If the listener specified by the argument was not previously added to
634      * this {@code DragSource}, no action is taken and no exception
635      * is thrown.
636      *
637      * @param dsml the {@code DragSourceMotionListener} to remove
638      *
639      * @see      #addDragSourceMotionListener
640      * @see      #getDragSourceMotionListeners
641      * @since 1.4
642      */
removeDragSourceMotionListener(DragSourceMotionListener dsml)643     public void removeDragSourceMotionListener(DragSourceMotionListener dsml) {
644         if (dsml != null) {
645             synchronized (this) {
646                 motionListener = DnDEventMulticaster.remove(motionListener, dsml);
647             }
648         }
649     }
650 
651     /**
652      * Gets all of the  {@code DragSourceMotionListener}s
653      * registered with this {@code DragSource}.
654      *
655      * @return all of this {@code DragSource}'s
656      *         {@code DragSourceMotionListener}s or an empty array if no
657      *         such listeners are currently registered
658      *
659      * @see      #addDragSourceMotionListener
660      * @see      #removeDragSourceMotionListener
661      * @since    1.4
662      */
getDragSourceMotionListeners()663     public DragSourceMotionListener[] getDragSourceMotionListeners() {
664         return getListeners(DragSourceMotionListener.class);
665     }
666 
667     /**
668      * Gets all the objects currently registered as
669      * <code><em>Foo</em>Listener</code>s upon this {@code DragSource}.
670      * <code><em>Foo</em>Listener</code>s are registered using the
671      * <code>add<em>Foo</em>Listener</code> method.
672      *
673      * @param <T> the type of listener objects
674      * @param listenerType the type of listeners requested; this parameter
675      *          should specify an interface that descends from
676      *          {@code java.util.EventListener}
677      * @return an array of all objects registered as
678      *          <code><em>Foo</em>Listener</code>s on this
679      *          {@code DragSource}, or an empty array if no such listeners
680      *          have been added
681      * @exception ClassCastException if {@code listenerType}
682      *          doesn't specify a class or interface that implements
683      *          {@code java.util.EventListener}
684      *
685      * @see #getDragSourceListeners
686      * @see #getDragSourceMotionListeners
687      * @since 1.4
688      */
getListeners(Class<T> listenerType)689     public <T extends EventListener> T[] getListeners(Class<T> listenerType) {
690         EventListener l = null;
691         if (listenerType == DragSourceListener.class) {
692             l = listener;
693         } else if (listenerType == DragSourceMotionListener.class) {
694             l = motionListener;
695         }
696         return DnDEventMulticaster.getListeners(l, listenerType);
697     }
698 
699     /**
700      * This method calls {@code dragEnter} on the
701      * {@code DragSourceListener}s registered with this
702      * {@code DragSource}, and passes them the specified
703      * {@code DragSourceDragEvent}.
704      *
705      * @param dsde the {@code DragSourceDragEvent}
706      */
processDragEnter(DragSourceDragEvent dsde)707     void processDragEnter(DragSourceDragEvent dsde) {
708         DragSourceListener dsl = listener;
709         if (dsl != null) {
710             dsl.dragEnter(dsde);
711         }
712     }
713 
714     /**
715      * This method calls {@code dragOver} on the
716      * {@code DragSourceListener}s registered with this
717      * {@code DragSource}, and passes them the specified
718      * {@code DragSourceDragEvent}.
719      *
720      * @param dsde the {@code DragSourceDragEvent}
721      */
processDragOver(DragSourceDragEvent dsde)722     void processDragOver(DragSourceDragEvent dsde) {
723         DragSourceListener dsl = listener;
724         if (dsl != null) {
725             dsl.dragOver(dsde);
726         }
727     }
728 
729     /**
730      * This method calls {@code dropActionChanged} on the
731      * {@code DragSourceListener}s registered with this
732      * {@code DragSource}, and passes them the specified
733      * {@code DragSourceDragEvent}.
734      *
735      * @param dsde the {@code DragSourceDragEvent}
736      */
processDropActionChanged(DragSourceDragEvent dsde)737     void processDropActionChanged(DragSourceDragEvent dsde) {
738         DragSourceListener dsl = listener;
739         if (dsl != null) {
740             dsl.dropActionChanged(dsde);
741         }
742     }
743 
744     /**
745      * This method calls {@code dragExit} on the
746      * {@code DragSourceListener}s registered with this
747      * {@code DragSource}, and passes them the specified
748      * {@code DragSourceEvent}.
749      *
750      * @param dse the {@code DragSourceEvent}
751      */
processDragExit(DragSourceEvent dse)752     void processDragExit(DragSourceEvent dse) {
753         DragSourceListener dsl = listener;
754         if (dsl != null) {
755             dsl.dragExit(dse);
756         }
757     }
758 
759     /**
760      * This method calls {@code dragDropEnd} on the
761      * {@code DragSourceListener}s registered with this
762      * {@code DragSource}, and passes them the specified
763      * {@code DragSourceDropEvent}.
764      *
765      * @param dsde the {@code DragSourceEvent}
766      */
processDragDropEnd(DragSourceDropEvent dsde)767     void processDragDropEnd(DragSourceDropEvent dsde) {
768         DragSourceListener dsl = listener;
769         if (dsl != null) {
770             dsl.dragDropEnd(dsde);
771         }
772     }
773 
774     /**
775      * This method calls {@code dragMouseMoved} on the
776      * {@code DragSourceMotionListener}s registered with this
777      * {@code DragSource}, and passes them the specified
778      * {@code DragSourceDragEvent}.
779      *
780      * @param dsde the {@code DragSourceEvent}
781      */
processDragMouseMoved(DragSourceDragEvent dsde)782     void processDragMouseMoved(DragSourceDragEvent dsde) {
783         DragSourceMotionListener dsml = motionListener;
784         if (dsml != null) {
785             dsml.dragMouseMoved(dsde);
786         }
787     }
788 
789     /**
790      * Serializes this {@code DragSource}. This method first performs
791      * default serialization. Next, it writes out this object's
792      * {@code FlavorMap} if and only if it can be serialized. If not,
793      * {@code null} is written instead. Next, it writes out
794      * {@code Serializable} listeners registered with this
795      * object. Listeners are written in a {@code null}-terminated sequence
796      * of 0 or more pairs. The pair consists of a {@code String} and an
797      * {@code Object}; the {@code String} indicates the type of the
798      * {@code Object} and is one of the following:
799      * <ul>
800      * <li>{@code dragSourceListenerK} indicating a
801      *     {@code DragSourceListener} object;
802      * <li>{@code dragSourceMotionListenerK} indicating a
803      *     {@code DragSourceMotionListener} object.
804      * </ul>
805      *
806      * @serialData Either a {@code FlavorMap} instance, or
807      *      {@code null}, followed by a {@code null}-terminated
808      *      sequence of 0 or more pairs; the pair consists of a
809      *      {@code String} and an {@code Object}; the
810      *      {@code String} indicates the type of the {@code Object}
811      *      and is one of the following:
812      *      <ul>
813      *      <li>{@code dragSourceListenerK} indicating a
814      *          {@code DragSourceListener} object;
815      *      <li>{@code dragSourceMotionListenerK} indicating a
816      *          {@code DragSourceMotionListener} object.
817      *      </ul>.
818      * @since 1.4
819      */
writeObject(ObjectOutputStream s)820     private void writeObject(ObjectOutputStream s) throws IOException {
821         s.defaultWriteObject();
822 
823         s.writeObject(SerializationTester.test(flavorMap) ? flavorMap : null);
824 
825         DnDEventMulticaster.save(s, dragSourceListenerK, listener);
826         DnDEventMulticaster.save(s, dragSourceMotionListenerK, motionListener);
827         s.writeObject(null);
828     }
829 
830     /**
831      * Deserializes this {@code DragSource}. This method first performs
832      * default deserialization. Next, this object's {@code FlavorMap} is
833      * deserialized by using the next object in the stream.
834      * If the resulting {@code FlavorMap} is {@code null}, this
835      * object's {@code FlavorMap} is set to the default FlavorMap for
836      * this thread's {@code ClassLoader}.
837      * Next, this object's listeners are deserialized by reading a
838      * {@code null}-terminated sequence of 0 or more key/value pairs
839      * from the stream:
840      * <ul>
841      * <li>If a key object is a {@code String} equal to
842      * {@code dragSourceListenerK}, a {@code DragSourceListener} is
843      * deserialized using the corresponding value object and added to this
844      * {@code DragSource}.
845      * <li>If a key object is a {@code String} equal to
846      * {@code dragSourceMotionListenerK}, a
847      * {@code DragSourceMotionListener} is deserialized using the
848      * corresponding value object and added to this {@code DragSource}.
849      * <li>Otherwise, the key/value pair is skipped.
850      * </ul>
851      *
852      * @see java.awt.datatransfer.SystemFlavorMap#getDefaultFlavorMap
853      * @since 1.4
854      */
readObject(ObjectInputStream s)855     private void readObject(ObjectInputStream s)
856       throws ClassNotFoundException, IOException {
857         s.defaultReadObject();
858 
859         // 'flavorMap' was written explicitly
860         flavorMap = (FlavorMap)s.readObject();
861 
862         // Implementation assumes 'flavorMap' is never null.
863         if (flavorMap == null) {
864             flavorMap = SystemFlavorMap.getDefaultFlavorMap();
865         }
866 
867         Object keyOrNull;
868         while (null != (keyOrNull = s.readObject())) {
869             String key = ((String)keyOrNull).intern();
870 
871             if (dragSourceListenerK == key) {
872                 addDragSourceListener((DragSourceListener)(s.readObject()));
873             } else if (dragSourceMotionListenerK == key) {
874                 addDragSourceMotionListener(
875                     (DragSourceMotionListener)(s.readObject()));
876             } else {
877                 // skip value for unrecognized key
878                 s.readObject();
879             }
880         }
881     }
882 
883     /**
884      * Returns the drag gesture motion threshold. The drag gesture motion threshold
885      * defines the recommended behavior for {@link MouseDragGestureRecognizer}s.
886      * <p>
887      * If the system property {@code awt.dnd.drag.threshold} is set to
888      * a positive integer, this method returns the value of the system property;
889      * otherwise if a pertinent desktop property is available and supported by
890      * the implementation of the Java platform, this method returns the value of
891      * that property; otherwise this method returns some default value.
892      * The pertinent desktop property can be queried using
893      * {@code java.awt.Toolkit.getDesktopProperty("DnD.gestureMotionThreshold")}.
894      *
895      * @return the drag gesture motion threshold
896      * @see MouseDragGestureRecognizer
897      * @since 1.5
898      */
getDragThreshold()899     public static int getDragThreshold() {
900         int ts = AccessController.doPrivileged(
901                 new GetIntegerAction("awt.dnd.drag.threshold", 0)).intValue();
902         if (ts > 0) {
903             return ts;
904         } else {
905             Integer td = (Integer)Toolkit.getDefaultToolkit().
906                     getDesktopProperty("DnD.gestureMotionThreshold");
907             if (td != null) {
908                 return td.intValue();
909             }
910         }
911         return 5;
912     }
913 
914     /*
915      * fields
916      */
917 
918     private transient FlavorMap flavorMap = SystemFlavorMap.getDefaultFlavorMap();
919 
920     private transient DragSourceListener listener;
921 
922     private transient DragSourceMotionListener motionListener;
923 }
924