|
When you use cut and paste to transmit information between two programs, the clipboard acts as an intermediary. The drag and drop metaphor cuts out the middleman and lets two programs communicate directly. The Java platform offers basic support for drag and drop. You can carry out drag and drop operations between Java applications and native applications. This section shows you how to write a Java application that is a drop target, and an application that is a drag source. Before going deeper into the Java platform support for drag and drop, let us quickly look at the drag and drop user interface. We use the Windows Explorer and WordPad programs as exampleson another platform, you can experiment with locally available programs with drag and drop capabilities. You initiate a drag operation with a gesture inside a drag sourceusually, by first selecting one or more elements and then dragging the selection away from its initial location (see Figure 7-51). Figure 7-51. Initiating a drag operationWhen you release the mouse button over a drop target that accepts the drop operation, the drop target queries the drag source for information about the dropped elements and initiates some operation. For example, if you drop a file icon from Windows Explorer to WordPad, then WordPad opens the file. However, if you drag a file icon on top of a directory icon in Windows Explorer, then Explorer moves the file into that directory. If you hold down the SHIFT or CTRL key while dragging, then the type of the drop action changes from a move action to a copy action, and a copy of the file is placed into the directory. If you hold down both SHIFT and CTRL keys, then a link to the file is placed into the directory. (Other platforms may use other keyboard combinations for these operations.) Thus, there are three types of drop actions with different gestures:
The intention of the link action is to establish a reference to the dropped element. Such links typically require support from the host operating system (such as symbolic links for files, or object linking for document components) and don't usually make a lot of sense in cross-platform programs. In this section, we focus on using drag and drop for copying and moving. There is usually some visual feedback for the drag operation. Minimally, the cursor shape changes. As the cursor moves over possible drop targets, the cursor shape indicates whether the drop is possible or not. If a drop is possible, the cursor shape also indicates the type of the drop action. Figure 7-52 shows several cursor shapes over drop targets. Figure 7-52. Cursor shapes over drop targetsDrop not allowed Move Copy Link You can also drag other elements besides file icons. For example, you can select text in WordPad and drag it. Try dropping text fragments into willing drop targets and see how they react. NOTE
Drop TargetsIn this section, we construct a simple Java application that is a drop target. The example program does nothing useful; it simply demonstrates how you can detect that a user would like to initiate a drop, how to accept the drop, and how to analyze the data that are being dropped. You can designate any AWT component to be a drop target. To do so, you construct a DropTarget object and pass the component and a drop target listener to the constructor. The constructor registers the object with the drag and drop system. DropTarget target = new DropTarget(component, listener); You can activate or deactivate the target with the setActive method. By default, the target is active. target.setActive(b); You can call the setDefaultActions method to set the following drop operations that you want to accept by default: DndConstants.ACTION_COPY DndConstants.ACTION_MOVE DndConstants.ACTION_COPY_OR_MOVE DndConstants.ACTION_LINK By default, all operations are allowed. Now you need to implement a drop target listener. The DropTargetListener interface has five methods: void dragEnter(DropTargetDragEvent event) void dragExit(DropTargetEvent event) void dragOver(DropTargetDragEvent event) void dropActionChanged(DropTargetDragEvent event) void drop(DropTargetDropEvent event) The dragEnter and dragExit methods are called when the cursor enters or exits the drop target component. In the dragEnter method, you can set the cursor shape to indicate whether the drop target is willing to accept the drop. You don't usually worry about the dragExit method. However, if you built up some elaborate mechanism for visual feedback, then you can dispose of it in this method. The dragOver method is called continuously as the user moves the mouse over the drop target component. You can use it to give detailed visual feedback about the effect of a drop. For example, if the drop target is a tree component, you can highlight the node under the cursor or even open up a subtree if the cursor hovers over a node. The dropActionChanged method is called if the user changes the action gesture. For example, if the user presses or lifts the SHIFT or CTRL keys while moving the mouse over the drop target component, then this method is called. That gives you a chance to modify the visual feedback and match it to the changed action. The most important method is the drop method. It is called when the user has committed to a drop by finishing the drop gesture, usually by releasing the mouse button. Note that this method is called whether or not you previously indicated that you would accept the drop. Look carefully at the parameters of the DropTargetListener methods. Three of them are a DropTargetDragEvent. However, the drop method receives a DropTargetDropEvent, and the dragExit method receives a DropTargetEvent. The DropTargetEvent class is the superclass of the other two event classes. It has only one method, getdropTargetContext, which returns a class with no interesting public methods. Since the purpose of a dragExit method is to clean up, you probably won't even look at the parameter. The DropTargetDragEvent and DropTargetDropEvent classes each have the following methods: int getDropAction() Point getLocation() DataFlavor[] getCurrentDataFlavors() boolean isDataFlavorSupported(DataFlavor flavor) You need these methods to test whether to encourage a drag or allow a drop, and to specify how to give visual feedback. Unfortunately, because the methods are not available in the common superclass, you'll have to implement the test logic twice, once for the drag and then again for the drop. If you don't want to encourage a drag, you call the rejectDrag method of the DropTargetDragEvent class in the dragEnter or dropActionChanged method. As a result, the cursor changes to a warning icon. If the user drops an item despite the warning, then you call the rejectDrop method of the DropTargetDropEvent class. The getdropAction method returns the drop action that the user intends to carry out. In many drag and drop operations, you don't want the action taken literally. For example, if you move a file icon into WordPad, then you don't want the drag source to delete the file. But you also don't want the drop target to insist that the user hold down a key when dragging. In this situation, the drop target should accept either copy or move actions. In the drop method, call the acceptDrop method of the DropTargetDropEvent with the actual action. If you call event.acceptDrop(DnDConstants.ACTION_MOVE); // drag source deletes dragged items! then the drag source will delete the dragged items. TIP
Here is an overview of a typical drop target listener:
Once you accept a drop, you need to analyze the data from the drag source. The gettransferable method of the DropTargetDropEvent class returns a reference to a transferable object. This is the same interface that is used for copy and paste. One data type that is more commonly used for drag and drop than for copy and paste is the DataFlavor.javaFileListFlavor. A file list describes a set of file icons that was dropped onto the target. The transferable object yields an object of type java.util.List whose items are File objects. Here is the code for retrieving the files:
Another flavor that can be dropped is text. You can retrieve the text in various flavors. The most convenient is DataFlavor.stringFlavor. The other flavors have a MIME type of text/plain and a variety of representation classes, including InputStream and [B (byte array). CAUTION
Depending on the drag source, you might also find data in other formats such as text/html text/rtf To read the data in that format, pick a convenient flavor, for example, an input stream, and obtain the data like this:
Our sample program does not attempt to do anything useful. It simply lets you drop items onto a text area. When you start dragging over the text area, the drop action is displayed. Once you initiate a drop, the dropped data are displayed. If the data are in text format, the program reads both ascii and unicode encodings. In our sample program, we do not give any visual feedback of the dragging process beyond the change to a warning cursor that automatically happens when the rejectDrag method is called. This program simply gives you a drop target for experimentation. Try dropping a selection of file names from Windows Explorer or a text fragment from WordPad (see Figure 7-53). Also see how a link attempt is rejected. If you press both the SHIFT and CTRL keys, then a warning icon appears when you drag an item over the text area. Figure 7-53. The DropTargetTest programExample 7-20 shows the complete program. Example 7-20. DropTargetTest.java[View full width] 1. import java.awt.*; 2. import java.awt.datatransfer.*; 3. import java.awt.event.*; 4. import java.awt.dnd.*; 5. import java.io.*; 6. import java.util.*; 7. import javax.swing.*; 8. 9. /** 10. This is a test class to test drag and drop behavior. Drop items into the text area to see the 11. MIME types of the drop target. 12. */ 13. public class DropTargetTest 14. { 15. public static void main(String[] args) 16. { 17. JFrame frame = new DropTargetFrame(); 18. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 19. frame.setVisible(true); 20. } 21. } 22. 23. /** 24. This frame contains a text area that is a simple drop target. 25. */ 26. class DropTargetFrame extends JFrame 27. { 28. public DropTargetFrame() 29. { 30. setTitle("DropTarget"); 31. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 32. 33. JTextArea textArea = new JTextArea("Drop items into this text area.\n"); 34. 35. new DropTarget(textArea, new TextDropTargetListener(textArea)); 36. add(new JScrollPane(textArea), "Center"); 37. } 38. 39. private static final int DEFAULT_WIDTH = 300; 40. private static final int DEFAULT_HEIGHT = 300; 41. } 42. 43. /** 44. This listener displays the properties of a dropped object. 45. */ 46. class TextDropTargetListener implements DropTargetListener 47. { 48. /** 49. Constructs a listener. 50. @param aTextArea the text area in which to display the 51. properties of the dropped object. 52. */ 53. public TextDropTargetListener(JTextArea aTextArea) 54. { 55. textArea = aTextArea; 56. } 57. 58. public void dragEnter(DropTargetDragEvent event) 59. { 60. int a = event.getDropAction(); 61. if ((a & DnDConstants.ACTION_COPY) != 0) 62. textArea.append("ACTION_COPY\n"); 63. if ((a & DnDConstants.ACTION_MOVE) != 0) 64. textArea.append("ACTION_MOVE\n"); 65. if ((a & DnDConstants.ACTION_LINK) != 0) 66. textArea.append("ACTION_LINK\n"); 67. 68. if (!isDragAcceptable(event)) 69. { 70. event.rejectDrag(); 71. return; 72. } 73. } 74. 75. public void dragExit(DropTargetEvent event) 76. { 77. } 78. 79. public void dragOver(DropTargetDragEvent event) 80. { 81. // you can provide visual feedback here 82. } 83. 84. public void dropActionChanged(DropTargetDragEvent event) 85. { 86. if (!isDragAcceptable(event)) 87. { 88. event.rejectDrag(); 89. return; 90. } 91. } 92. 93. public void drop(DropTargetDropEvent event) 94. { 95. if (!isDropAcceptable(event)) 96. { 97. event.rejectDrop(); 98. return; 99. } 100. 101. event.acceptDrop(DnDConstants.ACTION_COPY); 102. 103. Transferable transferable = event.getTransferable(); 104. 105. DataFlavor[] flavors = transferable.getTransferDataFlavors(); 106. for (int i = 0; i < flavors.length; i++) 107. { 108. DataFlavor d = flavors[i]; 109. textArea.append("MIME type=" + d.getMimeType() + "\n"); 110. 111. try 112. { 113. if (d.equals(DataFlavor.javaFileListFlavor)) 114. { 115. java.util.List<File> fileList 116. = (java.util.List<File>) transferable.getTransferData(d); 117. for (File f : fileList) 118. { 119. textArea.append(f + "\n"); 120. } 121. } 122. else if (d.equals(DataFlavor.stringFlavor)) 123. { 124. String s = (String) transferable.getTransferData(d); 125. textArea.append(s + "\n"); 126. } 127. } 128. catch (Exception e) 129. { 130. textArea.append(e + "\n"); 131. } 132. } 133. textArea.append("\n"); 134. event.dropComplete(true); 135. } 136. 137. public boolean isDragAcceptable(DropTargetDragEvent event) 138. { 139. // usually, you check the available data flavors here 140. // in this program, we accept all flavors 141. return (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0; 142. } 143. 144. public boolean isDropAcceptable(DropTargetDropEvent event) 145. { 146. // usually, you check the available data flavors here 147. // in this program, we accept all flavors 148. return (event.getDropAction() & DnDConstants.ACTION_COPY_OR_MOVE) != 0; 149. } 150. 151. private JTextArea textArea; 152. } java.awt.dnd.DropTarget 1.2
java.awt.dnd.DropTargetListener 1.2
java.awt.dnd.DropTargetDragEvent 1.2
java.awt.dnd.DropTargetDropEvent 1.2
Drag SourcesNow that you saw how to implement a program that contains a drop target, we show you how to implement a drag source. The program in Example 7-21 fills a JList with all files in the current directory (see Figure 7-54). The list component is a drag source. You can drag file items from the list component to any drop target that is willing to accept a list of files. Figure 7-54. The DragSourceTest programTo turn a component into a drag source, obtain a DragSource objectyou can simply call the static DragSource.getDefaultDragSource method. Then, call the createDefaultDragGestureRecognizer method and supply it with
For example, DragSource dragSource = DragSource.getDefaultDragSource(); dragSource.createDefaultDragGestureRecognizer(component, DnDConstants.ACTION_COPY_OR_MOVE, dragGestureListener); The DragGestureListener interface has a single method, dragGestureRecognized. The gesture recognizer calls that method as soon as it has noticed that the user wants to initiate a drag operation. In that method, you build the transferable object that the drop target will ultimately read in its drop method. Once you have assembled the transferable object, you call the startDrag method of the DragGestureEvent class. You supply an optional cursor, or null if you want to use the default drag cursor, followed by the transferable object and an object that implements the DragSourceListener interface. For example, event.startDrag(null, transferable, dragSourceListener); You then do the usual busywork of defining a transferable wrappersee the code in the example program for details. The drag source listener is notified repeatedly as the drag operation progresses. The interface has five methods: void dragEnter(DragSourceDragEvent event) void dragOver(DragSourceDragEvent event) void dragExit(DragSourceEvent event) void dropActionChanged(DragSourceDragEvent event) void dragDropEnd(DragSourceDropEvent event) You can use the first four methods to give the user visual feedback of the drag operation. However, generally, such feedback should be the role of the drop target. Only the last method, dragDropEnd, is important. This method is called when the drop method has finished. For a move operation, you check whether the drop has succeeded. In that case, you update the drag source. (For a copy operation, you probably don't have to do anything.) NOTE
Here is the dragDropEnd method for our example program. When a move has succeeded, we remove the moved items from the list model. public void dragDropEnd(DragSourceDropEvent event) { if (event.getDropSuccess()) { int action = event.getDropAction(); if (action == DnDConstants.ACTION_MOVE) { for (Object v : draggedValues) model.removeElement(v); } } } In this method, we rely on the drop method to tell us what drop was actually carried out. Recall that the drop method can change a move action to a copy action if the source allowed both actions. The event.geTDropAction of the DragSourceDropEvent class returns the action that the drop target reported when calling the acceptDrop method of the DropTargetDropEvent. Try out the program in Example 7-21 and drag file items to various drop targets, such as the program in Example 7-20 or a native program such as Windows Explorer or WordPad. NOTE
CAUTION
As you have seen, support for the system clipboard and the drag and drop mechanism are still very much a work in progress. The basics work on all platforms, but future versions of the Java platform will, we hope, offer more robust and comprehensive support. In this section, we have covered the basic mechanics of the drag and drop mechanism. For more information, particularly about programming visual feedback, we recommend Core Swing: Advanced Programming by Kim Topley [Prentice Hall 1999]. Example 7-21. DragSourceTest.java1. import java.awt.*; 2. import java.awt.datatransfer.*; 3. import java.awt.dnd.*; 4. import java.awt.event.*; 5. import java.io.*; 6. import java.util.*; 7. import java.util.List; 8. import javax.swing.*; 9. 10. 11. /** 12. This is a sample drag source for testing purposes. It consists of a list of files 13. in the current directory. 14. */ 15. public class DragSourceTest 16. { 17. public static void main(String[] args) 18. { 19. JFrame frame = new DragSourceFrame(); 20. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 21. frame.setVisible(true); 22. } 23. } 24. 25. /** 26. This frame contains a list of files in the current 27. directory with support for dragging files to a drop target. 28. Moved files are removed from the list. 29. */ 30. class DragSourceFrame extends JFrame 31. { 32. public DragSourceFrame() 33. { 34. setTitle("DragSourceTest"); 35. setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); 36. 37. File f = new File(".").getAbsoluteFile(); 38. File[] files = f.listFiles(); 39. model = new DefaultListModel(); 40. for (File file : files) 41. try 42. { 43. model.addElement(file.getCanonicalFile()); 44. } 45. catch (IOException e) 46. { 47. JOptionPane.showMessageDialog(this, e); 48. } 49. fileList = new JList(model); 50. add(new JScrollPane(fileList), BorderLayout.CENTER); 51. add(new JLabel("Drag files from this list"), BorderLayout.NORTH); 52. 53. DragSource dragSource = DragSource.getDefaultDragSource(); 54. dragSource.createDefaultDragGestureRecognizer(fileList, 55. DnDConstants.ACTION_COPY_OR_MOVE, new 56. DragGestureListener() 57. { 58. public void dragGestureRecognized(DragGestureEvent event) 59. { 60. draggedValues = fileList.getSelectedValues(); 61. Transferable transferable = new FileListTransferable(draggedValues); 62. event.startDrag(null, transferable, new FileListDragSourceListener()); 63. } 64. }); 65. } 66. 67. /** 68. A drag source listener that removes moved files from the file list. 69. */ 70. private class FileListDragSourceListener 71. extends DragSourceAdapter 72. { 73. public void dragDropEnd(DragSourceDropEvent event) 74. { 75. if (event.getDropSuccess()) 76. { 77. int action = event.getDropAction(); 78. if (action == DnDConstants.ACTION_MOVE) 79. { 80. for (Object v : draggedValues) 81. model.removeElement(v); 82. } 83. } 84. } 85. } 86. 87. private JList fileList; 88. private DefaultListModel model; 89. private Object[] draggedValues; 90. private static final int DEFAULT_WIDTH = 300; 91. private static final int DEFAULT_HEIGHT = 200; 92. } 93. 94. class FileListTransferable implements Transferable 95. { 96. public FileListTransferable(Object[] files) 97. { 98. fileList = new ArrayList<Object>(Arrays.asList(files)); 99. } 100. 101. public DataFlavor[] getTransferDataFlavors() 102. { 103. return flavors; 104. } 105. 106. public boolean isDataFlavorSupported(DataFlavor flavor) 107. { 108. return Arrays.asList(flavors).contains(flavor); 109. } 110. 111. public Object getTransferData(DataFlavor flavor) 112. throws UnsupportedFlavorException 113. { 114. if(flavor.equals(DataFlavor.javaFileListFlavor)) 115. return fileList; 116. else if(flavor.equals(DataFlavor.stringFlavor)) 117. return fileList.toString(); 118. else 119. throw new UnsupportedFlavorException(flavor); 120. } 121. 122. private static DataFlavor[] flavors = 123. { 124. DataFlavor.javaFileListFlavor, 125. DataFlavor.stringFlavor 126. }; 127. 128. private java.util.List<Object> fileList; 129. } java.awt.dnd.DragSource 1.2
java.awt.dnd.DragGestureListener 1.2
java.awt.dnd.DragGestureEvent 1.2
java.awt.dnd.DragSourceListener 1.2
java.awt.dnd.DragSourceDropEvent 1.2
Data Transfer Support in SwingStarting with JDK 1.4, Swing components have built-in support for data transfer. That frees programmers from much of the burden of implementing copy and paste or drag and drop. For example, start the TableSelectionTest program from Chapter 6 and highlight a range of cells. Then press CTRL+C and paste the clipboard contents into a text editor. The result is HTML-formatted text like this: <html> <table> <tr> <th id=2>C <th id=3>D <th id=4>E <th id=5>F <tr id=3> <td>12 <td>16 <td>20 <td>24 . . . </table> </html> If you add the line table.setDragEnabled(true); after the table constructor and recompile the program, then you can drag the selection area to drop targets. The drop target receives the table selection as HTML-formatted data. Table 7-6 summarizes the Swing components that are sources and targets for data transfer. The standard Cut, Copy, and Paste keyboard shortcuts are enabled for all components except the JColorChooser. Dragging is not enabled by default. You must call the setDragEnabled method to activate it.
The Swing package provides a potentially useful mechanism to quickly turn a component into a drop target. If the component has a method
then you turn it into a drop target for data flavors with representation class Type simply by calling
When a drop occurs, then the transfer handler checks whether one of the data flavors has representation class Type. If so, it invokes the setName method. NOTE
For example, suppose you want to use drag and drop to change the background color of a text field. You need a transfer handler that invokes the method void setBackground(Color c) when a Color object is dragged onto the text field. Simply call textField.setTransferHandler(new TransferHandler("background")); Example 7-22 demonstrates this behavior. As you can see in Figure 7-55, the top of the frame contains a color chooser, and the bottom, a text field with the text "Drag color here." You drag the color from the inside of the Preview panel, not from one of the color swatches. When you drag it onto the text field, its background color changes. Figure 7-55. The Swing drag and drop test programCAUTION
Example 7-22. SwingDnDTest.java1. import java.awt.*; 2. import javax.swing.*; 3. 4. /** 5. This program demonstrates how to easily add data transfer 6. capabilities to Swing components. Drag a color from the 7. "Preview" panel of the color chooser into the text field. 8. */ 9. public class SwingDnDTest 10. { 11. public static void main(String[] args) 12. { 13. JFrame frame = new SwingDnDFrame(); 14. frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 15. frame.setVisible(true); 16. } 17. } 18. 19. /** 20. This frame contains a color chooser and a text field. Dragging 21. a color into the text field changes its background color. 22. */ 23. class SwingDnDFrame extends JFrame 24. { 25. public SwingDnDFrame() 26. { 27. setTitle("SwingDnDTest"); 28. 29. JColorChooser chooser = new JColorChooser(); 30. chooser.setDragEnabled(true); 31. add(chooser, BorderLayout.CENTER); 32. JTextField textField = new JTextField("Drag color here"); 33. textField.setDragEnabled(true); 34. textField.setTransferHandler(new TransferHandler("background")); 35. add(textField, BorderLayout.SOUTH); 36. pack(); 37. } 38. } javax.swing.JComponent 1.2
javax.swing.TransferHandler 1.4
javx.swing.JFileChooser 1.2 javax.swing.JColorChooser 1.2 javax.swing.JTextComponent 1.2 javax.swing.JList 1.2 javax.swing.JTable 1.2 javax.swing.JTree 1.2
|
|