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