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