1 /*
   2  * Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.awt.dnd;
  27 
  28 import java.awt.Component;
  29 import java.awt.Cursor;
  30 import java.awt.GraphicsEnvironment;
  31 import java.awt.HeadlessException;
  32 import java.awt.Image;
  33 import java.awt.Point;
  34 import java.awt.Toolkit;
  35 import java.awt.datatransfer.FlavorMap;
  36 import java.awt.datatransfer.SystemFlavorMap;
  37 import java.awt.datatransfer.Transferable;
  38 import java.io.IOException;
  39 import java.io.ObjectInputStream;
  40 import java.io.ObjectOutputStream;
  41 import java.io.Serializable;
  42 import java.security.AccessController;
  43 import java.util.EventListener;
  44 
  45 import sun.awt.AWTAccessor;
  46 import sun.awt.AWTAccessor.DragSourceContextAccessor;
  47 import sun.awt.dnd.SunDragSourceContextPeer;
  48 import sun.security.action.GetIntegerAction;
  49 
  50 
  51 /**
  52  * The <code>DragSource</code> 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</code>, or application specific
  61  * object associated with a <code>Component</code>
  62  * instance in the GUI. [implementation dependent]
  63  * <LI>Some other arbitrary association. [implementation dependent]
  64  *</UL>
  65  *
  66  * Once the <code>DragSource</code> is
  67  * obtained, a <code>DragGestureRecognizer</code> should
  68  * also be obtained to associate the <code>DragSource</code>
  69  * with a particular
  70  * <code>Component</code>.
  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</code>, which is usually
  76  * implemented by a <code>DragGestureRecognizer</code>.
  77  *<P>
  78  * When a drag gesture occurs, the
  79  * <code>DragSource</code>'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</code> 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</code>
  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</code>
  96  * and associate the <code>DragSourceContextPeer</code>
  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</code>
 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</code>.
 115  *
 116  * @since 1.2
 117  */
 118 
 119 public class DragSource implements Serializable {
 120 
 121     private static final long serialVersionUID = 6236096958971414066L;
 122 
 123     /*
 124      * load a system default cursor
 125      */
 126 
 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</code> to use with a copy operation indicating
 144      * that a drop is currently allowed. <code>null</code> if
 145      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> to use with a move operation indicating
 154      * that a drop is currently allowed. <code>null</code> if
 155      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> to use with a link operation indicating
 164      * that a drop is currently allowed. <code>null</code> if
 165      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> to use with a copy operation indicating
 174      * that a drop is currently not allowed. <code>null</code> if
 175      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> to use with a move operation indicating
 184      * that a drop is currently not allowed. <code>null</code> if
 185      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> to use with a link operation indicating
 194      * that a drop is currently not allowed. <code>null</code> if
 195      * <code>GraphicsEnvironment.isHeadless()</code> returns <code>true</code>.
 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</code> 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</code> 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</code>.
 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</code>
 266      * that initiated the drag, the initial
 267      * <code>Cursor</code> to use,
 268      * the <code>Image</code> to drag,
 269      * the offset of the <code>Image</code> origin
 270      * from the hotspot of the <code>Cursor</code> at
 271      * the instant of the trigger,
 272      * the <code>Transferable</code> subject data
 273      * of the drag, the <code>DragSourceListener</code>,
 274      * and the <code>FlavorMap</code>.
 275      *
 276      * @param trigger        the <code>DragGestureEvent</code> 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</code> origin from the hotspot
 283      *                       of the <code>Cursor</code> at the instant of the trigger
 284      * @param transferable   the subject data of the drag
 285      * @param dsl            the <code>DragSourceListener</code>
 286      * @param flavorMap      the <code>FlavorMap</code> to use, or <code>null</code>
 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</code>
 326      * that initiated the drag, the initial
 327      * <code>Cursor</code> to use,
 328      * the <code>Transferable</code> subject data
 329      * of the drag, the <code>DragSourceListener</code>,
 330      * and the <code>FlavorMap</code>.
 331      *
 332      * @param trigger        the <code>DragGestureEvent</code> 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</code>
 340      * @param flavorMap      the <code>FlavorMap</code> to use or <code>null</code>
 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</code>
 359      * that initiated the drag, the initial <code>Cursor</code>
 360      * to use,
 361      * the <code>Image</code> to drag,
 362      * the offset of the <code>Image</code> origin
 363      * from the hotspot of the <code>Cursor</code>
 364      * at the instant of the trigger,
 365      * the subject data of the drag, and
 366      * the <code>DragSourceListener</code>.
 367      *
 368      * @param trigger           the <code>DragGestureEvent</code> 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</code> to drag or <code>null</code>
 374      * @param dragOffset        the offset of the <code>Image</code> origin from the hotspot
 375      *                          of the <code>Cursor</code> at the instant of the trigger
 376      * @param transferable      the subject data of the drag
 377      * @param dsl               the <code>DragSourceListener</code>
 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</code>
 397      * that initiated the drag, the initial
 398      * <code>Cursor</code> to
 399      * use,
 400      * the <code>Transferable</code> subject data
 401      * of the drag, and the <code>DragSourceListener</code>.
 402      *
 403      * @param trigger        the <code>DragGestureEvent</code> 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</code>
 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</code>
 430      * subclass, subclass <code>DragSource</code> and
 431      * override this method.
 432      * <p>
 433      * If <code>dragImage</code> is <code>null</code>, no image is used
 434      * to represent the drag over feedback for this drag operation, but
 435      * <code>NullPointerException</code> is not thrown.
 436      * <p>
 437      * If <code>dsl</code> is <code>null</code>, no drag source listener
 438      * is registered with the created <code>DragSourceContext</code>,
 439      * but <code>NullPointerException</code> is not thrown.
 440      *
 441      * @param dgl           The <code>DragGestureEvent</code> 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</code> to drag or <code>null</code>
 448      * @param imageOffset   The offset of the <code>Image</code> 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</code>
 452      *
 453      * @return the <code>DragSourceContext</code>
 454      *
 455      * @throws NullPointerException if <code>dscp</code> is <code>null</code>
 456      * @throws NullPointerException if <code>dgl</code> is <code>null</code>
 457      * @throws NullPointerException if <code>dragImage</code> is not
 458      *    <code>null</code> and <code>imageOffset</code> is <code>null</code>
 459      * @throws NullPointerException if <code>t</code> is <code>null</code>
 460      * @throws IllegalArgumentException if the <code>Component</code>
 461      *         associated with the trigger event is <code>null</code>.
 462      * @throws IllegalArgumentException if the <code>DragSource</code> for the
 463      *         trigger event is <code>null</code>.
 464      * @throws IllegalArgumentException if the drag action for the
 465      *         trigger event is <code>DnDConstants.ACTION_NONE</code>.
 466      * @throws IllegalArgumentException if the source actions for the
 467      *         <code>DragGestureRecognizer</code> associated with the trigger
 468      *         event are equal to <code>DnDConstants.ACTION_NONE</code>.
 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</code> for this <code>DragSource</code>.
 483      *
 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      *
 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</code> target
 502      * @param dgl        the <code>DragGestureListener</code> to notify
 503      *
 504      * @return the new <code>DragGestureRecognizer</code> or <code>null</code>
 505      *    if the <code>Toolkit.createDragGestureRecognizer</code> method
 506      *    has no implementation available for
 507      *    the requested <code>DragGestureRecognizer</code>
 508      *    subclass and returns <code>null</code>
 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</code>
 522      * that implements the default
 523      * abstract subclass of <code>DragGestureRecognizer</code>
 524      * for this <code>DragSource</code>,
 525      * and sets the specified <code>Component</code>
 526      * and <code>DragGestureListener</code> on the
 527      * newly created object.
 528      *
 529      * For this <code>DragSource</code>
 530      * the default is <code>MouseDragGestureRecognizer</code>.
 531      *
 532      * @param c       the <code>Component</code> target for the recognizer
 533      * @param actions the permitted source actions
 534      * @param dgl     the <code>DragGestureListener</code> to notify
 535      *
 536      * @return the new <code>DragGestureRecognizer</code> or <code>null</code>
 537      *    if the <code>Toolkit.createDragGestureRecognizer</code> method
 538      *    has no implementation available for
 539      *    the requested <code>DragGestureRecognizer</code>
 540      *    subclass and returns <code>null</code>
 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</code> to this
 549      * <code>DragSource</code> to receive drag source events during drag
 550      * operations initiated with this <code>DragSource</code>.
 551      * If a <code>null</code> listener is specified, no action is taken and no
 552      * exception is thrown.
 553      *
 554      * @param dsl the <code>DragSourceListener</code> 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</code> from this
 570      * <code>DragSource</code>.
 571      * If a <code>null</code> 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</code>, no action is taken and no exception
 575      * is thrown.
 576      *
 577      * @param dsl the <code>DragSourceListener</code> 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</code>s
 593      * registered with this <code>DragSource</code>.
 594      *
 595      * @return all of this <code>DragSource</code>'s
 596      *         <code>DragSourceListener</code>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</code> to this
 609      * <code>DragSource</code> to receive drag motion events during drag
 610      * operations initiated with this <code>DragSource</code>.
 611      * If a <code>null</code> listener is specified, no action is taken and no
 612      * exception is thrown.
 613      *
 614      * @param dsml the <code>DragSourceMotionListener</code> 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</code> from this
 630      * <code>DragSource</code>.
 631      * If a <code>null</code> 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</code>, no action is taken and no exception
 635      * is thrown.
 636      *
 637      * @param dsml the <code>DragSourceMotionListener</code> 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</code>s
 653      * registered with this <code>DragSource</code>.
 654      *
 655      * @return all of this <code>DragSource</code>'s
 656      *         <code>DragSourceMotionListener</code>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</code>.
 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</code>
 677      * @return an array of all objects registered as
 678      *          <code><em>Foo</em>Listener</code>s on this
 679      *          <code>DragSource</code>, or an empty array if no such listeners
 680      *          have been added
 681      * @exception ClassCastException if <code>listenerType</code>
 682      *          doesn't specify a class or interface that implements
 683      *          <code>java.util.EventListener</code>
 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</code> on the
 701      * <code>DragSourceListener</code>s registered with this
 702      * <code>DragSource</code>, and passes them the specified
 703      * <code>DragSourceDragEvent</code>.
 704      *
 705      * @param dsde the <code>DragSourceDragEvent</code>
 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</code> on the
 716      * <code>DragSourceListener</code>s registered with this
 717      * <code>DragSource</code>, and passes them the specified
 718      * <code>DragSourceDragEvent</code>.
 719      *
 720      * @param dsde the <code>DragSourceDragEvent</code>
 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</code> on the
 731      * <code>DragSourceListener</code>s registered with this
 732      * <code>DragSource</code>, and passes them the specified
 733      * <code>DragSourceDragEvent</code>.
 734      *
 735      * @param dsde the <code>DragSourceDragEvent</code>
 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</code> on the
 746      * <code>DragSourceListener</code>s registered with this
 747      * <code>DragSource</code>, and passes them the specified
 748      * <code>DragSourceEvent</code>.
 749      *
 750      * @param dse the <code>DragSourceEvent</code>
 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</code> on the
 761      * <code>DragSourceListener</code>s registered with this
 762      * <code>DragSource</code>, and passes them the specified
 763      * <code>DragSourceDropEvent</code>.
 764      *
 765      * @param dsde the <code>DragSourceEvent</code>
 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</code> on the
 776      * <code>DragSourceMotionListener</code>s registered with this
 777      * <code>DragSource</code>, and passes them the specified
 778      * <code>DragSourceDragEvent</code>.
 779      *
 780      * @param dsde the <code>DragSourceEvent</code>
 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</code>. This method first performs
 791      * default serialization. Next, it writes out this object's
 792      * <code>FlavorMap</code> if and only if it can be serialized. If not,
 793      * <code>null</code> is written instead. Next, it writes out
 794      * <code>Serializable</code> listeners registered with this
 795      * object. Listeners are written in a <code>null</code>-terminated sequence
 796      * of 0 or more pairs. The pair consists of a <code>String</code> and an
 797      * <code>Object</code>; the <code>String</code> indicates the type of the
 798      * <code>Object</code> and is one of the following:
 799      * <ul>
 800      * <li><code>dragSourceListenerK</code> indicating a
 801      *     <code>DragSourceListener</code> object;
 802      * <li><code>dragSourceMotionListenerK</code> indicating a
 803      *     <code>DragSourceMotionListener</code> object.
 804      * </ul>
 805      *
 806      * @serialData Either a <code>FlavorMap</code> instance, or
 807      *      <code>null</code>, followed by a <code>null</code>-terminated
 808      *      sequence of 0 or more pairs; the pair consists of a
 809      *      <code>String</code> and an <code>Object</code>; the
 810      *      <code>String</code> indicates the type of the <code>Object</code>
 811      *      and is one of the following:
 812      *      <ul>
 813      *      <li><code>dragSourceListenerK</code> indicating a
 814      *          <code>DragSourceListener</code> object;
 815      *      <li><code>dragSourceMotionListenerK</code> indicating a
 816      *          <code>DragSourceMotionListener</code> 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</code>. This method first performs
 832      * default deserialization. Next, this object's <code>FlavorMap</code> is
 833      * deserialized by using the next object in the stream.
 834      * If the resulting <code>FlavorMap</code> is <code>null</code>, this
 835      * object's <code>FlavorMap</code> is set to the default FlavorMap for
 836      * this thread's <code>ClassLoader</code>.
 837      * Next, this object's listeners are deserialized by reading a
 838      * <code>null</code>-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</code> equal to
 842      * <code>dragSourceListenerK</code>, a <code>DragSourceListener</code> is
 843      * deserialized using the corresponding value object and added to this
 844      * <code>DragSource</code>.
 845      * <li>If a key object is a <code>String</code> equal to
 846      * <code>dragSourceMotionListenerK</code>, a
 847      * <code>DragSourceMotionListener</code> is deserialized using the
 848      * corresponding value object and added to this <code>DragSource</code>.
 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</code> 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")</code>.
 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 }