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