I l@ve RuBoard |
![]() ![]() |
24.1 What Is Drag and Drop?If you have ever used a graphical file system manager to move a file from one folder to another, you have used Drag and Drop (often abbreviated DnD). The term "drag and drop" refers to a GUI action in which the end user "picks up" an object such as a file or piece of text by moving the cursor over the object, clicking the mouse button, and, without releasing the button, "dragging" the object to a particular area of the screen and releasing the mouse button to "drop" the object. This process is meant to extend the desktop metaphor. Just like your real desktop, you can rearrange things by picking them up, moving them, and dropping them in a filing cabinet, a trash can, an in-box, a shoebox, or the floor. Some programmers have added DnD functionality to individual components. For example, you might want to have a graphically rearrangeable JTree. Even without the DnD package, you can accomplish this with a clever bit of programming and a good deal of time. With the DnD package, however, not only do you not need the clever bit of programming, you are not limited to one component. You can drag information from one component to another in the same application, in two different Java applications (using separate JVMs), or even between your Java application and the native windowing system. 24.1.1 DnD and SDK 1.4SDK 1.4 introduced several new features that make minimal DnD functionality easy to program for simple cases. On the drag side, many components now have a new boolean property called dragEnabled. If you set this property to true, you can "export" information by dragging it away from the component. Table 24-1 shows the Swing components that support the dragEnabled property and the format of the information they export.
On the drop side, the text components all support basic drop functionality. Other components (JList, JTable, etc.) have a new property called transferHandler. If you set the transferHandler, you can easily accept dropped data of almost any type. Table 24-2 shows a list of the default import capabilities of several Swing components.
To get you going with the new "easy" stuff, let's start with an example. To show you the results of a drop, Figure 24-1 shows a screenshot of an all-encompassing transfer handler attached to a text area. You can drag all kinds of information: icons from your desktop, text from an editor, a chunk of cells from a spreadsheet, several items from a JTree in another window—just about anything. Drop your items in the text window, and it shows you the possible transfer flavors. If it knows how to handle any of the flavors (text, Java objects, etc.), it displays as much of the actual data as it can. Figure 24-1. UberHandler demo with a drop from a Microsoft Excel® spreadsheet![]() The source code for this handler follows. The (simple) GUI application you see in Figure 24-1 is built in the main( ) method of this handler. You could certainly ignore this application and attach the handler to a more elegant debugger of your own devising. // UberHandler.java import javax.swing.*; import java.awt.event.*; import java.awt.dnd.*; import java.awt.datatransfer.*; import java.io.*; public class UberHandler extends TransferHandler { JTextArea output; public void TransferHandler( ) { } public boolean canImport(JComponent dest, DataFlavor[] flavors) { // You bet we can! return true; } public boolean importData(JComponent src, Transferable transferable) { // Here's the tricky part. println("Receiving data from " + src); println("Transferable object is: " + transferable); println("Valid data flavors: "); DataFlavor[] flavors = transferable.getTransferDataFlavors( ); DataFlavor listFlavor = null; DataFlavor objectFlavor = null; DataFlavor readerFlavor = null; int lastFlavor = flavors.length - 1; // Check the flavors and see if we find one we like. If we do, save it. for (int f = 0; f <= lastFlavor; f++) { println(" " + flavors[f]); if (flavors[f].isFlavorJavaFileListType( )) { listFlavor = flavors[f]; } if (flavors[f].isFlavorSerializedObjectType( )) { objectFlavor = flavors[f]; } if (flavors[f].isRepresentationClassReader( )) { readerFlavor = flavors[f]; } } // Now try to display the content of the drop. try { DataFlavor bestTextFlavor = DataFlavor.selectBestTextFlavor(flavors); BufferedReader br = null; String line = null; if (bestTextFlavor != null) { println("Best text flavor: " + bestTextFlavor.getMimeType( )); println("Content:"); Reader r = bestTextFlavor.getReaderForText(transferable); br = new BufferedReader(r); line = br.readLine( ); while (line != null) { println(line); line = br.readLine( ); } br.close( ); } else if (listFlavor != null) { java.util.List list = (java.util.List)transferable.getTransferData(listFlavor); println(list); } else if (objectFlavor != null) { println("Data is a java object:\n" + transferable.getTransferData(objectFlavor)); } else if (readerFlavor != null) { println("Data is an InputStream:"); br = new BufferedReader((Reader)transferable.getTransferData(readerFlavor)); line = br.readLine( ); while (line != null) { println(line); } br.close( ); } else { // Don't know this flavor type yet println("No text representation to show."); } println("\n\n"); } catch (Exception e) { println("Caught exception decoding transfer:"); println(e); return false; } return true; } public void exportDone(JComponent src, Transferable data, int action) { // Just let us know when it occurs. System.err.println("Export Done."); } public void setOutput(JTextArea jta) { output = jta; } protected void print(Object o) { print(o.toString( )); } protected void print(String s) { if (output != null) { output.append(s); } else { System.out.println(s); } } protected void println(Object o) { println(o.toString( )); } protected void println(String s) { if (output != null) { output.append(s); output.append("\n"); } else { System.out.println(s); } } protected void println( ) { println(""); } public static void main(String args[]) { JFrame frame = new JFrame("Debugging Drop Zone"); frame.setSize(500,300); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JTextArea jta = new JTextArea( ); frame.getContentPane( ).add(new JScrollPane(jta)); UberHandler uh = new UberHandler( ); uh.setOutput(jta); jta.setTransferHandler(uh); frame.setVisible(true); } } 24.1.2 Programming with DnDSo how do we add more complex DnD functionality to a component? On the surface, it's very easy. DnD uses the same technique as most other GUI features: events. There are drag start events, drag source events, and drop events. To play with these events, implement the corresponding listener interfaces. Sound familiar? For example, to respond to a dropped object, we create an event handler that implements the DropTargetListener interface. What you do with the dropped object is the fun part. Just to prove that this stuff really works, let's take a look at a simple example of the drop listener (Figure 24-2). This application shows the names of any files you drag from your native windowing system's file manager. Figure 24-2. Two files dragged from a file manager onto our DropTest application![]() Admittedly, a screenshot does not do this test program justice. You really should compile and run DropTest.java to get the full effect. But trust us, it does work! We'll look at the source code for this example later in this section. With SDK 1.4, things get significantly easier, but again, more of that to come. First let's look at an overview of what's involved. You have three main areas to work with. A drop target accepts an incoming drag. The process of accepting the dragged information generates a series of events that you can respond to. The source of the dragged item might be your application, another Java application, or a native windowing system application such as your file manager. None of these matter to the drop target. Your application can also be a source for draggable items. This functionality requires the other two parts of DnD: a drag recognizer and a drag source. The drag recognizer is really just a glorified event adapter that listens for events that indicate if a drag has begun. In the simplest cases, this is usually a mouseDragged( ) event from the MouseMotionListener interface. (Later in this chapter, you will see how to make other events trigger a drag.) Once a drag is recognized, the drag source starts a drag and properly wraps the dragged information in an object that a drop target can recognize. Like the drop target, the information in a drag source is used inside your own application, another Java application, or a native application. Of course, all of these examples need to know what to do with the information once it arrives, but there are no a priori restrictions on where you drop your data. While you definitely should familiarize yourself with the DropTarget and DragSource classes, the driving force behind the DnD package is events. There are events for each of the three major players: drop target events, recognizer events, and drag source events. Use recognizer events to start the whole process. Your response to target and source events dictates the behavior of your application. Let's look at a typical series of events in DnD scenarios (see Table 24-3).
Throughout the entire process, you can determine whether the user wants to copy or move the information based on keyboard modifiers such as the Control key. We'll describe the API for all of these events in the course of this chapter. Let's get started! ![]() |
I l@ve RuBoard |
![]() ![]() |