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