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
26package java.awt.dnd;
27
28import java.awt.Component;
29import java.awt.Cursor;
30import java.awt.GraphicsEnvironment;
31import java.awt.HeadlessException;
32import java.awt.Image;
33import java.awt.Point;
34import java.awt.Toolkit;
35import java.awt.datatransfer.FlavorMap;
36import java.awt.datatransfer.SystemFlavorMap;
37import java.awt.datatransfer.Transferable;
38import java.io.IOException;
39import java.io.ObjectInputStream;
40import java.io.ObjectOutputStream;
41import java.io.Serializable;
42import java.security.AccessController;
43import java.util.EventListener;
44
45import sun.awt.AWTAccessor;
46import sun.awt.AWTAccessor.DragSourceContextAccessor;
47import sun.awt.dnd.SunDragSourceContextPeer;
48import 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
119public class DragSource implements Serializable {
120
121    private static final long serialVersionUID = 6236096958971414066L;
122
123    /*
124     * load a system default cursor
125     */
126
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     */
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
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     */
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
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
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
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
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
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
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
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
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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