1 /*
   2  * Copyright (c) 1997, 2014, 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.Image;
  31 import java.awt.Point;
  32 
  33 import java.awt.datatransfer.DataFlavor;
  34 import java.awt.datatransfer.Transferable;
  35 import java.awt.datatransfer.UnsupportedFlavorException;
  36 
  37 import java.awt.dnd.peer.DragSourceContextPeer;
  38 
  39 import java.io.IOException;
  40 import java.io.InvalidObjectException;
  41 import java.io.ObjectOutputStream;
  42 import java.io.ObjectInputStream;
  43 import java.io.Serializable;
  44 
  45 import java.util.TooManyListenersException;
  46 
  47 /**
  48  * The <code>DragSourceContext</code> class is responsible for managing the
  49  * initiator side of the Drag and Drop protocol. In particular, it is responsible
  50  * for managing drag event notifications to the
  51  * {@linkplain DragSourceListener DragSourceListeners}
  52  * and {@linkplain DragSourceMotionListener DragSourceMotionListeners}, and providing the
  53  * {@link Transferable} representing the source data for the drag operation.
  54  * <p>
  55  * Note that the <code>DragSourceContext</code> itself
  56  * implements the <code>DragSourceListener</code> and
  57  * <code>DragSourceMotionListener</code> interfaces.
  58  * This is to allow the platform peer
  59  * (the {@link DragSourceContextPeer} instance)
  60  * created by the {@link DragSource} to notify
  61  * the <code>DragSourceContext</code> of
  62  * state changes in the ongoing operation. This allows the
  63  * <code>DragSourceContext</code> object to interpose
  64  * itself between the platform and the
  65  * listeners provided by the initiator of the drag operation.
  66  * <p>
  67  * <a name="defaultCursor"></a>
  68  * By default, {@code DragSourceContext} sets the cursor as appropriate
  69  * for the current state of the drag and drop operation. For example, if
  70  * the user has chosen {@linkplain DnDConstants#ACTION_MOVE the move action},
  71  * and the pointer is over a target that accepts
  72  * the move action, the default move cursor is shown. When
  73  * the pointer is over an area that does not accept the transfer,
  74  * the default "no drop" cursor is shown.
  75  * <p>
  76  * This default handling mechanism is disabled when a custom cursor is set
  77  * by the {@link #setCursor} method. When the default handling is disabled,
  78  * it becomes the responsibility
  79  * of the developer to keep the cursor up to date, by listening
  80  * to the {@code DragSource} events and calling the {@code setCursor()} method.
  81  * Alternatively, you can provide custom cursor behavior by providing
  82  * custom implementations of the {@code DragSource}
  83  * and the {@code DragSourceContext} classes.
  84  *
  85  * @see DragSourceListener
  86  * @see DragSourceMotionListener
  87  * @see DnDConstants
  88  * @since 1.2
  89  */
  90 
  91 public class DragSourceContext
  92     implements DragSourceListener, DragSourceMotionListener, Serializable {
  93 
  94     private static final long serialVersionUID = -115407898692194719L;
  95 
  96     // used by updateCurrentCursor
  97 
  98     /**
  99      * An <code>int</code> used by updateCurrentCursor()
 100      * indicating that the <code>Cursor</code> should change
 101      * to the default (no drop) <code>Cursor</code>.
 102      */
 103     protected static final int DEFAULT = 0;
 104 
 105     /**
 106      * An <code>int</code> used by updateCurrentCursor()
 107      * indicating that the <code>Cursor</code>
 108      * has entered a <code>DropTarget</code>.
 109      */
 110     protected static final int ENTER   = 1;
 111 
 112     /**
 113      * An <code>int</code> used by updateCurrentCursor()
 114      * indicating that the <code>Cursor</code> is
 115      * over a <code>DropTarget</code>.
 116      */
 117     protected static final int OVER    = 2;
 118 
 119     /**
 120      * An <code>int</code> used by updateCurrentCursor()
 121      * indicating that the user operation has changed.
 122      */
 123 
 124     protected static final int CHANGED = 3;
 125 
 126     /**
 127      * Called from <code>DragSource</code>, this constructor creates a new
 128      * <code>DragSourceContext</code> given the
 129      * <code>DragSourceContextPeer</code> for this Drag, the
 130      * <code>DragGestureEvent</code> that triggered the Drag, the initial
 131      * <code>Cursor</code> to use for the Drag, an (optional)
 132      * <code>Image</code> to display while the Drag is taking place, the offset
 133      * of the <code>Image</code> origin from the hotspot at the instant of the
 134      * triggering event, the <code>Transferable</code> subject data, and the
 135      * <code>DragSourceListener</code> to use during the Drag and Drop
 136      * operation.
 137      * <br>
 138      * If <code>DragSourceContextPeer</code> is <code>null</code>
 139      * <code>NullPointerException</code> is thrown.
 140      * <br>
 141      * If <code>DragGestureEvent</code> is <code>null</code>
 142      * <code>NullPointerException</code> is thrown.
 143      * <br>
 144      * If <code>Cursor</code> is <code>null</code> no exception is thrown and
 145      * the default drag cursor behavior is activated for this drag operation.
 146      * <br>
 147      * If <code>Image</code> is <code>null</code> no exception is thrown.
 148      * <br>
 149      * If <code>Image</code> is not <code>null</code> and the offset is
 150      * <code>null</code> <code>NullPointerException</code> is thrown.
 151      * <br>
 152      * If <code>Transferable</code> is <code>null</code>
 153      * <code>NullPointerException</code> is thrown.
 154      * <br>
 155      * If <code>DragSourceListener</code> is <code>null</code> no exception
 156      * is thrown.
 157      *
 158      * @param dscp       the <code>DragSourceContextPeer</code> for this drag
 159      * @param trigger    the triggering event
 160      * @param dragCursor     the initial {@code Cursor} for this drag operation
 161      *                       or {@code null} for the default cursor handling;
 162      *                       see <a href="DragSourceContext.html#defaultCursor">class level documentation</a>
 163      *                       for more details on the cursor handling mechanism during drag and drop
 164      * @param dragImage  the <code>Image</code> to drag (or <code>null</code>)
 165      * @param offset     the offset of the image origin from the hotspot at the
 166      *                   instant of the triggering event
 167      * @param t          the <code>Transferable</code>
 168      * @param dsl        the <code>DragSourceListener</code>
 169      *
 170      * @throws IllegalArgumentException if the <code>Component</code> associated
 171      *         with the trigger event is <code>null</code>.
 172      * @throws IllegalArgumentException if the <code>DragSource</code> for the
 173      *         trigger event is <code>null</code>.
 174      * @throws IllegalArgumentException if the drag action for the
 175      *         trigger event is <code>DnDConstants.ACTION_NONE</code>.
 176      * @throws IllegalArgumentException if the source actions for the
 177      *         <code>DragGestureRecognizer</code> associated with the trigger
 178      *         event are equal to <code>DnDConstants.ACTION_NONE</code>.
 179      * @throws NullPointerException if dscp, trigger, or t are null, or
 180      *         if dragImage is non-null and offset is null
 181      */
 182     public DragSourceContext(DragSourceContextPeer dscp,
 183                              DragGestureEvent trigger, Cursor dragCursor,
 184                              Image dragImage, Point offset, Transferable t,
 185                              DragSourceListener dsl) {
 186         if (dscp == null) {
 187             throw new NullPointerException("DragSourceContextPeer");
 188         }
 189 
 190         if (trigger == null) {
 191             throw new NullPointerException("Trigger");
 192         }
 193 
 194         if (trigger.getDragSource() == null) {
 195             throw new IllegalArgumentException("DragSource");
 196         }
 197 
 198         if (trigger.getComponent() == null) {
 199             throw new IllegalArgumentException("Component");
 200         }
 201 
 202         if (trigger.getSourceAsDragGestureRecognizer().getSourceActions() ==
 203                  DnDConstants.ACTION_NONE) {
 204             throw new IllegalArgumentException("source actions");
 205         }
 206 
 207         if (trigger.getDragAction() == DnDConstants.ACTION_NONE) {
 208             throw new IllegalArgumentException("no drag action");
 209         }
 210 
 211         if (t == null) {
 212             throw new NullPointerException("Transferable");
 213         }
 214 
 215         if (dragImage != null && offset == null) {
 216             throw new NullPointerException("offset");
 217         }
 218 
 219         peer         = dscp;
 220         this.trigger = trigger;
 221         cursor       = dragCursor;
 222         transferable = t;
 223         listener     = dsl;
 224         sourceActions =
 225             trigger.getSourceAsDragGestureRecognizer().getSourceActions();
 226 
 227         useCustomCursor = (dragCursor != null);
 228 
 229         updateCurrentCursor(trigger.getDragAction(), getSourceActions(), DEFAULT);
 230     }
 231 
 232     /**
 233      * Returns the <code>DragSource</code>
 234      * that instantiated this <code>DragSourceContext</code>.
 235      *
 236      * @return the <code>DragSource</code> that
 237      *   instantiated this <code>DragSourceContext</code>
 238      */
 239 
 240     public DragSource   getDragSource() { return trigger.getDragSource(); }
 241 
 242     /**
 243      * Returns the <code>Component</code> associated with this
 244      * <code>DragSourceContext</code>.
 245      *
 246      * @return the <code>Component</code> that started the drag
 247      */
 248 
 249     public Component    getComponent() { return trigger.getComponent(); }
 250 
 251     /**
 252      * Returns the <code>DragGestureEvent</code>
 253      * that initially triggered the drag.
 254      *
 255      * @return the Event that triggered the drag
 256      */
 257 
 258     public DragGestureEvent getTrigger() { return trigger; }
 259 
 260     /**
 261      * Returns a bitwise mask of <code>DnDConstants</code> that
 262      * represent the set of drop actions supported by the drag source for the
 263      * drag operation associated with this <code>DragSourceContext</code>.
 264      *
 265      * @return the drop actions supported by the drag source
 266      */
 267     public int  getSourceActions() {
 268         return sourceActions;
 269     }
 270 
 271     /**
 272      * Sets the cursor for this drag operation to the specified
 273      * <code>Cursor</code>.  If the specified <code>Cursor</code>
 274      * is <code>null</code>, the default drag cursor behavior is
 275      * activated for this drag operation, otherwise it is deactivated.
 276      *
 277      * @param c     the initial {@code Cursor} for this drag operation,
 278      *                       or {@code null} for the default cursor handling;
 279      *                       see {@linkplain Cursor class
 280      *                       level documentation} for more details
 281      *                       on the cursor handling during drag and drop
 282      *
 283      */
 284 
 285     public synchronized void setCursor(Cursor c) {
 286         useCustomCursor = (c != null);
 287         setCursorImpl(c);
 288     }
 289 
 290     /**
 291      * Returns the current drag <code>Cursor</code>.
 292      *
 293      * @return the current drag <code>Cursor</code>
 294      */
 295 
 296     public Cursor getCursor() { return cursor; }
 297 
 298     /**
 299      * Add a <code>DragSourceListener</code> to this
 300      * <code>DragSourceContext</code> if one has not already been added.
 301      * If a <code>DragSourceListener</code> already exists,
 302      * this method throws a <code>TooManyListenersException</code>.
 303      *
 304      * @param dsl the <code>DragSourceListener</code> to add.
 305      * Note that while <code>null</code> is not prohibited,
 306      * it is not acceptable as a parameter.
 307      *
 308      * @throws TooManyListenersException if
 309      * a <code>DragSourceListener</code> has already been added
 310      */
 311 
 312     public synchronized void addDragSourceListener(DragSourceListener dsl) throws TooManyListenersException {
 313         if (dsl == null) return;
 314 
 315         if (equals(dsl)) throw new IllegalArgumentException("DragSourceContext may not be its own listener");
 316 
 317         if (listener != null)
 318             throw new TooManyListenersException();
 319         else
 320             listener = dsl;
 321     }
 322 
 323     /**
 324      * Removes the specified <code>DragSourceListener</code>
 325      * from  this <code>DragSourceContext</code>.
 326      *
 327      * @param dsl the <code>DragSourceListener</code> to remove;
 328      *     note that while <code>null</code> is not prohibited,
 329      *     it is not acceptable as a parameter
 330      */
 331 
 332     public synchronized void removeDragSourceListener(DragSourceListener dsl) {
 333         if (listener != null && listener.equals(dsl)) {
 334             listener = null;
 335         } else
 336             throw new IllegalArgumentException();
 337     }
 338 
 339     /**
 340      * Notifies the peer that the <code>Transferable</code>'s
 341      * <code>DataFlavor</code>s have changed.
 342      */
 343 
 344     public void transferablesFlavorsChanged() {
 345         if (peer != null) peer.transferablesFlavorsChanged();
 346     }
 347 
 348     /**
 349      * Calls <code>dragEnter</code> on the
 350      * <code>DragSourceListener</code>s registered with this
 351      * <code>DragSourceContext</code> and with the associated
 352      * <code>DragSource</code>, and passes them the specified
 353      * <code>DragSourceDragEvent</code>.
 354      *
 355      * @param dsde the <code>DragSourceDragEvent</code>
 356      */
 357     public void dragEnter(DragSourceDragEvent dsde) {
 358         DragSourceListener dsl = listener;
 359         if (dsl != null) {
 360             dsl.dragEnter(dsde);
 361         }
 362         getDragSource().processDragEnter(dsde);
 363 
 364         updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), ENTER);
 365     }
 366 
 367     /**
 368      * Calls <code>dragOver</code> on the
 369      * <code>DragSourceListener</code>s registered with this
 370      * <code>DragSourceContext</code> and with the associated
 371      * <code>DragSource</code>, and passes them the specified
 372      * <code>DragSourceDragEvent</code>.
 373      *
 374      * @param dsde the <code>DragSourceDragEvent</code>
 375      */
 376     public void dragOver(DragSourceDragEvent dsde) {
 377         DragSourceListener dsl = listener;
 378         if (dsl != null) {
 379             dsl.dragOver(dsde);
 380         }
 381         getDragSource().processDragOver(dsde);
 382 
 383         updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), OVER);
 384     }
 385 
 386     /**
 387      * Calls <code>dragExit</code> on the
 388      * <code>DragSourceListener</code>s registered with this
 389      * <code>DragSourceContext</code> and with the associated
 390      * <code>DragSource</code>, and passes them the specified
 391      * <code>DragSourceEvent</code>.
 392      *
 393      * @param dse the <code>DragSourceEvent</code>
 394      */
 395     public void dragExit(DragSourceEvent dse) {
 396         DragSourceListener dsl = listener;
 397         if (dsl != null) {
 398             dsl.dragExit(dse);
 399         }
 400         getDragSource().processDragExit(dse);
 401 
 402         updateCurrentCursor(DnDConstants.ACTION_NONE, DnDConstants.ACTION_NONE, DEFAULT);
 403     }
 404 
 405     /**
 406      * Calls <code>dropActionChanged</code> on the
 407      * <code>DragSourceListener</code>s registered with this
 408      * <code>DragSourceContext</code> and with the associated
 409      * <code>DragSource</code>, and passes them the specified
 410      * <code>DragSourceDragEvent</code>.
 411      *
 412      * @param dsde the <code>DragSourceDragEvent</code>
 413      */
 414     public void dropActionChanged(DragSourceDragEvent dsde) {
 415         DragSourceListener dsl = listener;
 416         if (dsl != null) {
 417             dsl.dropActionChanged(dsde);
 418         }
 419         getDragSource().processDropActionChanged(dsde);
 420 
 421         updateCurrentCursor(getSourceActions(), dsde.getTargetActions(), CHANGED);
 422     }
 423 
 424     /**
 425      * Calls <code>dragDropEnd</code> on the
 426      * <code>DragSourceListener</code>s registered with this
 427      * <code>DragSourceContext</code> and with the associated
 428      * <code>DragSource</code>, and passes them the specified
 429      * <code>DragSourceDropEvent</code>.
 430      *
 431      * @param dsde the <code>DragSourceDropEvent</code>
 432      */
 433     public void dragDropEnd(DragSourceDropEvent dsde) {
 434         DragSourceListener dsl = listener;
 435         if (dsl != null) {
 436             dsl.dragDropEnd(dsde);
 437         }
 438         getDragSource().processDragDropEnd(dsde);
 439     }
 440 
 441     /**
 442      * Calls <code>dragMouseMoved</code> on the
 443      * <code>DragSourceMotionListener</code>s registered with the
 444      * <code>DragSource</code> associated with this
 445      * <code>DragSourceContext</code>, and them passes the specified
 446      * <code>DragSourceDragEvent</code>.
 447      *
 448      * @param dsde the <code>DragSourceDragEvent</code>
 449      * @since 1.4
 450      */
 451     public void dragMouseMoved(DragSourceDragEvent dsde) {
 452         getDragSource().processDragMouseMoved(dsde);
 453     }
 454 
 455     /**
 456      * Returns the <code>Transferable</code> associated with
 457      * this <code>DragSourceContext</code>.
 458      *
 459      * @return the <code>Transferable</code>
 460      */
 461     public Transferable getTransferable() { return transferable; }
 462 
 463     /**
 464      * If the default drag cursor behavior is active, this method
 465      * sets the default drag cursor for the specified actions
 466      * supported by the drag source, the drop target action,
 467      * and status, otherwise this method does nothing.
 468      *
 469      * @param sourceAct the actions supported by the drag source
 470      * @param targetAct the drop target action
 471      * @param status one of the fields <code>DEFAULT</code>,
 472      *               <code>ENTER</code>, <code>OVER</code>,
 473      *               <code>CHANGED</code>
 474      */
 475     @SuppressWarnings("fallthrough")
 476     protected synchronized void updateCurrentCursor(int sourceAct, int targetAct, int status) {
 477 
 478         // if the cursor has been previously set then don't do any defaults
 479         // processing.
 480 
 481         if (useCustomCursor) {
 482             return;
 483         }
 484 
 485         // do defaults processing
 486 
 487         Cursor c = null;
 488 
 489         switch (status) {
 490             default:
 491                 targetAct = DnDConstants.ACTION_NONE;
 492             case ENTER:
 493             case OVER:
 494             case CHANGED:
 495                 int    ra = sourceAct & targetAct;
 496 
 497                 if (ra == DnDConstants.ACTION_NONE) { // no drop possible
 498                     if ((sourceAct & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
 499                         c = DragSource.DefaultLinkNoDrop;
 500                     else if ((sourceAct & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
 501                         c = DragSource.DefaultMoveNoDrop;
 502                     else
 503                         c = DragSource.DefaultCopyNoDrop;
 504                 } else { // drop possible
 505                     if ((ra & DnDConstants.ACTION_LINK) == DnDConstants.ACTION_LINK)
 506                         c = DragSource.DefaultLinkDrop;
 507                     else if ((ra & DnDConstants.ACTION_MOVE) == DnDConstants.ACTION_MOVE)
 508                         c = DragSource.DefaultMoveDrop;
 509                     else
 510                         c = DragSource.DefaultCopyDrop;
 511                 }
 512         }
 513 
 514         setCursorImpl(c);
 515     }
 516 
 517     private void setCursorImpl(Cursor c) {
 518         if (cursor == null || !cursor.equals(c)) {
 519             cursor = c;
 520             if (peer != null) peer.setCursor(cursor);
 521         }
 522     }
 523 
 524     /**
 525      * Serializes this <code>DragSourceContext</code>. This method first
 526      * performs default serialization. Next, this object's
 527      * <code>Transferable</code> is written out if and only if it can be
 528      * serialized. If not, <code>null</code> is written instead. In this case,
 529      * a <code>DragSourceContext</code> created from the resulting deserialized
 530      * stream will contain a dummy <code>Transferable</code> which supports no
 531      * <code>DataFlavor</code>s. Finally, this object's
 532      * <code>DragSourceListener</code> is written out if and only if it can be
 533      * serialized. If not, <code>null</code> is written instead.
 534      *
 535      * @serialData The default serializable fields, in alphabetical order,
 536      *             followed by either a <code>Transferable</code> instance, or
 537      *             <code>null</code>, followed by either a
 538      *             <code>DragSourceListener</code> instance, or
 539      *             <code>null</code>.
 540      * @since 1.4
 541      */
 542     private void writeObject(ObjectOutputStream s) throws IOException {
 543         s.defaultWriteObject();
 544 
 545         s.writeObject(SerializationTester.test(transferable)
 546                       ? transferable : null);
 547         s.writeObject(SerializationTester.test(listener)
 548                       ? listener : null);
 549     }
 550 
 551     /**
 552      * Deserializes this <code>DragSourceContext</code>. This method first
 553      * performs default deserialization for all non-<code>transient</code>
 554      * fields. This object's <code>Transferable</code> and
 555      * <code>DragSourceListener</code> are then deserialized as well by using
 556      * the next two objects in the stream. If the resulting
 557      * <code>Transferable</code> is <code>null</code>, this object's
 558      * <code>Transferable</code> is set to a dummy <code>Transferable</code>
 559      * which supports no <code>DataFlavor</code>s.
 560      *
 561      * @since 1.4
 562      */
 563     private void readObject(ObjectInputStream s)
 564         throws ClassNotFoundException, IOException
 565     {
 566         ObjectInputStream.GetField f = s.readFields();
 567 
 568         DragGestureEvent newTrigger = (DragGestureEvent)f.get("trigger", null);
 569         if (newTrigger == null) {
 570             throw new InvalidObjectException("Null trigger");
 571         }
 572         if (newTrigger.getDragSource() == null) {
 573             throw new InvalidObjectException("Null DragSource");
 574         }
 575         if (newTrigger.getComponent() == null) {
 576             throw new InvalidObjectException("Null trigger component");
 577         }
 578 
 579         int newSourceActions = f.get("sourceActions", 0)
 580                 & (DnDConstants.ACTION_COPY_OR_MOVE | DnDConstants.ACTION_LINK);
 581         if (newSourceActions == DnDConstants.ACTION_NONE) {
 582             throw new InvalidObjectException("Invalid source actions");
 583         }
 584         int triggerActions = newTrigger.getDragAction();
 585         if (triggerActions != DnDConstants.ACTION_COPY &&
 586                 triggerActions != DnDConstants.ACTION_MOVE &&
 587                 triggerActions != DnDConstants.ACTION_LINK) {
 588             throw new InvalidObjectException("No drag action");
 589         }
 590         trigger = newTrigger;
 591 
 592         cursor = (Cursor)f.get("cursor", null);
 593         useCustomCursor = f.get("useCustomCursor", false);
 594         sourceActions = newSourceActions;
 595 
 596         transferable = (Transferable)s.readObject();
 597         listener = (DragSourceListener)s.readObject();
 598 
 599         // Implementation assumes 'transferable' is never null.
 600         if (transferable == null) {
 601             if (emptyTransferable == null) {
 602                 emptyTransferable = new Transferable() {
 603                         public DataFlavor[] getTransferDataFlavors() {
 604                             return new DataFlavor[0];
 605                         }
 606                         public boolean isDataFlavorSupported(DataFlavor flavor)
 607                         {
 608                             return false;
 609                         }
 610                         public Object getTransferData(DataFlavor flavor)
 611                             throws UnsupportedFlavorException
 612                         {
 613                             throw new UnsupportedFlavorException(flavor);
 614                         }
 615                     };
 616             }
 617             transferable = emptyTransferable;
 618         }
 619     }
 620 
 621     private static Transferable emptyTransferable;
 622 
 623     /*
 624      * fields
 625      */
 626 
 627     private transient DragSourceContextPeer peer;
 628 
 629     /**
 630      * The event which triggered the start of the drag.
 631      *
 632      * @serial
 633      */
 634     private DragGestureEvent    trigger;
 635 
 636     /**
 637      * The current drag cursor.
 638      *
 639      * @serial
 640      */
 641     private Cursor              cursor;
 642 
 643     private transient Transferable      transferable;
 644 
 645     private transient DragSourceListener    listener;
 646 
 647     /**
 648      * <code>true</code> if the custom drag cursor is used instead of the
 649      * default one.
 650      *
 651      * @serial
 652      */
 653     private boolean useCustomCursor;
 654 
 655     /**
 656      * A bitwise mask of <code>DnDConstants</code> that represents the set of
 657      * drop actions supported by the drag source for the drag operation associated
 658      * with this <code>DragSourceContext.</code>
 659      *
 660      * @serial
 661      */
 662     private int sourceActions;
 663 }