1 /*
   2  * Copyright (c) 2000, 2011, 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 package javax.swing;
  26 
  27 import java.awt.*;
  28 import java.awt.event.*;
  29 import java.awt.datatransfer.*;
  30 import java.awt.dnd.*;
  31 import java.awt.peer.ComponentPeer;
  32 import java.beans.*;
  33 import java.lang.reflect.*;
  34 import java.io.*;
  35 import java.util.TooManyListenersException;
  36 import javax.swing.plaf.UIResource;
  37 import javax.swing.event.*;
  38 import javax.swing.text.JTextComponent;
  39 
  40 import sun.reflect.misc.MethodUtil;
  41 import sun.swing.SwingUtilities2;
  42 import sun.awt.AppContext;
  43 import sun.swing.*;
  44 import sun.awt.SunToolkit;
  45 
  46 import java.security.AccessController;
  47 import java.security.PrivilegedAction;
  48 
  49 import java.security.AccessControlContext;
  50 import java.security.ProtectionDomain;
  51 import sun.misc.SharedSecrets;
  52 import sun.misc.JavaSecurityAccess;
  53 
  54 import sun.awt.AWTAccessor;
  55 
  56 /**
  57  * This class is used to handle the transfer of a <code>Transferable</code>
  58  * to and from Swing components.  The <code>Transferable</code> is used to
  59  * represent data that is exchanged via a cut, copy, or paste
  60  * to/from a clipboard.  It is also used in drag-and-drop operations
  61  * to represent a drag from a component, and a drop to a component.
  62  * Swing provides functionality that automatically supports cut, copy,
  63  * and paste keyboard bindings that use the functionality provided by
  64  * an implementation of this class.  Swing also provides functionality
  65  * that automatically supports drag and drop that uses the functionality
  66  * provided by an implementation of this class.  The Swing developer can
  67  * concentrate on specifying the semantics of a transfer primarily by setting
  68  * the <code>transferHandler</code> property on a Swing component.
  69  * <p>
  70  * This class is implemented to provide a default behavior of transferring
  71  * a component property simply by specifying the name of the property in
  72  * the constructor.  For example, to transfer the foreground color from
  73  * one component to another either via the clipboard or a drag and drop operation
  74  * a <code>TransferHandler</code> can be constructed with the string "foreground".  The
  75  * built in support will use the color returned by <code>getForeground</code> as the source
  76  * of the transfer, and <code>setForeground</code> for the target of a transfer.
  77  * <p>
  78  * Please see
  79  * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/dnd.html">
  80  * How to Use Drag and Drop and Data Transfer</a>,
  81  * a section in <em>The Java Tutorial</em>, for more information.
  82  *
  83  *
  84  * @author Timothy Prinzing
  85  * @author Shannon Hickey
  86  * @since 1.4
  87  */
  88 @SuppressWarnings("serial")
  89 public class TransferHandler implements Serializable {
  90 
  91     /**
  92      * An <code>int</code> representing no transfer action.
  93      */
  94     public static final int NONE = DnDConstants.ACTION_NONE;
  95 
  96     /**
  97      * An <code>int</code> representing a &quot;copy&quot; transfer action.
  98      * This value is used when data is copied to a clipboard
  99      * or copied elsewhere in a drag and drop operation.
 100      */
 101     public static final int COPY = DnDConstants.ACTION_COPY;
 102 
 103     /**
 104      * An <code>int</code> representing a &quot;move&quot; transfer action.
 105      * This value is used when data is moved to a clipboard (i.e. a cut)
 106      * or moved elsewhere in a drag and drop operation.
 107      */
 108     public static final int MOVE = DnDConstants.ACTION_MOVE;
 109 
 110     /**
 111      * An <code>int</code> representing a source action capability of either
 112      * &quot;copy&quot; or &quot;move&quot;.
 113      */
 114     public static final int COPY_OR_MOVE = DnDConstants.ACTION_COPY_OR_MOVE;
 115 
 116     /**
 117      * An <code>int</code> representing a &quot;link&quot; transfer action.
 118      * This value is used to specify that data should be linked in a drag
 119      * and drop operation.
 120      *
 121      * @see java.awt.dnd.DnDConstants#ACTION_LINK
 122      * @since 1.6
 123      */
 124     public static final int LINK = DnDConstants.ACTION_LINK;
 125 
 126     /**
 127      * An interface to tag things with a {@code getTransferHandler} method.
 128      */
 129     interface HasGetTransferHandler {
 130 
 131         /** Returns the {@code TransferHandler}.
 132          *
 133          * @return The {@code TransferHandler} or {@code null}
 134          */
 135         public TransferHandler getTransferHandler();
 136     }
 137 
 138     /**
 139      * Represents a location where dropped data should be inserted.
 140      * This is a base class that only encapsulates a point.
 141      * Components supporting drop may provide subclasses of this
 142      * containing more information.
 143      * <p>
 144      * Developers typically shouldn't create instances of, or extend, this
 145      * class. Instead, these are something provided by the DnD
 146      * implementation by <code>TransferSupport</code> instances and by
 147      * components with a <code>getDropLocation()</code> method.
 148      *
 149      * @see javax.swing.TransferHandler.TransferSupport#getDropLocation
 150      * @since 1.6
 151      */
 152     public static class DropLocation {
 153         private final Point dropPoint;
 154 
 155         /**
 156          * Constructs a drop location for the given point.
 157          *
 158          * @param dropPoint the drop point, representing the mouse's
 159          *        current location within the component.
 160          * @throws IllegalArgumentException if the point
 161          *         is <code>null</code>
 162          */
 163         protected DropLocation(Point dropPoint) {
 164             if (dropPoint == null) {
 165                 throw new IllegalArgumentException("Point cannot be null");
 166             }
 167 
 168             this.dropPoint = new Point(dropPoint);
 169         }
 170 
 171         /**
 172          * Returns the drop point, representing the mouse's
 173          * current location within the component.
 174          *
 175          * @return the drop point.
 176          */
 177         public final Point getDropPoint() {
 178             return new Point(dropPoint);
 179         }
 180 
 181         /**
 182          * Returns a string representation of this drop location.
 183          * This method is intended to be used for debugging purposes,
 184          * and the content and format of the returned string may vary
 185          * between implementations.
 186          *
 187          * @return a string representation of this drop location
 188          */
 189         public String toString() {
 190             return getClass().getName() + "[dropPoint=" + dropPoint + "]";
 191         }
 192     };
 193 
 194     /**
 195      * This class encapsulates all relevant details of a clipboard
 196      * or drag and drop transfer, and also allows for customizing
 197      * aspects of the drag and drop experience.
 198      * <p>
 199      * The main purpose of this class is to provide the information
 200      * needed by a developer to determine the suitability of a
 201      * transfer or to import the data contained within. But it also
 202      * doubles as a controller for customizing properties during drag
 203      * and drop, such as whether or not to show the drop location,
 204      * and which drop action to use.
 205      * <p>
 206      * Developers typically need not create instances of this
 207      * class. Instead, they are something provided by the DnD
 208      * implementation to certain methods in <code>TransferHandler</code>.
 209      *
 210      * @see #canImport(TransferHandler.TransferSupport)
 211      * @see #importData(TransferHandler.TransferSupport)
 212      * @since 1.6
 213      */
 214     public final static class TransferSupport {
 215         private boolean isDrop;
 216         private Component component;
 217 
 218         private boolean showDropLocationIsSet;
 219         private boolean showDropLocation;
 220 
 221         private int dropAction = -1;
 222 
 223         /**
 224          * The source is a {@code DropTargetDragEvent} or
 225          * {@code DropTargetDropEvent} for drops,
 226          * and a {@code Transferable} otherwise
 227          */
 228         private Object source;
 229 
 230         private DropLocation dropLocation;
 231 
 232         /**
 233          * Create a <code>TransferSupport</code> with <code>isDrop()</code>
 234          * <code>true</code> for the given component, event, and index.
 235          *
 236          * @param component the target component
 237          * @param event a <code>DropTargetEvent</code>
 238          */
 239         private TransferSupport(Component component,
 240                              DropTargetEvent event) {
 241 
 242             isDrop = true;
 243             setDNDVariables(component, event);
 244         }
 245 
 246         /**
 247          * Create a <code>TransferSupport</code> with <code>isDrop()</code>
 248          * <code>false</code> for the given component and
 249          * <code>Transferable</code>.
 250          *
 251          * @param component the target component
 252          * @param transferable the transferable
 253          * @throws NullPointerException if either parameter
 254          *         is <code>null</code>
 255          */
 256         public TransferSupport(Component component, Transferable transferable) {
 257             if (component == null) {
 258                 throw new NullPointerException("component is null");
 259             }
 260 
 261             if (transferable == null) {
 262                 throw new NullPointerException("transferable is null");
 263             }
 264 
 265             isDrop = false;
 266             this.component = component;
 267             this.source = transferable;
 268         }
 269 
 270         /**
 271          * Allows for a single instance to be reused during DnD.
 272          *
 273          * @param component the target component
 274          * @param event a <code>DropTargetEvent</code>
 275          */
 276         private void setDNDVariables(Component component,
 277                                      DropTargetEvent event) {
 278 
 279             assert isDrop;
 280 
 281             this.component = component;
 282             this.source = event;
 283             dropLocation = null;
 284             dropAction = -1;
 285             showDropLocationIsSet = false;
 286 
 287             if (source == null) {
 288                 return;
 289             }
 290 
 291             assert source instanceof DropTargetDragEvent ||
 292                    source instanceof DropTargetDropEvent;
 293 
 294             Point p = source instanceof DropTargetDragEvent
 295                           ? ((DropTargetDragEvent)source).getLocation()
 296                           : ((DropTargetDropEvent)source).getLocation();
 297 
 298             if (SunToolkit.isInstanceOf(component, "javax.swing.text.JTextComponent")) {
 299                 dropLocation = SwingAccessor.getJTextComponentAccessor().
 300                                    dropLocationForPoint((JTextComponent)component, p);
 301             } else if (component instanceof JComponent) {
 302                 dropLocation = ((JComponent)component).dropLocationForPoint(p);
 303             }
 304 
 305             /*
 306              * The drop location may be null at this point if the component
 307              * doesn't return custom drop locations. In this case, a point-only
 308              * drop location will be created lazily when requested.
 309              */
 310         }
 311 
 312         /**
 313          * Returns whether or not this <code>TransferSupport</code>
 314          * represents a drop operation.
 315          *
 316          * @return <code>true</code> if this is a drop operation,
 317          *         <code>false</code> otherwise.
 318          */
 319         public boolean isDrop() {
 320             return isDrop;
 321         }
 322 
 323         /**
 324          * Returns the target component of this transfer.
 325          *
 326          * @return the target component
 327          */
 328         public Component getComponent() {
 329             return component;
 330         }
 331 
 332         /**
 333          * Checks that this is a drop and throws an
 334          * {@code IllegalStateException} if it isn't.
 335          *
 336          * @throws IllegalStateException if {@code isDrop} is false.
 337          */
 338         private void assureIsDrop() {
 339             if (!isDrop) {
 340                 throw new IllegalStateException("Not a drop");
 341             }
 342         }
 343 
 344         /**
 345          * Returns the current (non-{@code null}) drop location for the component,
 346          * when this {@code TransferSupport} represents a drop.
 347          * <p>
 348          * Note: For components with built-in drop support, this location
 349          * will be a subclass of {@code DropLocation} of the same type
 350          * returned by that component's {@code getDropLocation} method.
 351          * <p>
 352          * This method is only for use with drag and drop transfers.
 353          * Calling it when {@code isDrop()} is {@code false} results
 354          * in an {@code IllegalStateException}.
 355          *
 356          * @return the drop location
 357          * @throws IllegalStateException if this is not a drop
 358          * @see #isDrop()
 359          */
 360         public DropLocation getDropLocation() {
 361             assureIsDrop();
 362 
 363             if (dropLocation == null) {
 364                 /*
 365                  * component didn't give us a custom drop location,
 366                  * so lazily create a point-only location
 367                  */
 368                 Point p = source instanceof DropTargetDragEvent
 369                               ? ((DropTargetDragEvent)source).getLocation()
 370                               : ((DropTargetDropEvent)source).getLocation();
 371 
 372                 dropLocation = new DropLocation(p);
 373             }
 374 
 375             return dropLocation;
 376         }
 377 
 378         /**
 379          * Sets whether or not the drop location should be visually indicated
 380          * for the transfer - which must represent a drop. This is applicable to
 381          * those components that automatically
 382          * show the drop location when appropriate during a drag and drop
 383          * operation). By default, the drop location is shown only when the
 384          * {@code TransferHandler} has said it can accept the import represented
 385          * by this {@code TransferSupport}. With this method you can force the
 386          * drop location to always be shown, or always not be shown.
 387          * <p>
 388          * This method is only for use with drag and drop transfers.
 389          * Calling it when {@code isDrop()} is {@code false} results
 390          * in an {@code IllegalStateException}.
 391          *
 392          * @param showDropLocation whether or not to indicate the drop location
 393          * @throws IllegalStateException if this is not a drop
 394          * @see #isDrop()
 395          */
 396         public void setShowDropLocation(boolean showDropLocation) {
 397             assureIsDrop();
 398 
 399             this.showDropLocation = showDropLocation;
 400             this.showDropLocationIsSet = true;
 401         }
 402 
 403         /**
 404          * Sets the drop action for the transfer - which must represent a drop
 405          * - to the given action,
 406          * instead of the default user drop action. The action must be
 407          * supported by the source's drop actions, and must be one
 408          * of {@code COPY}, {@code MOVE} or {@code LINK}.
 409          * <p>
 410          * This method is only for use with drag and drop transfers.
 411          * Calling it when {@code isDrop()} is {@code false} results
 412          * in an {@code IllegalStateException}.
 413          *
 414          * @param dropAction the drop action
 415          * @throws IllegalStateException if this is not a drop
 416          * @throws IllegalArgumentException if an invalid action is specified
 417          * @see #getDropAction
 418          * @see #getUserDropAction
 419          * @see #getSourceDropActions
 420          * @see #isDrop()
 421          */
 422         public void setDropAction(int dropAction) {
 423             assureIsDrop();
 424 
 425             int action = dropAction & getSourceDropActions();
 426 
 427             if (!(action == COPY || action == MOVE || action == LINK)) {
 428                 throw new IllegalArgumentException("unsupported drop action: " + dropAction);
 429             }
 430 
 431             this.dropAction = dropAction;
 432         }
 433 
 434         /**
 435          * Returns the action chosen for the drop, when this
 436          * {@code TransferSupport} represents a drop.
 437          * <p>
 438          * Unless explicitly chosen by way of {@code setDropAction},
 439          * this returns the user drop action provided by
 440          * {@code getUserDropAction}.
 441          * <p>
 442          * You may wish to query this in {@code TransferHandler}'s
 443          * {@code importData} method to customize processing based
 444          * on the action.
 445          * <p>
 446          * This method is only for use with drag and drop transfers.
 447          * Calling it when {@code isDrop()} is {@code false} results
 448          * in an {@code IllegalStateException}.
 449          *
 450          * @return the action chosen for the drop
 451          * @throws IllegalStateException if this is not a drop
 452          * @see #setDropAction
 453          * @see #getUserDropAction
 454          * @see #isDrop()
 455          */
 456         public int getDropAction() {
 457             return dropAction == -1 ? getUserDropAction() : dropAction;
 458         }
 459 
 460         /**
 461          * Returns the user drop action for the drop, when this
 462          * {@code TransferSupport} represents a drop.
 463          * <p>
 464          * The user drop action is chosen for a drop as described in the
 465          * documentation for {@link java.awt.dnd.DropTargetDragEvent} and
 466          * {@link java.awt.dnd.DropTargetDropEvent}. A different action
 467          * may be chosen as the drop action by way of the {@code setDropAction}
 468          * method.
 469          * <p>
 470          * You may wish to query this in {@code TransferHandler}'s
 471          * {@code canImport} method when determining the suitability of a
 472          * drop or when deciding on a drop action to explicitly choose.
 473          * <p>
 474          * This method is only for use with drag and drop transfers.
 475          * Calling it when {@code isDrop()} is {@code false} results
 476          * in an {@code IllegalStateException}.
 477          *
 478          * @return the user drop action
 479          * @throws IllegalStateException if this is not a drop
 480          * @see #setDropAction
 481          * @see #getDropAction
 482          * @see #isDrop()
 483          */
 484         public int getUserDropAction() {
 485             assureIsDrop();
 486 
 487             return (source instanceof DropTargetDragEvent)
 488                 ? ((DropTargetDragEvent)source).getDropAction()
 489                 : ((DropTargetDropEvent)source).getDropAction();
 490         }
 491 
 492         /**
 493          * Returns the drag source's supported drop actions, when this
 494          * {@code TransferSupport} represents a drop.
 495          * <p>
 496          * The source actions represent the set of actions supported by the
 497          * source of this transfer, and are represented as some bitwise-OR
 498          * combination of {@code COPY}, {@code MOVE} and {@code LINK}.
 499          * You may wish to query this in {@code TransferHandler}'s
 500          * {@code canImport} method when determining the suitability of a drop
 501          * or when deciding on a drop action to explicitly choose. To determine
 502          * if a particular action is supported by the source, bitwise-AND
 503          * the action with the source drop actions, and then compare the result
 504          * against the original action. For example:
 505          * <pre>
 506          * boolean copySupported = (COPY & getSourceDropActions()) == COPY;
 507          * </pre>
 508          * <p>
 509          * This method is only for use with drag and drop transfers.
 510          * Calling it when {@code isDrop()} is {@code false} results
 511          * in an {@code IllegalStateException}.
 512          *
 513          * @return the drag source's supported drop actions
 514          * @throws IllegalStateException if this is not a drop
 515          * @see #isDrop()
 516          */
 517         public int getSourceDropActions() {
 518             assureIsDrop();
 519 
 520             return (source instanceof DropTargetDragEvent)
 521                 ? ((DropTargetDragEvent)source).getSourceActions()
 522                 : ((DropTargetDropEvent)source).getSourceActions();
 523         }
 524 
 525         /**
 526          * Returns the data flavors for this transfer.
 527          *
 528          * @return the data flavors for this transfer
 529          */
 530         public DataFlavor[] getDataFlavors() {
 531             if (isDrop) {
 532                 if (source instanceof DropTargetDragEvent) {
 533                     return ((DropTargetDragEvent)source).getCurrentDataFlavors();
 534                 } else {
 535                     return ((DropTargetDropEvent)source).getCurrentDataFlavors();
 536                 }
 537             }
 538 
 539             return ((Transferable)source).getTransferDataFlavors();
 540         }
 541 
 542         /**
 543          * Returns whether or not the given data flavor is supported.
 544          *
 545          * @param df the <code>DataFlavor</code> to test
 546          * @return whether or not the given flavor is supported.
 547          */
 548         public boolean isDataFlavorSupported(DataFlavor df) {
 549             if (isDrop) {
 550                 if (source instanceof DropTargetDragEvent) {
 551                     return ((DropTargetDragEvent)source).isDataFlavorSupported(df);
 552                 } else {
 553                     return ((DropTargetDropEvent)source).isDataFlavorSupported(df);
 554                 }
 555             }
 556 
 557             return ((Transferable)source).isDataFlavorSupported(df);
 558         }
 559 
 560         /**
 561          * Returns the <code>Transferable</code> associated with this transfer.
 562          * <p>
 563          * Note: Unless it is necessary to fetch the <code>Transferable</code>
 564          * directly, use one of the other methods on this class to inquire about
 565          * the transfer. This may perform better than fetching the
 566          * <code>Transferable</code> and asking it directly.
 567          *
 568          * @return the <code>Transferable</code> associated with this transfer
 569          */
 570         public Transferable getTransferable() {
 571             if (isDrop) {
 572                 if (source instanceof DropTargetDragEvent) {
 573                     return ((DropTargetDragEvent)source).getTransferable();
 574                 } else {
 575                     return ((DropTargetDropEvent)source).getTransferable();
 576                 }
 577             }
 578 
 579             return (Transferable)source;
 580         }
 581     }
 582 
 583 
 584     /**
 585      * Returns an {@code Action} that performs cut operations to the
 586      * clipboard. When performed, this action operates on the {@code JComponent}
 587      * source of the {@code ActionEvent} by invoking {@code exportToClipboard},
 588      * with a {@code MOVE} action, on the component's {@code TransferHandler}.
 589      *
 590      * @return an {@code Action} for performing cuts to the clipboard
 591      */
 592     public static Action getCutAction() {
 593         return cutAction;
 594     }
 595 
 596     /**
 597      * Returns an {@code Action} that performs copy operations to the
 598      * clipboard. When performed, this action operates on the {@code JComponent}
 599      * source of the {@code ActionEvent} by invoking {@code exportToClipboard},
 600      * with a {@code COPY} action, on the component's {@code TransferHandler}.
 601      *
 602      * @return an {@code Action} for performing copies to the clipboard
 603      */
 604     public static Action getCopyAction() {
 605         return copyAction;
 606     }
 607 
 608     /**
 609      * Returns an {@code Action} that performs paste operations from the
 610      * clipboard. When performed, this action operates on the {@code JComponent}
 611      * source of the {@code ActionEvent} by invoking {@code importData},
 612      * with the clipboard contents, on the component's {@code TransferHandler}.
 613      *
 614      * @return an {@code Action} for performing pastes from the clipboard
 615      */
 616     public static Action getPasteAction() {
 617         return pasteAction;
 618     }
 619 
 620 
 621     /**
 622      * Constructs a transfer handler that can transfer a Java Bean property
 623      * from one component to another via the clipboard or a drag and drop
 624      * operation.
 625      *
 626      * @param property  the name of the property to transfer; this can
 627      *  be <code>null</code> if there is no property associated with the transfer
 628      *  handler (a subclass that performs some other kind of transfer, for example)
 629      */
 630     public TransferHandler(String property) {
 631         propertyName = property;
 632     }
 633 
 634     /**
 635      * Convenience constructor for subclasses.
 636      */
 637     protected TransferHandler() {
 638         this(null);
 639     }
 640 
 641 
 642     /**
 643      * image for the {@code startDrag} method
 644      *
 645      * @see java.awt.dnd.DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
 646      */
 647     private  Image dragImage;
 648 
 649     /**
 650      * anchor offset for the {@code startDrag} method
 651      *
 652      * @see java.awt.dnd.DragGestureEvent#startDrag(Cursor dragCursor, Image dragImage, Point imageOffset, Transferable transferable, DragSourceListener dsl)
 653      */
 654     private  Point dragImageOffset;
 655 
 656     /**
 657      * Sets the drag image parameter. The image has to be prepared
 658      * for rendering by the moment of the call. The image is stored
 659      * by reference because of some performance reasons.
 660      *
 661      * @param img an image to drag
 662      */
 663     public void setDragImage(Image img) {
 664         dragImage = img;
 665     }
 666 
 667     /**
 668      * Returns the drag image. If there is no image to drag,
 669      * the returned value is {@code null}.
 670      *
 671      * @return the reference to the drag image
 672      */
 673     public Image getDragImage() {
 674         return dragImage;
 675     }
 676 
 677     /**
 678      * Sets an anchor offset for the image to drag.
 679      * It can not be {@code null}.
 680      *
 681      * @param p a {@code Point} object that corresponds
 682      * to coordinates of an anchor offset of the image
 683      * relative to the upper left corner of the image
 684      */
 685     public void setDragImageOffset(Point p) {
 686         dragImageOffset = new Point(p);
 687     }
 688 
 689     /**
 690      * Returns an anchor offset for the image to drag.
 691      *
 692      * @return a {@code Point} object that corresponds
 693      * to coordinates of an anchor offset of the image
 694      * relative to the upper left corner of the image.
 695      * The point {@code (0,0)} returns by default.
 696      */
 697     public Point getDragImageOffset() {
 698         if (dragImageOffset == null) {
 699             return new Point(0,0);
 700         }
 701         return new Point(dragImageOffset);
 702     }
 703 
 704     /**
 705      * Causes the Swing drag support to be initiated.  This is called by
 706      * the various UI implementations in the <code>javax.swing.plaf.basic</code>
 707      * package if the dragEnabled property is set on the component.
 708      * This can be called by custom UI
 709      * implementations to use the Swing drag support.  This method can also be called
 710      * by a Swing extension written as a subclass of <code>JComponent</code>
 711      * to take advantage of the Swing drag support.
 712      * <p>
 713      * The transfer <em>will not necessarily</em> have been completed at the
 714      * return of this call (i.e. the call does not block waiting for the drop).
 715      * The transfer will take place through the Swing implementation of the
 716      * <code>java.awt.dnd</code> mechanism, requiring no further effort
 717      * from the developer. The <code>exportDone</code> method will be called
 718      * when the transfer has completed.
 719      *
 720      * @param comp  the component holding the data to be transferred;
 721      *              provided to enable sharing of <code>TransferHandler</code>s
 722      * @param e     the event that triggered the transfer
 723      * @param action the transfer action initially requested;
 724      *               either {@code COPY}, {@code MOVE} or {@code LINK};
 725      *               the DnD system may change the action used during the
 726      *               course of the drag operation
 727      */
 728     public void exportAsDrag(JComponent comp, InputEvent e, int action) {
 729         int srcActions = getSourceActions(comp);
 730 
 731         // only mouse events supported for drag operations
 732         if (!(e instanceof MouseEvent)
 733                 // only support known actions
 734                 || !(action == COPY || action == MOVE || action == LINK)
 735                 // only support valid source actions
 736                 || (srcActions & action) == 0) {
 737 
 738             action = NONE;
 739         }
 740 
 741         if (action != NONE && !GraphicsEnvironment.isHeadless()) {
 742             if (recognizer == null) {
 743                 recognizer = new SwingDragGestureRecognizer(new DragHandler());
 744             }
 745             recognizer.gestured(comp, (MouseEvent)e, srcActions, action);
 746         } else {
 747             exportDone(comp, null, NONE);
 748         }
 749     }
 750 
 751     /**
 752      * Causes a transfer from the given component to the
 753      * given clipboard.  This method is called by the default cut and
 754      * copy actions registered in a component's action map.
 755      * <p>
 756      * The transfer will take place using the <code>java.awt.datatransfer</code>
 757      * mechanism, requiring no further effort from the developer. Any data
 758      * transfer <em>will</em> be complete and the <code>exportDone</code>
 759      * method will be called with the action that occurred, before this method
 760      * returns. Should the clipboard be unavailable when attempting to place
 761      * data on it, the <code>IllegalStateException</code> thrown by
 762      * {@link Clipboard#setContents(Transferable, ClipboardOwner)} will
 763      * be propogated through this method. However,
 764      * <code>exportDone</code> will first be called with an action
 765      * of <code>NONE</code> for consistency.
 766      *
 767      * @param comp  the component holding the data to be transferred;
 768      *              provided to enable sharing of <code>TransferHandler</code>s
 769      * @param clip  the clipboard to transfer the data into
 770      * @param action the transfer action requested; this should
 771      *  be a value of either <code>COPY</code> or <code>MOVE</code>;
 772      *  the operation performed is the intersection  of the transfer
 773      *  capabilities given by getSourceActions and the requested action;
 774      *  the intersection may result in an action of <code>NONE</code>
 775      *  if the requested action isn't supported
 776      * @throws IllegalStateException if the clipboard is currently unavailable
 777      * @see Clipboard#setContents(Transferable, ClipboardOwner)
 778      */
 779     public void exportToClipboard(JComponent comp, Clipboard clip, int action)
 780                                                   throws IllegalStateException {
 781 
 782         if ((action == COPY || action == MOVE)
 783                 && (getSourceActions(comp) & action) != 0) {
 784 
 785             Transferable t = createTransferable(comp);
 786             if (t != null) {
 787                 try {
 788                     clip.setContents(t, null);
 789                     exportDone(comp, t, action);
 790                     return;
 791                 } catch (IllegalStateException ise) {
 792                     exportDone(comp, t, NONE);
 793                     throw ise;
 794                 }
 795             }
 796         }
 797 
 798         exportDone(comp, null, NONE);
 799     }
 800 
 801     /**
 802      * Causes a transfer to occur from a clipboard or a drag and
 803      * drop operation. The <code>Transferable</code> to be
 804      * imported and the component to transfer to are contained
 805      * within the <code>TransferSupport</code>.
 806      * <p>
 807      * While the drag and drop implementation calls {@code canImport}
 808      * to determine the suitability of a transfer before calling this
 809      * method, the implementation of paste does not. As such, it cannot
 810      * be assumed that the transfer is acceptable upon a call to
 811      * this method for paste. It is recommended that {@code canImport} be
 812      * explicitly called to cover this case.
 813      * <p>
 814      * Note: The <code>TransferSupport</code> object passed to this method
 815      * is only valid for the duration of the method call. It is undefined
 816      * what values it may contain after this method returns.
 817      *
 818      * @param support the object containing the details of
 819      *        the transfer, not <code>null</code>.
 820      * @return true if the data was inserted into the component,
 821      *         false otherwise
 822      * @throws NullPointerException if <code>support</code> is {@code null}
 823      * @see #canImport(TransferHandler.TransferSupport)
 824      * @since 1.6
 825      */
 826     public boolean importData(TransferSupport support) {
 827         return support.getComponent() instanceof JComponent
 828             ? importData((JComponent)support.getComponent(), support.getTransferable())
 829             : false;
 830     }
 831 
 832     /**
 833      * Causes a transfer to a component from a clipboard or a
 834      * DND drop operation.  The <code>Transferable</code> represents
 835      * the data to be imported into the component.
 836      * <p>
 837      * Note: Swing now calls the newer version of <code>importData</code>
 838      * that takes a <code>TransferSupport</code>, which in turn calls this
 839      * method (if the component in the {@code TransferSupport} is a
 840      * {@code JComponent}). Developers are encouraged to call and override the
 841      * newer version as it provides more information (and is the only
 842      * version that supports use with a {@code TransferHandler} set directly
 843      * on a {@code JFrame} or other non-{@code JComponent}).
 844      *
 845      * @param comp  the component to receive the transfer;
 846      *              provided to enable sharing of <code>TransferHandler</code>s
 847      * @param t     the data to import
 848      * @return  true if the data was inserted into the component, false otherwise
 849      * @see #importData(TransferHandler.TransferSupport)
 850      */
 851     public boolean importData(JComponent comp, Transferable t) {
 852         PropertyDescriptor prop = getPropertyDescriptor(comp);
 853         if (prop != null) {
 854             Method writer = prop.getWriteMethod();
 855             if (writer == null) {
 856                 // read-only property. ignore
 857                 return false;
 858             }
 859             Class<?>[] params = writer.getParameterTypes();
 860             if (params.length != 1) {
 861                 // zero or more than one argument, ignore
 862                 return false;
 863             }
 864             DataFlavor flavor = getPropertyDataFlavor(params[0], t.getTransferDataFlavors());
 865             if (flavor != null) {
 866                 try {
 867                     Object value = t.getTransferData(flavor);
 868                     Object[] args = { value };
 869                     MethodUtil.invoke(writer, comp, args);
 870                     return true;
 871                 } catch (Exception ex) {
 872                     System.err.println("Invocation failed");
 873                     // invocation code
 874                 }
 875             }
 876         }
 877         return false;
 878     }
 879 
 880     /**
 881      * This method is called repeatedly during a drag and drop operation
 882      * to allow the developer to configure properties of, and to return
 883      * the acceptability of transfers; with a return value of {@code true}
 884      * indicating that the transfer represented by the given
 885      * {@code TransferSupport} (which contains all of the details of the
 886      * transfer) is acceptable at the current time, and a value of {@code false}
 887      * rejecting the transfer.
 888      * <p>
 889      * For those components that automatically display a drop location during
 890      * drag and drop, accepting the transfer, by default, tells them to show
 891      * the drop location. This can be changed by calling
 892      * {@code setShowDropLocation} on the {@code TransferSupport}.
 893      * <p>
 894      * By default, when the transfer is accepted, the chosen drop action is that
 895      * picked by the user via their drag gesture. The developer can override
 896      * this and choose a different action, from the supported source
 897      * actions, by calling {@code setDropAction} on the {@code TransferSupport}.
 898      * <p>
 899      * On every call to {@code canImport}, the {@code TransferSupport} contains
 900      * fresh state. As such, any properties set on it must be set on every
 901      * call. Upon a drop, {@code canImport} is called one final time before
 902      * calling into {@code importData}. Any state set on the
 903      * {@code TransferSupport} during that last call will be available in
 904      * {@code importData}.
 905      * <p>
 906      * This method is not called internally in response to paste operations.
 907      * As such, it is recommended that implementations of {@code importData}
 908      * explicitly call this method for such cases and that this method
 909      * be prepared to return the suitability of paste operations as well.
 910      * <p>
 911      * Note: The <code>TransferSupport</code> object passed to this method
 912      * is only valid for the duration of the method call. It is undefined
 913      * what values it may contain after this method returns.
 914      *
 915      * @param support the object containing the details of
 916      *        the transfer, not <code>null</code>.
 917      * @return <code>true</code> if the import can happen,
 918      *         <code>false</code> otherwise
 919      * @throws NullPointerException if <code>support</code> is {@code null}
 920      * @see #importData(TransferHandler.TransferSupport)
 921      * @see javax.swing.TransferHandler.TransferSupport#setShowDropLocation
 922      * @see javax.swing.TransferHandler.TransferSupport#setDropAction
 923      * @since 1.6
 924      */
 925     public boolean canImport(TransferSupport support) {
 926         return support.getComponent() instanceof JComponent
 927             ? canImport((JComponent)support.getComponent(), support.getDataFlavors())
 928             : false;
 929     }
 930 
 931     /**
 932      * Indicates whether a component will accept an import of the given
 933      * set of data flavors prior to actually attempting to import it.
 934      * <p>
 935      * Note: Swing now calls the newer version of <code>canImport</code>
 936      * that takes a <code>TransferSupport</code>, which in turn calls this
 937      * method (only if the component in the {@code TransferSupport} is a
 938      * {@code JComponent}). Developers are encouraged to call and override the
 939      * newer version as it provides more information (and is the only
 940      * version that supports use with a {@code TransferHandler} set directly
 941      * on a {@code JFrame} or other non-{@code JComponent}).
 942      *
 943      * @param comp  the component to receive the transfer;
 944      *              provided to enable sharing of <code>TransferHandler</code>s
 945      * @param transferFlavors  the data formats available
 946      * @return  true if the data can be inserted into the component, false otherwise
 947      * @see #canImport(TransferHandler.TransferSupport)
 948      */
 949     public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
 950         PropertyDescriptor prop = getPropertyDescriptor(comp);
 951         if (prop != null) {
 952             Method writer = prop.getWriteMethod();
 953             if (writer == null) {
 954                 // read-only property. ignore
 955                 return false;
 956             }
 957             Class<?>[] params = writer.getParameterTypes();
 958             if (params.length != 1) {
 959                 // zero or more than one argument, ignore
 960                 return false;
 961             }
 962             DataFlavor flavor = getPropertyDataFlavor(params[0], transferFlavors);
 963             if (flavor != null) {
 964                 return true;
 965             }
 966         }
 967         return false;
 968     }
 969 
 970     /**
 971      * Returns the type of transfer actions supported by the source;
 972      * any bitwise-OR combination of {@code COPY}, {@code MOVE}
 973      * and {@code LINK}.
 974      * <p>
 975      * Some models are not mutable, so a transfer operation of {@code MOVE}
 976      * should not be advertised in that case. Returning {@code NONE}
 977      * disables transfers from the component.
 978      *
 979      * @param c  the component holding the data to be transferred;
 980      *           provided to enable sharing of <code>TransferHandler</code>s
 981      * @return {@code COPY} if the transfer property can be found,
 982      *          otherwise returns <code>NONE</code>
 983      */
 984     public int getSourceActions(JComponent c) {
 985         PropertyDescriptor prop = getPropertyDescriptor(c);
 986         if (prop != null) {
 987             return COPY;
 988         }
 989         return NONE;
 990     }
 991 
 992     /**
 993      * Returns an object that establishes the look of a transfer.  This is
 994      * useful for both providing feedback while performing a drag operation and for
 995      * representing the transfer in a clipboard implementation that has a visual
 996      * appearance.  The implementation of the <code>Icon</code> interface should
 997      * not alter the graphics clip or alpha level.
 998      * The icon implementation need not be rectangular or paint all of the
 999      * bounding rectangle and logic that calls the icons paint method should
1000      * not assume the all bits are painted. <code>null</code> is a valid return value
1001      * for this method and indicates there is no visual representation provided.
1002      * In that case, the calling logic is free to represent the
1003      * transferable however it wants.
1004      * <p>
1005      * The default Swing logic will not do an alpha blended drag animation if
1006      * the return is <code>null</code>.
1007      *
1008      * @param t  the data to be transferred; this value is expected to have been
1009      *  created by the <code>createTransferable</code> method
1010      * @return  <code>null</code>, indicating
1011      *    there is no default visual representation
1012      */
1013     public Icon getVisualRepresentation(Transferable t) {
1014         return null;
1015     }
1016 
1017     /**
1018      * Creates a <code>Transferable</code> to use as the source for
1019      * a data transfer. Returns the representation of the data to
1020      * be transferred, or <code>null</code> if the component's
1021      * property is <code>null</code>
1022      *
1023      * @param c  the component holding the data to be transferred;
1024      *              provided to enable sharing of <code>TransferHandler</code>s
1025      * @return  the representation of the data to be transferred, or
1026      *  <code>null</code> if the property associated with <code>c</code>
1027      *  is <code>null</code>
1028      *
1029      */
1030     protected Transferable createTransferable(JComponent c) {
1031         PropertyDescriptor property = getPropertyDescriptor(c);
1032         if (property != null) {
1033             return new PropertyTransferable(property, c);
1034         }
1035         return null;
1036     }
1037 
1038     /**
1039      * Invoked after data has been exported.  This method should remove
1040      * the data that was transferred if the action was <code>MOVE</code>.
1041      * <p>
1042      * This method is implemented to do nothing since <code>MOVE</code>
1043      * is not a supported action of this implementation
1044      * (<code>getSourceActions</code> does not include <code>MOVE</code>).
1045      *
1046      * @param source the component that was the source of the data
1047      * @param data   The data that was transferred or possibly null
1048      *               if the action is <code>NONE</code>.
1049      * @param action the actual action that was performed
1050      */
1051     protected void exportDone(JComponent source, Transferable data, int action) {
1052     }
1053 
1054     /**
1055      * Fetches the property descriptor for the property assigned to this transfer
1056      * handler on the given component (transfer handler may be shared).  This
1057      * returns <code>null</code> if the property descriptor can't be found
1058      * or there is an error attempting to fetch the property descriptor.
1059      */
1060     private PropertyDescriptor getPropertyDescriptor(JComponent comp) {
1061         if (propertyName == null) {
1062             return null;
1063         }
1064         Class<?> k = comp.getClass();
1065         BeanInfo bi;
1066         try {
1067             bi = Introspector.getBeanInfo(k);
1068         } catch (IntrospectionException ex) {
1069             return null;
1070         }
1071         PropertyDescriptor props[] = bi.getPropertyDescriptors();
1072         for (int i=0; i < props.length; i++) {
1073             if (propertyName.equals(props[i].getName())) {
1074                 Method reader = props[i].getReadMethod();
1075 
1076                 if (reader != null) {
1077                     Class<?>[] params = reader.getParameterTypes();
1078 
1079                     if (params == null || params.length == 0) {
1080                         // found the desired descriptor
1081                         return props[i];
1082                     }
1083                 }
1084             }
1085         }
1086         return null;
1087     }
1088 
1089     /**
1090      * Fetches the data flavor from the array of possible flavors that
1091      * has data of the type represented by property type.  Null is
1092      * returned if there is no match.
1093      */
1094     private DataFlavor getPropertyDataFlavor(Class<?> k, DataFlavor[] flavors) {
1095         for(int i = 0; i < flavors.length; i++) {
1096             DataFlavor flavor = flavors[i];
1097             if ("application".equals(flavor.getPrimaryType()) &&
1098                 "x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
1099                 k.isAssignableFrom(flavor.getRepresentationClass())) {
1100 
1101                 return flavor;
1102             }
1103         }
1104         return null;
1105     }
1106 
1107 
1108     private String propertyName;
1109     private static SwingDragGestureRecognizer recognizer = null;
1110 
1111     private static DropTargetListener getDropTargetListener() {
1112         synchronized(DropHandler.class) {
1113             DropHandler handler =
1114                 (DropHandler)AppContext.getAppContext().get(DropHandler.class);
1115 
1116             if (handler == null) {
1117                 handler = new DropHandler();
1118                 AppContext.getAppContext().put(DropHandler.class, handler);
1119             }
1120 
1121             return handler;
1122         }
1123     }
1124 
1125     static class PropertyTransferable implements Transferable {
1126 
1127         PropertyTransferable(PropertyDescriptor p, JComponent c) {
1128             property = p;
1129             component = c;
1130         }
1131 
1132         // --- Transferable methods ----------------------------------------------
1133 
1134         /**
1135          * Returns an array of <code>DataFlavor</code> objects indicating the flavors the data
1136          * can be provided in.  The array should be ordered according to preference
1137          * for providing the data (from most richly descriptive to least descriptive).
1138          * @return an array of data flavors in which this data can be transferred
1139          */
1140         public DataFlavor[] getTransferDataFlavors() {
1141             DataFlavor[] flavors = new DataFlavor[1];
1142             Class<?> propertyType = property.getPropertyType();
1143             String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=" + propertyType.getName();
1144             try {
1145                 flavors[0] = new DataFlavor(mimeType);
1146             } catch (ClassNotFoundException cnfe) {
1147                 flavors = new DataFlavor[0];
1148             }
1149             return flavors;
1150         }
1151 
1152         /**
1153          * Returns whether the specified data flavor is supported for
1154          * this object.
1155          * @param flavor the requested flavor for the data
1156          * @return true if this <code>DataFlavor</code> is supported,
1157          *   otherwise false
1158          */
1159         public boolean isDataFlavorSupported(DataFlavor flavor) {
1160             Class<?> propertyType = property.getPropertyType();
1161             if ("application".equals(flavor.getPrimaryType()) &&
1162                 "x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
1163                 flavor.getRepresentationClass().isAssignableFrom(propertyType)) {
1164 
1165                 return true;
1166             }
1167             return false;
1168         }
1169 
1170         /**
1171          * Returns an object which represents the data to be transferred.  The class
1172          * of the object returned is defined by the representation class of the flavor.
1173          *
1174          * @param flavor the requested flavor for the data
1175          * @see DataFlavor#getRepresentationClass
1176          * @exception IOException                if the data is no longer available
1177          *              in the requested flavor.
1178          * @exception UnsupportedFlavorException if the requested data flavor is
1179          *              not supported.
1180          */
1181         public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
1182             if (! isDataFlavorSupported(flavor)) {
1183                 throw new UnsupportedFlavorException(flavor);
1184             }
1185             Method reader = property.getReadMethod();
1186             Object value = null;
1187             try {
1188                 value = MethodUtil.invoke(reader, component, (Object[])null);
1189             } catch (Exception ex) {
1190                 throw new IOException("Property read failed: " + property.getName());
1191             }
1192             return value;
1193         }
1194 
1195         JComponent component;
1196         PropertyDescriptor property;
1197     }
1198 
1199     /**
1200      * This is the default drop target for drag and drop operations if
1201      * one isn't provided by the developer.  <code>DropTarget</code>
1202      * only supports one <code>DropTargetListener</code> and doesn't
1203      * function properly if it isn't set.
1204      * This class sets the one listener as the linkage of drop handling
1205      * to the <code>TransferHandler</code>, and adds support for
1206      * additional listeners which some of the <code>ComponentUI</code>
1207      * implementations install to manipulate a drop insertion location.
1208      */
1209     static class SwingDropTarget extends DropTarget implements UIResource {
1210 
1211         SwingDropTarget(Component c) {
1212             super(c, COPY_OR_MOVE | LINK, null);
1213             try {
1214                 // addDropTargetListener is overridden
1215                 // we specifically need to add to the superclass
1216                 super.addDropTargetListener(getDropTargetListener());
1217             } catch (TooManyListenersException tmle) {}
1218         }
1219 
1220         public void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
1221             // Since the super class only supports one DropTargetListener,
1222             // and we add one from the constructor, we always add to the
1223             // extended list.
1224             if (listenerList == null) {
1225                 listenerList = new EventListenerList();
1226             }
1227             listenerList.add(DropTargetListener.class, dtl);
1228         }
1229 
1230         public void removeDropTargetListener(DropTargetListener dtl) {
1231             if (listenerList != null) {
1232                 listenerList.remove(DropTargetListener.class, dtl);
1233             }
1234         }
1235 
1236         @Override
1237         public void removeNotify(ComponentPeer peer) {
1238             super.removeNotify(peer);
1239             if (dtListener != null && dtListener instanceof DropHandler) {
1240                 ((DropHandler)dtListener).cleanup(false);
1241             }
1242         }
1243 
1244         // --- DropTargetListener methods (multicast) --------------------------
1245 
1246         public void dragEnter(DropTargetDragEvent e) {
1247             super.dragEnter(e);
1248             if (listenerList != null) {
1249                 Object[] listeners = listenerList.getListenerList();
1250                 for (int i = listeners.length-2; i>=0; i-=2) {
1251                     if (listeners[i]==DropTargetListener.class) {
1252                         ((DropTargetListener)listeners[i+1]).dragEnter(e);
1253                     }
1254                 }
1255             }
1256         }
1257 
1258         public void dragOver(DropTargetDragEvent e) {
1259             super.dragOver(e);
1260             if (listenerList != null) {
1261                 Object[] listeners = listenerList.getListenerList();
1262                 for (int i = listeners.length-2; i>=0; i-=2) {
1263                     if (listeners[i]==DropTargetListener.class) {
1264                         ((DropTargetListener)listeners[i+1]).dragOver(e);
1265                     }
1266                 }
1267             }
1268         }
1269 
1270         public void dragExit(DropTargetEvent e) {
1271             super.dragExit(e);
1272             if (listenerList != null) {
1273                 Object[] listeners = listenerList.getListenerList();
1274                 for (int i = listeners.length-2; i>=0; i-=2) {
1275                     if (listeners[i]==DropTargetListener.class) {
1276                         ((DropTargetListener)listeners[i+1]).dragExit(e);
1277                     }
1278                 }
1279             }
1280         }
1281 
1282         public void drop(DropTargetDropEvent e) {
1283             super.drop(e);
1284             if (listenerList != null) {
1285                 Object[] listeners = listenerList.getListenerList();
1286                 for (int i = listeners.length-2; i>=0; i-=2) {
1287                     if (listeners[i]==DropTargetListener.class) {
1288                         ((DropTargetListener)listeners[i+1]).drop(e);
1289                     }
1290                 }
1291             }
1292         }
1293 
1294         public void dropActionChanged(DropTargetDragEvent e) {
1295             super.dropActionChanged(e);
1296             if (listenerList != null) {
1297                 Object[] listeners = listenerList.getListenerList();
1298                 for (int i = listeners.length-2; i>=0; i-=2) {
1299                     if (listeners[i]==DropTargetListener.class) {
1300                         ((DropTargetListener)listeners[i+1]).dropActionChanged(e);
1301                     }
1302                 }
1303             }
1304         }
1305 
1306         private EventListenerList listenerList;
1307     }
1308 
1309     private static class DropHandler implements DropTargetListener,
1310                                                 Serializable,
1311                                                 ActionListener {
1312 
1313         private Timer timer;
1314         private Point lastPosition;
1315         private Rectangle outer = new Rectangle();
1316         private Rectangle inner = new Rectangle();
1317         private int hysteresis = 10;
1318 
1319         private Component component;
1320         private Object state;
1321         private TransferSupport support =
1322             new TransferSupport(null, (DropTargetEvent)null);
1323 
1324         private static final int AUTOSCROLL_INSET = 10;
1325 
1326         /**
1327          * Update the geometry of the autoscroll region.  The geometry is
1328          * maintained as a pair of rectangles.  The region can cause
1329          * a scroll if the pointer sits inside it for the duration of the
1330          * timer.  The region that causes the timer countdown is the area
1331          * between the two rectangles.
1332          * <p>
1333          * This is implemented to use the visible area of the component
1334          * as the outer rectangle, and the insets are fixed at 10. Should
1335          * the component be smaller than a total of 20 in any direction,
1336          * autoscroll will not occur in that direction.
1337          */
1338         private void updateAutoscrollRegion(JComponent c) {
1339             // compute the outer
1340             Rectangle visible = c.getVisibleRect();
1341             outer.setBounds(visible.x, visible.y, visible.width, visible.height);
1342 
1343             // compute the insets
1344             Insets i = new Insets(0, 0, 0, 0);
1345             if (c instanceof Scrollable) {
1346                 int minSize = 2 * AUTOSCROLL_INSET;
1347 
1348                 if (visible.width >= minSize) {
1349                     i.left = i.right = AUTOSCROLL_INSET;
1350                 }
1351 
1352                 if (visible.height >= minSize) {
1353                     i.top = i.bottom = AUTOSCROLL_INSET;
1354                 }
1355             }
1356 
1357             // set the inner from the insets
1358             inner.setBounds(visible.x + i.left,
1359                           visible.y + i.top,
1360                           visible.width - (i.left + i.right),
1361                           visible.height - (i.top  + i.bottom));
1362         }
1363 
1364         /**
1365          * Perform an autoscroll operation.  This is implemented to scroll by the
1366          * unit increment of the Scrollable using scrollRectToVisible.  If the
1367          * cursor is in a corner of the autoscroll region, more than one axis will
1368          * scroll.
1369          */
1370         private void autoscroll(JComponent c, Point pos) {
1371             if (c instanceof Scrollable) {
1372                 Scrollable s = (Scrollable) c;
1373                 if (pos.y < inner.y) {
1374                     // scroll upward
1375                     int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, -1);
1376                     Rectangle r = new Rectangle(inner.x, outer.y - dy, inner.width, dy);
1377                     c.scrollRectToVisible(r);
1378                 } else if (pos.y > (inner.y + inner.height)) {
1379                     // scroll downard
1380                     int dy = s.getScrollableUnitIncrement(outer, SwingConstants.VERTICAL, 1);
1381                     Rectangle r = new Rectangle(inner.x, outer.y + outer.height, inner.width, dy);
1382                     c.scrollRectToVisible(r);
1383                 }
1384 
1385                 if (pos.x < inner.x) {
1386                     // scroll left
1387                     int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, -1);
1388                     Rectangle r = new Rectangle(outer.x - dx, inner.y, dx, inner.height);
1389                     c.scrollRectToVisible(r);
1390                 } else if (pos.x > (inner.x + inner.width)) {
1391                     // scroll right
1392                     int dx = s.getScrollableUnitIncrement(outer, SwingConstants.HORIZONTAL, 1);
1393                     Rectangle r = new Rectangle(outer.x + outer.width, inner.y, dx, inner.height);
1394                     c.scrollRectToVisible(r);
1395                 }
1396             }
1397         }
1398 
1399         /**
1400          * Initializes the internal properties if they haven't been already
1401          * inited. This is done lazily to avoid loading of desktop properties.
1402          */
1403         private void initPropertiesIfNecessary() {
1404             if (timer == null) {
1405                 Toolkit t = Toolkit.getDefaultToolkit();
1406                 Integer prop;
1407 
1408                 prop = (Integer)
1409                     t.getDesktopProperty("DnD.Autoscroll.interval");
1410 
1411                 timer = new Timer(prop == null ? 100 : prop.intValue(), this);
1412 
1413                 prop = (Integer)
1414                     t.getDesktopProperty("DnD.Autoscroll.initialDelay");
1415 
1416                 timer.setInitialDelay(prop == null ? 100 : prop.intValue());
1417 
1418                 prop = (Integer)
1419                     t.getDesktopProperty("DnD.Autoscroll.cursorHysteresis");
1420 
1421                 if (prop != null) {
1422                     hysteresis = prop.intValue();
1423                 }
1424             }
1425         }
1426 
1427         /**
1428          * The timer fired, perform autoscroll if the pointer is within the
1429          * autoscroll region.
1430          * <P>
1431          * @param e the <code>ActionEvent</code>
1432          */
1433         public void actionPerformed(ActionEvent e) {
1434             updateAutoscrollRegion((JComponent)component);
1435             if (outer.contains(lastPosition) && !inner.contains(lastPosition)) {
1436                 autoscroll((JComponent)component, lastPosition);
1437             }
1438         }
1439 
1440         // --- DropTargetListener methods -----------------------------------
1441 
1442         private void setComponentDropLocation(TransferSupport support,
1443                                               boolean forDrop) {
1444 
1445             DropLocation dropLocation = (support == null)
1446                                         ? null
1447                                         : support.getDropLocation();
1448 
1449             if (SunToolkit.isInstanceOf(component, "javax.swing.text.JTextComponent")) {
1450                 state = SwingAccessor.getJTextComponentAccessor().
1451                             setDropLocation((JTextComponent)component, dropLocation, state, forDrop);
1452             } else if (component instanceof JComponent) {
1453                 state = ((JComponent)component).setDropLocation(dropLocation, state, forDrop);
1454             }
1455         }
1456 
1457         private void handleDrag(DropTargetDragEvent e) {
1458             TransferHandler importer =
1459                 ((HasGetTransferHandler)component).getTransferHandler();
1460 
1461             if (importer == null) {
1462                 e.rejectDrag();
1463                 setComponentDropLocation(null, false);
1464                 return;
1465             }
1466 
1467             support.setDNDVariables(component, e);
1468             boolean canImport = importer.canImport(support);
1469 
1470             if (canImport) {
1471                 e.acceptDrag(support.getDropAction());
1472             } else {
1473                 e.rejectDrag();
1474             }
1475 
1476             boolean showLocation = support.showDropLocationIsSet ?
1477                                    support.showDropLocation :
1478                                    canImport;
1479 
1480             setComponentDropLocation(showLocation ? support : null, false);
1481         }
1482 
1483         public void dragEnter(DropTargetDragEvent e) {
1484             state = null;
1485             component = e.getDropTargetContext().getComponent();
1486 
1487             handleDrag(e);
1488 
1489             if (component instanceof JComponent) {
1490                 lastPosition = e.getLocation();
1491                 updateAutoscrollRegion((JComponent)component);
1492                 initPropertiesIfNecessary();
1493             }
1494         }
1495 
1496         public void dragOver(DropTargetDragEvent e) {
1497             handleDrag(e);
1498 
1499             if (!(component instanceof JComponent)) {
1500                 return;
1501             }
1502 
1503             Point p = e.getLocation();
1504 
1505             if (Math.abs(p.x - lastPosition.x) > hysteresis
1506                     || Math.abs(p.y - lastPosition.y) > hysteresis) {
1507                 // no autoscroll
1508                 if (timer.isRunning()) timer.stop();
1509             } else {
1510                 if (!timer.isRunning()) timer.start();
1511             }
1512 
1513             lastPosition = p;
1514         }
1515 
1516         public void dragExit(DropTargetEvent e) {
1517             cleanup(false);
1518         }
1519 
1520         public void drop(DropTargetDropEvent e) {
1521             TransferHandler importer =
1522                 ((HasGetTransferHandler)component).getTransferHandler();
1523 
1524             if (importer == null) {
1525                 e.rejectDrop();
1526                 cleanup(false);
1527                 return;
1528             }
1529 
1530             support.setDNDVariables(component, e);
1531             boolean canImport = importer.canImport(support);
1532 
1533             if (canImport) {
1534                 e.acceptDrop(support.getDropAction());
1535 
1536                 boolean showLocation = support.showDropLocationIsSet ?
1537                                        support.showDropLocation :
1538                                        canImport;
1539 
1540                 setComponentDropLocation(showLocation ? support : null, false);
1541 
1542                 boolean success;
1543 
1544                 try {
1545                     success = importer.importData(support);
1546                 } catch (RuntimeException re) {
1547                     success = false;
1548                 }
1549 
1550                 e.dropComplete(success);
1551                 cleanup(success);
1552             } else {
1553                 e.rejectDrop();
1554                 cleanup(false);
1555             }
1556         }
1557 
1558         public void dropActionChanged(DropTargetDragEvent e) {
1559             /*
1560              * Work-around for Linux bug where dropActionChanged
1561              * is called before dragEnter.
1562              */
1563             if (component == null) {
1564                 return;
1565             }
1566 
1567             handleDrag(e);
1568         }
1569 
1570         private void cleanup(boolean forDrop) {
1571             setComponentDropLocation(null, forDrop);
1572             if (component instanceof JComponent) {
1573                 ((JComponent)component).dndDone();
1574             }
1575 
1576             if (timer != null) {
1577                 timer.stop();
1578             }
1579 
1580             state = null;
1581             component = null;
1582             lastPosition = null;
1583         }
1584     }
1585 
1586     /**
1587      * This is the default drag handler for drag and drop operations that
1588      * use the <code>TransferHandler</code>.
1589      */
1590     private static class DragHandler implements DragGestureListener, DragSourceListener {
1591 
1592         private boolean scrolls;
1593 
1594         // --- DragGestureListener methods -----------------------------------
1595 
1596         /**
1597          * a Drag gesture has been recognized
1598          */
1599         public void dragGestureRecognized(DragGestureEvent dge) {
1600             JComponent c = (JComponent) dge.getComponent();
1601             TransferHandler th = c.getTransferHandler();
1602             Transferable t = th.createTransferable(c);
1603             if (t != null) {
1604                 scrolls = c.getAutoscrolls();
1605                 c.setAutoscrolls(false);
1606                 try {
1607                     Image im = th.getDragImage();
1608                     if (im == null) {
1609                         dge.startDrag(null, t, this);
1610                     } else {
1611                         dge.startDrag(null, im, th.getDragImageOffset(), t, this);
1612                     }
1613                     return;
1614                 } catch (RuntimeException re) {
1615                     c.setAutoscrolls(scrolls);
1616                 }
1617             }
1618 
1619             th.exportDone(c, t, NONE);
1620         }
1621 
1622         // --- DragSourceListener methods -----------------------------------
1623 
1624         /**
1625          * as the hotspot enters a platform dependent drop site
1626          */
1627         public void dragEnter(DragSourceDragEvent dsde) {
1628         }
1629 
1630         /**
1631          * as the hotspot moves over a platform dependent drop site
1632          */
1633         public void dragOver(DragSourceDragEvent dsde) {
1634         }
1635 
1636         /**
1637          * as the hotspot exits a platform dependent drop site
1638          */
1639         public void dragExit(DragSourceEvent dsde) {
1640         }
1641 
1642         /**
1643          * as the operation completes
1644          */
1645         public void dragDropEnd(DragSourceDropEvent dsde) {
1646             DragSourceContext dsc = dsde.getDragSourceContext();
1647             JComponent c = (JComponent)dsc.getComponent();
1648             if (dsde.getDropSuccess()) {
1649                 c.getTransferHandler().exportDone(c, dsc.getTransferable(), dsde.getDropAction());
1650             } else {
1651                 c.getTransferHandler().exportDone(c, dsc.getTransferable(), NONE);
1652             }
1653             c.setAutoscrolls(scrolls);
1654         }
1655 
1656         public void dropActionChanged(DragSourceDragEvent dsde) {
1657         }
1658     }
1659 
1660     private static class SwingDragGestureRecognizer extends DragGestureRecognizer {
1661 
1662         SwingDragGestureRecognizer(DragGestureListener dgl) {
1663             super(DragSource.getDefaultDragSource(), null, NONE, dgl);
1664         }
1665 
1666         void gestured(JComponent c, MouseEvent e, int srcActions, int action) {
1667             setComponent(c);
1668             setSourceActions(srcActions);
1669             appendEvent(e);
1670             fireDragGestureRecognized(action, e.getPoint());
1671         }
1672 
1673         /**
1674          * register this DragGestureRecognizer's Listeners with the Component
1675          */
1676         protected void registerListeners() {
1677         }
1678 
1679         /**
1680          * unregister this DragGestureRecognizer's Listeners with the Component
1681          *
1682          * subclasses must override this method
1683          */
1684         protected void unregisterListeners() {
1685         }
1686 
1687     }
1688 
1689     static final Action cutAction = new TransferAction("cut");
1690     static final Action copyAction = new TransferAction("copy");
1691     static final Action pasteAction = new TransferAction("paste");
1692 
1693     static class TransferAction extends UIAction implements UIResource {
1694 
1695         TransferAction(String name) {
1696             super(name);
1697         }
1698 
1699         public boolean isEnabled(Object sender) {
1700             if (sender instanceof JComponent
1701                 && ((JComponent)sender).getTransferHandler() == null) {
1702                     return false;
1703             }
1704 
1705             return true;
1706         }
1707 
1708         private static final JavaSecurityAccess javaSecurityAccess =
1709             SharedSecrets.getJavaSecurityAccess();
1710 
1711         public void actionPerformed(final ActionEvent e) {
1712             final Object src = e.getSource();
1713 
1714             final PrivilegedAction<Void> action = new PrivilegedAction<Void>() {
1715                 public Void run() {
1716                     actionPerformedImpl(e);
1717                     return null;
1718                 }
1719             };
1720 
1721             final AccessControlContext stack = AccessController.getContext();
1722             final AccessControlContext srcAcc = AWTAccessor.getComponentAccessor().getAccessControlContext((Component)src);
1723             final AccessControlContext eventAcc = AWTAccessor.getAWTEventAccessor().getAccessControlContext(e);
1724 
1725                 if (srcAcc == null) {
1726                     javaSecurityAccess.doIntersectionPrivilege(action, stack, eventAcc);
1727                 } else {
1728                     javaSecurityAccess.doIntersectionPrivilege(
1729                         new PrivilegedAction<Void>() {
1730                             public Void run() {
1731                                 javaSecurityAccess.doIntersectionPrivilege(action, eventAcc);
1732                                 return null;
1733                              }
1734                     }, stack, srcAcc);
1735                 }
1736         }
1737 
1738         private void actionPerformedImpl(ActionEvent e) {
1739             Object src = e.getSource();
1740             if (src instanceof JComponent) {
1741                 JComponent c = (JComponent) src;
1742                 TransferHandler th = c.getTransferHandler();
1743                 Clipboard clipboard = getClipboard(c);
1744                 String name = (String) getValue(Action.NAME);
1745 
1746                 Transferable trans = null;
1747 
1748                 // any of these calls may throw IllegalStateException
1749                 try {
1750                     if ((clipboard != null) && (th != null) && (name != null)) {
1751                         if ("cut".equals(name)) {
1752                             th.exportToClipboard(c, clipboard, MOVE);
1753                         } else if ("copy".equals(name)) {
1754                             th.exportToClipboard(c, clipboard, COPY);
1755                         } else if ("paste".equals(name)) {
1756                             trans = clipboard.getContents(null);
1757                         }
1758                     }
1759                 } catch (IllegalStateException ise) {
1760                     // clipboard was unavailable
1761                     UIManager.getLookAndFeel().provideErrorFeedback(c);
1762                     return;
1763                 }
1764 
1765                 // this is a paste action, import data into the component
1766                 if (trans != null) {
1767                     th.importData(new TransferSupport(c, trans));
1768                 }
1769             }
1770         }
1771 
1772         /**
1773          * Returns the clipboard to use for cut/copy/paste.
1774          */
1775         private Clipboard getClipboard(JComponent c) {
1776             if (SwingUtilities2.canAccessSystemClipboard()) {
1777                 return c.getToolkit().getSystemClipboard();
1778             }
1779             Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext().
1780                 get(SandboxClipboardKey);
1781             if (clipboard == null) {
1782                 clipboard = new Clipboard("Sandboxed Component Clipboard");
1783                 sun.awt.AppContext.getAppContext().put(SandboxClipboardKey,
1784                                                        clipboard);
1785             }
1786             return clipboard;
1787         }
1788 
1789         /**
1790          * Key used in app context to lookup Clipboard to use if access to
1791          * System clipboard is denied.
1792          */
1793         private static Object SandboxClipboardKey = new Object();
1794 
1795     }
1796 
1797 }