1. /*
  2. * @(#)TransferHandler.java 1.29 04/06/02
  3. *
  4. * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
  5. * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
  6. */
  7. package javax.swing;
  8. import java.awt.*;
  9. import java.awt.event.*;
  10. import java.awt.datatransfer.*;
  11. import java.awt.dnd.*;
  12. import java.beans.*;
  13. import java.lang.reflect.*;
  14. import java.io.*;
  15. import java.util.TooManyListenersException;
  16. import javax.swing.plaf.UIResource;
  17. import javax.swing.event.*;
  18. import com.sun.java.swing.SwingUtilities2;
  19. /**
  20. * This class is used to handle the transfer of a <code>Transferable</code>
  21. * to and from Swing components. The <code>Transferable</code> is used to
  22. * represent data that is exchanged via a cut, copy, or paste
  23. * to/from a clipboard. It is also used in drag-and-drop operations
  24. * to represent a drag from a component, and a drop to a component.
  25. * Swing provides functionality that automatically supports cut, copy,
  26. * and paste keyboard bindings that use the functionality provided by
  27. * an implementation of this class. Swing also provides functionality
  28. * that automatically supports drag and drop that uses the functionality
  29. * provided by an implementation of this class. The Swing developer can
  30. * concentrate on specifying the semantics of a transfer primarily by setting
  31. * the <code>transferHandler</code> property on a Swing component.
  32. * <p>
  33. * This class is implemented to provide a default behavior of transferring
  34. * a component property simply by specifying the name of the property in
  35. * the constructor. For example, to transfer the foreground color from
  36. * one component to another either via the clipboard or a drag and drop operation
  37. * a <code>TransferHandler</code> can be constructed with the string "foreground". The
  38. * built in support will use the color returned by <code>getForeground</code> as the source
  39. * of the transfer, and <code>setForeground</code> for the target of a transfer.
  40. * <p>
  41. * Please see
  42. * <a href="http://java.sun.com/docs/books/tutorial/uiswing/misc/dnd.html">
  43. * How to Use Drag and Drop and Data Transfer</a>,
  44. * a section in <em>The Java Tutorial</em>, for more information.
  45. *
  46. *
  47. * @author Timothy Prinzing
  48. * @version 1.29 06/02/04
  49. * @since 1.4
  50. */
  51. public class TransferHandler implements Serializable {
  52. /**
  53. * An <code>int</code> representing no transfer action.
  54. */
  55. public static final int NONE = DnDConstants.ACTION_NONE;
  56. /**
  57. * An <code>int</code> representing a "copy" transfer action.
  58. * This value is used when data is copied to a clipboard
  59. * or copied elsewhere in a drag and drop operation.
  60. */
  61. public static final int COPY = DnDConstants.ACTION_COPY;
  62. /**
  63. * An <code>int</code> representing a "move" transfer action.
  64. * This value is used when data is moved to a clipboard (i.e. a cut)
  65. * or moved elsewhere in a drag and drop operation.
  66. */
  67. public static final int MOVE = DnDConstants.ACTION_MOVE;
  68. /**
  69. * An <code>int</code> representing a source action capability of either
  70. * "copy" or "move".
  71. */
  72. public static final int COPY_OR_MOVE = DnDConstants.ACTION_COPY_OR_MOVE;
  73. /**
  74. * An <code>int</code> representing a "link" transfer action.
  75. * This value is used to specify that data should be linked in a drag
  76. * and drop operation.
  77. */
  78. private static final int LINK = DnDConstants.ACTION_LINK;
  79. /**
  80. * Returns an <code>Action</code> that behaves like a 'cut' operation.
  81. * That is, this will invoke <code>exportToClipboard</code> with
  82. * a <code>MOVE</code> argument on the <code>TransferHandler</code>
  83. * associated with the <code>JComponent</code> that is the source of
  84. * the <code>ActionEvent</code>.
  85. *
  86. * @return cut Action
  87. */
  88. public static Action getCutAction() {
  89. return cutAction;
  90. }
  91. /**
  92. * Returns an <code>Action</code> that behaves like a 'copy' operation.
  93. * That is, this will invoke <code>exportToClipboard</code> with
  94. * a <code>COPY</code> argument on the <code>TransferHandler</code>
  95. * associated with the <code>JComponent</code> that is the source of
  96. * the <code>ActionEvent</code>.
  97. *
  98. * @return cut Action
  99. */
  100. public static Action getCopyAction() {
  101. return copyAction;
  102. }
  103. /**
  104. * Returns an <code>Action</code> that behaves like a 'paste' operation.
  105. * That is, this will invoke <code>importData</code> on the
  106. * <code>TransferHandler</code>
  107. * associated with the <code>JComponent</code> that is the source of
  108. * the <code>ActionEvent</code>.
  109. *
  110. * @return cut Action
  111. */
  112. public static Action getPasteAction() {
  113. return pasteAction;
  114. }
  115. /**
  116. * Constructs a transfer handler that can transfer a Java Bean property
  117. * from one component to another via the clipboard or a drag and drop
  118. * operation.
  119. *
  120. * @param property the name of the property to transfer; this can
  121. * be <code>null</code> if there is no property associated with the transfer
  122. * handler (a subclass that performs some other kind of transfer, for example)
  123. */
  124. public TransferHandler(String property) {
  125. propertyName = property;
  126. }
  127. /**
  128. * Convenience constructor for subclasses.
  129. */
  130. protected TransferHandler() {
  131. this(null);
  132. }
  133. /**
  134. * Causes the Swing drag support to be initiated. This is called by
  135. * the various UI implementations in the <code>javax.swing.plaf.basic</code>
  136. * package if the dragEnabled property is set on the component.
  137. * This can be called by custom UI
  138. * implementations to use the Swing drag support. This method can also be called
  139. * by a Swing extension written as a subclass of <code>JComponent</code>
  140. * to take advantage of the Swing drag support.
  141. * <p>
  142. * The transfer <em>will not necessarily</em> have been completed at the
  143. * return of this call (i.e. the call does not block waiting for the drop).
  144. * The transfer will take place through the Swing implementation of the
  145. * <code>java.awt.dnd</code> mechanism, requiring no further effort
  146. * from the developer. The <code>exportDone</code> method will be called
  147. * when the transfer has completed.
  148. *
  149. * @param comp the component holding the data to be transferred; this
  150. * argument is provided to enable sharing of <code>TransferHandler</code>s by
  151. * multiple components
  152. * @param e the event that triggered the transfer
  153. * @param action the transfer action initially requested; this should
  154. * be a value of either <code>COPY</code> or <code>MOVE</code>
  155. * the value may be changed during the course of the drag operation
  156. */
  157. public void exportAsDrag(JComponent comp, InputEvent e, int action) {
  158. int srcActions = getSourceActions(comp);
  159. int dragAction = srcActions & action;
  160. if (! (e instanceof MouseEvent)) {
  161. // only mouse events supported for drag operations
  162. dragAction = NONE;
  163. }
  164. if (dragAction != NONE && !GraphicsEnvironment.isHeadless()) {
  165. if (recognizer == null) {
  166. recognizer = new SwingDragGestureRecognizer(new DragHandler());
  167. }
  168. recognizer.gestured(comp, (MouseEvent)e, srcActions, dragAction);
  169. } else {
  170. exportDone(comp, null, NONE);
  171. }
  172. }
  173. /**
  174. * Causes a transfer from the given component to the
  175. * given clipboard. This method is called by the default cut and
  176. * copy actions registered in a component's action map.
  177. * <p>
  178. * The transfer will take place using the <code>java.awt.datatransfer</code>
  179. * mechanism, requiring no further effort from the developer. Any data
  180. * transfer <em>will</em> be complete and the <code>exportDone</code>
  181. * method will be called with the action that occurred, before this method
  182. * returns. Should the clipboard be unavailable when attempting to place
  183. * data on it, the <code>IllegalStateException</code> thrown by
  184. * {@link Clipboard#setContents(Transferable, ClipboardOwner)} will
  185. * be propogated through this method. However,
  186. * <code>exportDone</code> will first be called with an action
  187. * of <code>NONE</code> for consistency.
  188. *
  189. * @param comp the component holding the data to be transferred; this
  190. * argument is provided to enable sharing of <code>TransferHandler</code>s by
  191. * multiple components
  192. * @param clip the clipboard to transfer the data into
  193. * @param action the transfer action requested; this should
  194. * be a value of either <code>COPY</code> or <code>MOVE</code>
  195. * the operation performed is the intersection of the transfer
  196. * capabilities given by getSourceActions and the requested action;
  197. * the intersection may result in an action of <code>NONE</code>
  198. * if the requested action isn't supported
  199. * @throws IllegalStateException if the clipboard is currently unavailable
  200. * @see Clipboard#setContents(Transferable, ClipboardOwner)
  201. */
  202. public void exportToClipboard(JComponent comp, Clipboard clip, int action)
  203. throws IllegalStateException {
  204. int clipboardAction = getSourceActions(comp) & action;
  205. if (clipboardAction != NONE) {
  206. Transferable t = createTransferable(comp);
  207. if (t != null) {
  208. try {
  209. clip.setContents(t, null);
  210. exportDone(comp, t, clipboardAction);
  211. return;
  212. } catch (IllegalStateException ise) {
  213. exportDone(comp, t, NONE);
  214. throw ise;
  215. }
  216. }
  217. }
  218. exportDone(comp, null, NONE);
  219. }
  220. /**
  221. * Causes a transfer to a component from a clipboard or a
  222. * DND drop operation. The <code>Transferable</code> represents
  223. * the data to be imported into the component.
  224. *
  225. * @param comp the component to receive the transfer; this
  226. * argument is provided to enable sharing of <code>TransferHandler</code>s
  227. * by multiple components
  228. * @param t the data to import
  229. * @return true if the data was inserted into the component, false otherwise
  230. */
  231. public boolean importData(JComponent comp, Transferable t) {
  232. PropertyDescriptor prop = getPropertyDescriptor(comp);
  233. if (prop != null) {
  234. Method writer = prop.getWriteMethod();
  235. if (writer == null) {
  236. // read-only property. ignore
  237. return false;
  238. }
  239. Class[] params = writer.getParameterTypes();
  240. if (params.length != 1) {
  241. // zero or more than one argument, ignore
  242. return false;
  243. }
  244. DataFlavor flavor = getPropertyDataFlavor(params[0], t.getTransferDataFlavors());
  245. if (flavor != null) {
  246. try {
  247. Object value = t.getTransferData(flavor);
  248. Object[] args = { value };
  249. writer.invoke(comp, args);
  250. return true;
  251. } catch (Exception ex) {
  252. System.err.println("Invocation failed");
  253. // invocation code
  254. }
  255. }
  256. }
  257. return false;
  258. }
  259. /**
  260. * Indicates whether a component would accept an import of the given
  261. * set of data flavors prior to actually attempting to import it.
  262. *
  263. * @param comp the component to receive the transfer; this
  264. * argument is provided to enable sharing of <code>TransferHandlers</code>
  265. * by multiple components
  266. * @param transferFlavors the data formats available
  267. * @return true if the data can be inserted into the component, false otherwise
  268. */
  269. public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) {
  270. PropertyDescriptor prop = getPropertyDescriptor(comp);
  271. if (prop != null) {
  272. Method writer = prop.getWriteMethod();
  273. if (writer == null) {
  274. // read-only property. ignore
  275. return false;
  276. }
  277. Class[] params = writer.getParameterTypes();
  278. if (params.length != 1) {
  279. // zero or more than one argument, ignore
  280. return false;
  281. }
  282. DataFlavor flavor = getPropertyDataFlavor(params[0], transferFlavors);
  283. if (flavor != null) {
  284. return true;
  285. }
  286. }
  287. return false;
  288. }
  289. /**
  290. * Returns the type of transfer actions supported by the source.
  291. * Some models are not mutable, so a transfer operation of <code>COPY</code>
  292. * only should be advertised in that case.
  293. *
  294. * @param c the component holding the data to be transferred; this
  295. * argument is provided to enable sharing of <code>TransferHandler</code>s
  296. * by multiple components.
  297. * @return <code>COPY</code> if the transfer property can be found,
  298. * otherwise returns <code>NONE</code> a return value of
  299. * of <code>NONE</code> disables any transfers out of the component
  300. */
  301. public int getSourceActions(JComponent c) {
  302. PropertyDescriptor prop = getPropertyDescriptor(c);
  303. if (prop != null) {
  304. return COPY;
  305. }
  306. return NONE;
  307. }
  308. /**
  309. * Returns an object that establishes the look of a transfer. This is
  310. * useful for both providing feedback while performing a drag operation and for
  311. * representing the transfer in a clipboard implementation that has a visual
  312. * appearance. The implementation of the <code>Icon</code> interface should
  313. * not alter the graphics clip or alpha level.
  314. * The icon implementation need not be rectangular or paint all of the
  315. * bounding rectangle and logic that calls the icons paint method should
  316. * not assume the all bits are painted. <code>null</code> is a valid return value
  317. * for this method and indicates there is no visual representation provided.
  318. * In that case, the calling logic is free to represent the
  319. * transferable however it wants.
  320. * <p>
  321. * The default Swing logic will not do an alpha blended drag animation if
  322. * the return is <code>null</code>.
  323. *
  324. * @param t the data to be transferred; this value is expected to have been
  325. * created by the <code>createTransferable</code> method
  326. * @return <code>null</code>, indicating
  327. * there is no default visual representation
  328. */
  329. public Icon getVisualRepresentation(Transferable t) {
  330. return null;
  331. }
  332. /**
  333. * Creates a <code>Transferable</code> to use as the source for
  334. * a data transfer. Returns the representation of the data to
  335. * be transferred, or <code>null</code> if the component's
  336. * property is <code>null</code>
  337. *
  338. * @param c the component holding the data to be transferred; this
  339. * argument is provided to enable sharing of <code>TransferHandler</code>s
  340. * by multiple components
  341. * @return the representation of the data to be transferred, or
  342. * <code>null</code> if the property associated with <code>c</code>
  343. * is <code>null</code>
  344. *
  345. */
  346. protected Transferable createTransferable(JComponent c) {
  347. PropertyDescriptor property = getPropertyDescriptor(c);
  348. if (property != null) {
  349. return new PropertyTransferable(property, c);
  350. }
  351. return null;
  352. }
  353. /**
  354. * Invoked after data has been exported. This method should remove
  355. * the data that was transferred if the action was <code>MOVE</code>.
  356. * <p>
  357. * This method is implemented to do nothing since <code>MOVE</code>
  358. * is not a supported action of this implementation
  359. * (<code>getSourceActions</code> does not include <code>MOVE</code>).
  360. *
  361. * @param source the component that was the source of the data
  362. * @param data The data that was transferred or possibly null
  363. * if the action is <code>NONE</code>.
  364. * @param action the actual action that was performed
  365. */
  366. protected void exportDone(JComponent source, Transferable data, int action) {
  367. }
  368. /**
  369. * Fetches the property descriptor for the property assigned to this transfer
  370. * handler on the given component (transfer handler may be shared). This
  371. * returns <code>null</code> if the property descriptor can't be found
  372. * or there is an error attempting to fetch the property descriptor.
  373. */
  374. private PropertyDescriptor getPropertyDescriptor(JComponent comp) {
  375. if (propertyName == null) {
  376. return null;
  377. }
  378. Class k = comp.getClass();
  379. BeanInfo bi;
  380. try {
  381. bi = Introspector.getBeanInfo(k);
  382. } catch (IntrospectionException ex) {
  383. return null;
  384. }
  385. PropertyDescriptor props[] = bi.getPropertyDescriptors();
  386. for (int i=0; i < props.length; i++) {
  387. if (propertyName.equals(props[i].getName())) {
  388. Method reader = props[i].getReadMethod();
  389. if (reader != null) {
  390. Class[] params = reader.getParameterTypes();
  391. if (params == null || params.length == 0) {
  392. // found the desired descriptor
  393. return props[i];
  394. }
  395. }
  396. }
  397. }
  398. return null;
  399. }
  400. /**
  401. * Fetches the data flavor from the array of possible flavors that
  402. * has data of the type represented by property type. Null is
  403. * returned if there is no match.
  404. */
  405. private DataFlavor getPropertyDataFlavor(Class k, DataFlavor[] flavors) {
  406. for(int i = 0; i < flavors.length; i++) {
  407. DataFlavor flavor = flavors[i];
  408. if ("application".equals(flavor.getPrimaryType()) &&
  409. "x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
  410. k.isAssignableFrom(flavor.getRepresentationClass())) {
  411. return flavor;
  412. }
  413. }
  414. return null;
  415. }
  416. private String propertyName;
  417. private static SwingDragGestureRecognizer recognizer = null;
  418. private static DropTargetListener dropLinkage = null;
  419. private static DropTargetListener getDropTargetListener() {
  420. if (dropLinkage == null) {
  421. dropLinkage = new DropHandler();
  422. }
  423. return dropLinkage;
  424. }
  425. static class PropertyTransferable implements Transferable {
  426. PropertyTransferable(PropertyDescriptor p, JComponent c) {
  427. property = p;
  428. component = c;
  429. }
  430. // --- Transferable methods ----------------------------------------------
  431. /**
  432. * Returns an array of <code>DataFlavor</code> objects indicating the flavors the data
  433. * can be provided in. The array should be ordered according to preference
  434. * for providing the data (from most richly descriptive to least descriptive).
  435. * @return an array of data flavors in which this data can be transferred
  436. */
  437. public DataFlavor[] getTransferDataFlavors() {
  438. DataFlavor[] flavors = new DataFlavor[1];
  439. Class propertyType = property.getPropertyType();
  440. String mimeType = DataFlavor.javaJVMLocalObjectMimeType + ";class=" + propertyType.getName();
  441. try {
  442. flavors[0] = new DataFlavor(mimeType);
  443. } catch (ClassNotFoundException cnfe) {
  444. flavors = new DataFlavor[0];
  445. }
  446. return flavors;
  447. }
  448. /**
  449. * Returns whether the specified data flavor is supported for
  450. * this object.
  451. * @param flavor the requested flavor for the data
  452. * @return true if this <code>DataFlavor</code> is supported,
  453. * otherwise false
  454. */
  455. public boolean isDataFlavorSupported(DataFlavor flavor) {
  456. Class propertyType = property.getPropertyType();
  457. if ("application".equals(flavor.getPrimaryType()) &&
  458. "x-java-jvm-local-objectref".equals(flavor.getSubType()) &&
  459. flavor.getRepresentationClass().isAssignableFrom(propertyType)) {
  460. return true;
  461. }
  462. return false;
  463. }
  464. /**
  465. * Returns an object which represents the data to be transferred. The class
  466. * of the object returned is defined by the representation class of the flavor.
  467. *
  468. * @param flavor the requested flavor for the data
  469. * @see DataFlavor#getRepresentationClass
  470. * @exception IOException if the data is no longer available
  471. * in the requested flavor.
  472. * @exception UnsupportedFlavorException if the requested data flavor is
  473. * not supported.
  474. */
  475. public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
  476. if (! isDataFlavorSupported(flavor)) {
  477. throw new UnsupportedFlavorException(flavor);
  478. }
  479. Method reader = property.getReadMethod();
  480. Object value = null;
  481. try {
  482. value = reader.invoke(component, null);
  483. } catch (Exception ex) {
  484. throw new IOException("Property read failed: " + property.getName());
  485. }
  486. return value;
  487. }
  488. JComponent component;
  489. PropertyDescriptor property;
  490. }
  491. /**
  492. * This is the default drop target for drag and drop operations if
  493. * one isn't provided by the developer. <code>DropTarget</code>
  494. * only supports one <code>DropTargetListener</code> and doesn't
  495. * function properly if it isn't set.
  496. * This class sets the one listener as the linkage of drop handling
  497. * to the <code>TransferHandler</code>, and adds support for
  498. * additional listeners which some of the <code>ComponentUI</code>
  499. * implementations install to manipulate a drop insertion location.
  500. */
  501. static class SwingDropTarget extends DropTarget implements UIResource {
  502. SwingDropTarget(JComponent c) {
  503. super();
  504. setComponent(c);
  505. try {
  506. super.addDropTargetListener(getDropTargetListener());
  507. } catch (TooManyListenersException tmle) {}
  508. }
  509. public void addDropTargetListener(DropTargetListener dtl) throws TooManyListenersException {
  510. // Since the super class only supports one DropTargetListener,
  511. // and we add one from the constructor, we always add to the
  512. // extended list.
  513. if (listenerList == null) {
  514. listenerList = new EventListenerList();
  515. }
  516. listenerList.add(DropTargetListener.class, dtl);
  517. }
  518. public void removeDropTargetListener(DropTargetListener dtl) {
  519. if (listenerList != null) {
  520. listenerList.remove(DropTargetListener.class, dtl);
  521. }
  522. }
  523. // --- DropTargetListener methods (multicast) --------------------------
  524. public void dragEnter(DropTargetDragEvent e) {
  525. super.dragEnter(e);
  526. if (listenerList != null) {
  527. Object[] listeners = listenerList.getListenerList();
  528. for (int i = listeners.length-2; i>=0; i-=2) {
  529. if (listeners[i]==DropTargetListener.class) {
  530. ((DropTargetListener)listeners[i+1]).dragEnter(e);
  531. }
  532. }
  533. }
  534. }
  535. public void dragOver(DropTargetDragEvent e) {
  536. super.dragOver(e);
  537. if (listenerList != null) {
  538. Object[] listeners = listenerList.getListenerList();
  539. for (int i = listeners.length-2; i>=0; i-=2) {
  540. if (listeners[i]==DropTargetListener.class) {
  541. ((DropTargetListener)listeners[i+1]).dragOver(e);
  542. }
  543. }
  544. }
  545. }
  546. public void dragExit(DropTargetEvent e) {
  547. super.dragExit(e);
  548. if (listenerList != null) {
  549. Object[] listeners = listenerList.getListenerList();
  550. for (int i = listeners.length-2; i>=0; i-=2) {
  551. if (listeners[i]==DropTargetListener.class) {
  552. ((DropTargetListener)listeners[i+1]).dragExit(e);
  553. }
  554. }
  555. }
  556. }
  557. public void drop(DropTargetDropEvent e) {
  558. super.drop(e);
  559. if (listenerList != null) {
  560. Object[] listeners = listenerList.getListenerList();
  561. for (int i = listeners.length-2; i>=0; i-=2) {
  562. if (listeners[i]==DropTargetListener.class) {
  563. ((DropTargetListener)listeners[i+1]).drop(e);
  564. }
  565. }
  566. }
  567. }
  568. public void dropActionChanged(DropTargetDragEvent e) {
  569. super.dropActionChanged(e);
  570. if (listenerList != null) {
  571. Object[] listeners = listenerList.getListenerList();
  572. for (int i = listeners.length-2; i>=0; i-=2) {
  573. if (listeners[i]==DropTargetListener.class) {
  574. ((DropTargetListener)listeners[i+1]).dropActionChanged(e);
  575. }
  576. }
  577. }
  578. }
  579. private EventListenerList listenerList;
  580. }
  581. private static class DropHandler implements DropTargetListener, Serializable {
  582. private boolean canImport;
  583. private boolean actionSupported(int action) {
  584. return (action & (COPY_OR_MOVE | LINK)) != NONE;
  585. }
  586. // --- DropTargetListener methods -----------------------------------
  587. public void dragEnter(DropTargetDragEvent e) {
  588. DataFlavor[] flavors = e.getCurrentDataFlavors();
  589. JComponent c = (JComponent)e.getDropTargetContext().getComponent();
  590. TransferHandler importer = c.getTransferHandler();
  591. if (importer != null && importer.canImport(c, flavors)) {
  592. canImport = true;
  593. } else {
  594. canImport = false;
  595. }
  596. int dropAction = e.getDropAction();
  597. if (canImport && actionSupported(dropAction)) {
  598. e.acceptDrag(dropAction);
  599. } else {
  600. e.rejectDrag();
  601. }
  602. }
  603. public void dragOver(DropTargetDragEvent e) {
  604. int dropAction = e.getDropAction();
  605. if (canImport && actionSupported(dropAction)) {
  606. e.acceptDrag(dropAction);
  607. } else {
  608. e.rejectDrag();
  609. }
  610. }
  611. public void dragExit(DropTargetEvent e) {
  612. }
  613. public void drop(DropTargetDropEvent e) {
  614. int dropAction = e.getDropAction();
  615. JComponent c = (JComponent)e.getDropTargetContext().getComponent();
  616. TransferHandler importer = c.getTransferHandler();
  617. if (canImport && importer != null && actionSupported(dropAction)) {
  618. e.acceptDrop(dropAction);
  619. try {
  620. Transferable t = e.getTransferable();
  621. e.dropComplete(importer.importData(c, t));
  622. } catch (RuntimeException re) {
  623. e.dropComplete(false);
  624. }
  625. } else {
  626. e.rejectDrop();
  627. }
  628. }
  629. public void dropActionChanged(DropTargetDragEvent e) {
  630. int dropAction = e.getDropAction();
  631. if (canImport && actionSupported(dropAction)) {
  632. e.acceptDrag(dropAction);
  633. } else {
  634. e.rejectDrag();
  635. }
  636. }
  637. }
  638. /**
  639. * This is the default drag handler for drag and drop operations that
  640. * use the <code>TransferHandler</code>.
  641. */
  642. private static class DragHandler implements DragGestureListener, DragSourceListener {
  643. private boolean scrolls;
  644. // --- DragGestureListener methods -----------------------------------
  645. /**
  646. * a Drag gesture has been recognized
  647. */
  648. public void dragGestureRecognized(DragGestureEvent dge) {
  649. JComponent c = (JComponent) dge.getComponent();
  650. TransferHandler th = c.getTransferHandler();
  651. Transferable t = th.createTransferable(c);
  652. if (t != null) {
  653. scrolls = c.getAutoscrolls();
  654. c.setAutoscrolls(false);
  655. try {
  656. dge.startDrag(null, t, this);
  657. return;
  658. } catch (RuntimeException re) {
  659. c.setAutoscrolls(scrolls);
  660. }
  661. }
  662. th.exportDone(c, t, NONE);
  663. }
  664. // --- DragSourceListener methods -----------------------------------
  665. /**
  666. * as the hotspot enters a platform dependent drop site
  667. */
  668. public void dragEnter(DragSourceDragEvent dsde) {
  669. }
  670. /**
  671. * as the hotspot moves over a platform dependent drop site
  672. */
  673. public void dragOver(DragSourceDragEvent dsde) {
  674. }
  675. /**
  676. * as the hotspot exits a platform dependent drop site
  677. */
  678. public void dragExit(DragSourceEvent dsde) {
  679. }
  680. /**
  681. * as the operation completes
  682. */
  683. public void dragDropEnd(DragSourceDropEvent dsde) {
  684. DragSourceContext dsc = dsde.getDragSourceContext();
  685. JComponent c = (JComponent)dsc.getComponent();
  686. if (dsde.getDropSuccess()) {
  687. c.getTransferHandler().exportDone(c, dsc.getTransferable(), dsde.getDropAction());
  688. } else {
  689. c.getTransferHandler().exportDone(c, dsc.getTransferable(), NONE);
  690. }
  691. c.setAutoscrolls(scrolls);
  692. }
  693. public void dropActionChanged(DragSourceDragEvent dsde) {
  694. }
  695. }
  696. private static class SwingDragGestureRecognizer extends DragGestureRecognizer {
  697. SwingDragGestureRecognizer(DragGestureListener dgl) {
  698. super(DragSource.getDefaultDragSource(), null, NONE, dgl);
  699. }
  700. void gestured(JComponent c, MouseEvent e, int srcActions, int action) {
  701. setComponent(c);
  702. setSourceActions(srcActions);
  703. appendEvent(e);
  704. fireDragGestureRecognized(action, e.getPoint());
  705. }
  706. /**
  707. * register this DragGestureRecognizer's Listeners with the Component
  708. */
  709. protected void registerListeners() {
  710. }
  711. /**
  712. * unregister this DragGestureRecognizer's Listeners with the Component
  713. *
  714. * subclasses must override this method
  715. */
  716. protected void unregisterListeners() {
  717. }
  718. }
  719. static final Action cutAction = new TransferAction("cut");
  720. static final Action copyAction = new TransferAction("copy");
  721. static final Action pasteAction = new TransferAction("paste");
  722. static class TransferAction extends AbstractAction implements UIResource {
  723. TransferAction(String name) {
  724. super(name);
  725. }
  726. public void actionPerformed(ActionEvent e) {
  727. Object src = e.getSource();
  728. if (src instanceof JComponent) {
  729. JComponent c = (JComponent) src;
  730. TransferHandler th = c.getTransferHandler();
  731. Clipboard clipboard = getClipboard(c);
  732. String name = (String) getValue(Action.NAME);
  733. Transferable trans = null;
  734. // any of these calls may throw IllegalStateException
  735. try {
  736. if ((clipboard != null) && (th != null) && (name != null)) {
  737. if ("cut".equals(name)) {
  738. th.exportToClipboard(c, clipboard, MOVE);
  739. } else if ("copy".equals(name)) {
  740. th.exportToClipboard(c, clipboard, COPY);
  741. } else if ("paste".equals(name)) {
  742. trans = clipboard.getContents(null);
  743. }
  744. }
  745. } catch (IllegalStateException ise) {
  746. // clipboard was unavailable
  747. UIManager.getLookAndFeel().provideErrorFeedback(c);
  748. return;
  749. }
  750. // this is a paste action, import data into the component
  751. if (trans != null) {
  752. th.importData(c, trans);
  753. }
  754. }
  755. }
  756. /**
  757. * Returns the clipboard to use for cut/copy/paste.
  758. */
  759. private Clipboard getClipboard(JComponent c) {
  760. if (SwingUtilities2.canAccessSystemClipboard()) {
  761. return c.getToolkit().getSystemClipboard();
  762. }
  763. Clipboard clipboard = (Clipboard)sun.awt.AppContext.getAppContext().
  764. get(SandboxClipboardKey);
  765. if (clipboard == null) {
  766. clipboard = new Clipboard("Sandboxed Component Clipboard");
  767. sun.awt.AppContext.getAppContext().put(SandboxClipboardKey,
  768. clipboard);
  769. }
  770. return clipboard;
  771. }
  772. /**
  773. * Key used in app context to lookup Clipboard to use if access to
  774. * System clipboard is denied.
  775. */
  776. private static Object SandboxClipboardKey = new Object();
  777. }
  778. }