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