1 /*
   2  * Copyright (c) 1997, 2006, 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 
 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      */
 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 
 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      */
 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 
 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 
 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 
 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 
 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 
 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 
 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
 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 
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 <code>ClassCastException</code> 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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      */
 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 }