36 import javax.swing.event.*;
37 import javax.swing.text.JTextComponent;
38
39 import sun.reflect.misc.MethodUtil;
40 import sun.swing.SwingUtilities2;
41 import sun.awt.AppContext;
42 import sun.swing.*;
43 import sun.awt.SunToolkit;
44
45 import java.security.AccessController;
46 import java.security.PrivilegedAction;
47
48 import java.security.AccessControlContext;
49 import java.security.ProtectionDomain;
50 import jdk.internal.misc.SharedSecrets;
51 import jdk.internal.misc.JavaSecurityAccess;
52
53 import 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")
88 public 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 "copy" 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 "move" 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 * "copy" or "move".
112 */
113 public static final int COPY_OR_MOVE = DnDConstants.ACTION_COPY_OR_MOVE;
114
115 /**
116 * An <code>int</code> representing a "link" 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 /**
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 */
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}.
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;
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 {
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();
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
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 }
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);
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) {
|
36 import javax.swing.event.*;
37 import javax.swing.text.JTextComponent;
38
39 import sun.reflect.misc.MethodUtil;
40 import sun.swing.SwingUtilities2;
41 import sun.awt.AppContext;
42 import sun.swing.*;
43 import sun.awt.SunToolkit;
44
45 import java.security.AccessController;
46 import java.security.PrivilegedAction;
47
48 import java.security.AccessControlContext;
49 import java.security.ProtectionDomain;
50 import jdk.internal.misc.SharedSecrets;
51 import jdk.internal.misc.JavaSecurityAccess;
52
53 import sun.awt.AWTAccessor;
54
55 /**
56 * This class is used to handle the transfer of a {@code Transferable}
57 * to and from Swing components. The {@code Transferable} 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} 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} can be constructed with the string "foreground". The
74 * built in support will use the color returned by {@code getForeground} as the source
75 * of the transfer, and {@code setForeground} 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")
88 public class TransferHandler implements Serializable {
89
90 /**
91 * An {@code int} representing no transfer action.
92 */
93 public static final int NONE = DnDConstants.ACTION_NONE;
94
95 /**
96 * An {@code int} representing a "copy" 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} representing a "move" 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} representing a source action capability of either
111 * "copy" or "move".
112 */
113 public static final int COPY_OR_MOVE = DnDConstants.ACTION_COPY_OR_MOVE;
114
115 /**
116 * An {@code int} representing a "link" 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} instances and by
146 * components with a {@code getDropLocation()} 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}
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 /**
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}.
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} with {@code isDrop()}
233 * {@code true} for the given component, event, and index.
234 *
235 * @param component the target component
236 * @param event a {@code DropTargetEvent}
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} with {@code isDrop()}
247 * {@code false} for the given component and
248 * {@code Transferable}.
249 *
250 * @param component the target component
251 * @param transferable the transferable
252 * @throws NullPointerException if either parameter
253 * is {@code null}
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}
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}
313 * represents a drop operation.
314 *
315 * @return {@code true} if this is a drop operation,
316 * {@code false} 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 */
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} 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} associated with this transfer.
561 * <p>
562 * Note: Unless it is necessary to fetch the {@code Transferable}
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} and asking it directly.
566 *
567 * @return the {@code Transferable} 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}.
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} 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;
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}
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}
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} mechanism, requiring no further effort
716 * from the developer. The {@code exportDone} 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}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}
756 * mechanism, requiring no further effort from the developer. Any data
757 * transfer <em>will</em> be complete and the {@code exportDone}
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} thrown by
761 * {@link Clipboard#setContents(Transferable, ClipboardOwner)} will
762 * be propagated through this method. However,
763 * {@code exportDone} will first be called with an action
764 * of {@code NONE} for consistency.
765 *
766 * @param comp the component holding the data to be transferred;
767 * provided to enable sharing of {@code TransferHandler}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} or {@code MOVE};
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}
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} to be
803 * imported and the component to transfer to are contained
804 * within the {@code TransferSupport}.
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} 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}.
819 * @return true if the data was inserted into the component,
820 * false otherwise
821 * @throws NullPointerException if {@code support} 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} represents
834 * the data to be imported into the component.
835 * <p>
836 * Note: Swing now calls the newer version of {@code importData}
837 * that takes a {@code TransferSupport}, 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}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 {
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} 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}.
916 * @return {@code true} if the import can happen,
917 * {@code false} otherwise
918 * @throws NullPointerException if {@code support} 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}
935 * that takes a {@code TransferSupport}, 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}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}s
980 * @return {@code COPY} if the transfer property can be found,
981 * otherwise returns {@code NONE}
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} 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} 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}.
1006 *
1007 * @param t the data to be transferred; this value is expected to have been
1008 * created by the {@code createTransferable} method
1009 * @return {@code null}, 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} to use as the source for
1018 * a data transfer. Returns the representation of the data to
1019 * be transferred, or {@code null} if the component's
1020 * property is {@code null}
1021 *
1022 * @param c the component holding the data to be transferred;
1023 * provided to enable sharing of {@code TransferHandler}s
1024 * @return the representation of the data to be transferred, or
1025 * {@code null} if the property associated with {@code c}
1026 * is {@code null}
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}.
1040 * <p>
1041 * This method is implemented to do nothing since {@code MOVE}
1042 * is not a supported action of this implementation
1043 * ({@code getSourceActions} does not include {@code MOVE}).
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}.
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} 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();
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} 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} 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
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}
1201 * only supports one {@code DropTargetListener} 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}, and adds support for
1205 * additional listeners which some of the {@code ComponentUI}
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 }
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}
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);
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}.
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) {
|