I l@ve RuBoard |
![]() ![]() |
24.2 The Drop APIThis example uses several of the classes found in the java.awt.dnd package. Here's how they fit together (see Figure 24-3). Remember that we're just looking at the drop side of DnD. We'll explore the drag side in the next section and autoscrolling at the end of this chapter. Figure 24-3. DnD class diagram for the drop side![]() 24.2.1 The DropTarget ClassAn obvious starting point for playing with this API is the DropTarget class. This class encapsulates the functionality you add to regular GUI components so that they can respond to drop events. You can make just about anything a drop target. Well, not anything, but any Component can be a drop target. Typically, you pick the component that responds to the drop—such as the text area in our example—but you can also pick a proxy component that simply helps produce all the right events while your event handler plays with the dropped data. Note that just because the DropTarget class implements the DropTargetListener interface, this does not mean that the target is already handling drop events for you. The DropTarget class implementation of the interface supports the internal mechanisms for responding to events; it isn't used by us as programmers. We still have to create our own listener. 24.2.1.1 PropertiesTable 24-4 lists five properties of the DropTarget class that govern the context of this target and its data transfer capabilities.
The active property handles exactly what its name suggests: the active state of this target. If active is false, this target does not accept any dropped information. The component property references the component this target is using onscreen. Notice that you can set this property, too. You could have one DropTarget that uses a variety of components during different stages of your application. This is possible because dropTargetContext keeps a reference to the native peer supporting the drop target, and that native peer requires only that something onscreen can activate drop events. The defaultActions and flavorMap properties determine the types of transfers (move, copy, link) and the types of data (filenames, strings, objects, etc.) you accept, respectively. The complete details of the FlavorMap class are beyond the scope of this chapter, but we'll look at the pertinent aspects later when we create our own data to drag. Fortunately, it is often simple to figure out what "flavor" a dropped object uses. If you know more about the incoming types of data than Java does, you can supply your own flavor map, but usually, the convenience methods supplied by the DataFlavor class are sufficient. You can easily find other Java classes, byte streams, and filename lists. For example, we'll be working with lists of files created by your windowing system's native file manager. You can use the isFlavorJavaFileListType( ) method to determine whether the user dropped valid filename data into your application. Many similar methods exist for the primary types of information that your Java application can handle. 24.2.1.2 EventsA DropTarget produces DropTargetEvent objects as the user enters, moves over, and exits the target, and eventually drops information on the target. Depending on the specific action—dragging actions such as entering and exiting versus dropping—a subclass of DropTargetEvent may be used. In either case, you implement the DropTargetListener interface to respond to these events.
The DropTarget class itself implements the DropTargetListener interface (discussed in detail later), so it defines the following methods:
Note that this trapping behavior prevents the drop target from being its own listener. You'll receive an IllegalArgumentException if you try to add the target as its own listener. 24.2.1.3 ConstructorsSeveral constructors are available for creating DropTarget objects. Depending on how much of the default information you want (or how much information you need to supply at runtime after the DropTarget is instantiated), you can pick an appropriate constructor. You can create drop targets along with your components, or you can attach them at runtime. The latter might be useful if you have a dynamic interface because the visual component that accepts incoming drops can change.
Typically, supplying the component, a listener, and the operations you want to support is sufficient to get started. If your drop target is active only in certain situations, starting out with a disabled target may be useful. 24.2.2 The DnDConstants ClassThis quick little helper class defines the different types of actions (move, copy, and link) that are supported by the DnD framework. Notice that we say "DnD framework" and not just "DnD." The Java 2 API is ready for all of these actions, but your application or windowing system may not be. For example, while copy and move operations are universally understood, the link/reference operation is not. You might find that operation useful when dragging and dropping within a single application. 24.2.3 The DropTargetContext ClassAs mentioned earlier, DropTargetContext supports a DropTarget with all the useful implementations. This class provides you with access to the DnD peer. Most of the methods update the native peer to keep the DnD state of your windowing system consistent. For programmers, this class represents the primary location for interesting information when handling a drop or drag event. You can retrieve an instance of the context from any drop event and use it to provide user feedback. You can also use it to retrieve the drop target if you need it. 24.2.3.1 PropertiesReflecting the fact that this class serves primarily as peer support, many of its properties are protected (see Table 24-6).
The component and ropTarget properties are both publicly accessible and simply return references to the DropTarget object (and its Component) associated with this context. The currentDataFlavors and currentDataFlavorsAsList properties return the data flavors currently supported. This can be useful when updating a drag cursor. (For example, if the drop target cannot accept any of the current flavors, a "no drop" cursor can be displayed.) Which of these two properties you access depends entirely on your preference for using either an array of objects or a list. The targetActions property indicates the supported operations for the drop target. These actions can be any of the values from the DnDConstants class shown in Table 24-5. The transferable property is a bundled reference to the data being transferred by the DnD operation. The protected TransferableProxy inner class wraps Transferable objects and, if possible, provides efficiencies when dealing with local objects (which were dragged and dropped in the same virtual machine). However, programmers' access to this class is restricted to subclasses of the DropTargetContext class. In practice, you shouldn't worry about these classes since the event classes give you front-line access to most of the real action. But if you start low-level development on DnD support (for example, if you needed to remap the behavior of the native windowing system), you'll definitely need these classes. 24.2.4 The DropTargetListener InterfaceIf you want to respond to drop events, you need to implement the DropTargetListener interface and register with the DropTarget. You get only one listener per drop target. Unlike many other event handlers, which handle interfaces automatically, you must implement some of these interface methods yourself. Specifically, you need to accept or reject the operation generating the events. This acceptance or rejection keeps the visual clues—typically, the appearance of the mouse cursor—in sync with your application logic. We'll look at an example of this shortly. 24.2.4.1 MethodsThe DropTargetListener methods break a drop event into five categories. These methods are similar to the MouseListener and MouseMotionListener methods. They describe the key moments in a drop process. Note that several variations of DropTargetEvent are used as arguments. These events are described in more detail in the next section.
24.2.5 The DropTargetAdapter ClassAs with other listeners in the java.awt.event package, the DropTargetListener interface comes with a companion adapter: DropTargetAdapter. This class was introduced in 1.4 and simply implements DropTargetListener, providing null bodies for each of the listener's methods. 24.2.6 The DropTargetEvent ClassThe event-handling methods for a drop listener receive a DropTargetEvent. The DropTargetEvent class serves as the basis for the more specific events that are passed to the drop( ) and various drag methods. It provides access to the DropTargetContext through its only property, which is shown in Table 24-7.
As its name implies, this property gives you access to the DropTargetContext associated with the target that generated the event. You can use the context to get at the GUI component associated with the event. (The getSource( ) method returns the DropTarget object, not the component.) Depending on whether you pick up drag events or the final drop event, you get one of two DropTargetEvent subclasses: DropTargetDragEvent or DropTargetDropEvent. Both contain more or less the same information, but drop events give you the capacity to retrieve the transferred data. 24.2.7 The DropTargetDragEvent ClassAs the user drags a piece of information over your drop target, several drag events are generated. You can monitor these events and use them to dynamically control the cursor or the drop target. You have probably seen a cursor change to a big "not here!" symbol over invalid drop targets. 24.2.7.1 PropertiesThe properties of a DropTargetDragEvent object should give you all the information you need to make such changes. These properties are listed in Table 24-8.
The currentDataFlavors and currentDataFlavorsAsList properties give you access to the properties of the same name in the DropTargetContext class. The location property gives you the coordinates of the mouse pointer for this particular event. The dropAction property indicates which operation (move, copy, link) the user intends to perform while the sourceActions property indicates which operations the source of the data supports. If you find an incompatibility, you can reject the drag event using one of the drag methods described in the next section. 24.2.7.2 Drag methodsOnce you have looked at the event, you can decide whether to accept it. The following methods facilitate announcing that decision. As you accept( ) or reject( ) the drag, the cursor should follow suit to give the user proper visual feedback.
24.2.7.3 Transfer data methodThe DropTargetDragEvent also carries some information about the data wrapped up in the event.
24.2.8 The DropTargetDropEvent ClassThis class is very similar to the DropTargetDragEvent class, but now that we have a real drop, we can gain access to the information that the user was dragging, not just the meta-information. We can also use the DropTargetDropEvent class to signal the original drag source of a successful (or failed) drop. 24.2.8.1 PropertiesSimilar to the drag version, the properties for DropTargetDropEvent shown in Table 24-9 give you access to just about everything that's useful.
The currentDataFlavors , currentDataFlavorsAsList, dropAction, location, and sourceActions properties all mimic their respective counterparts in the DropTargetDragEvent class. There are also two new properties: localTransfer and transferable. The localTransfer property tells you whether the data being transferred came from the same virtual machine. You can use this information to process the transfer more efficiently. The transferable property is a Transferable object that represents the actual transferred data. Again, Transferable comes from the java.awt.datatransfer package, but we'll see some examples of extracting the data from a Transferable object in the code examples. 24.2.8.2 Drop methodsAs with the drag events, you can accept or reject drops.
24.2.8.3 Transfer data methodAs with the drag events, you can check for specific data flavor support.
24.2.9 Drop ExampleWell, after all that, here's the source code from the file list drop application in Figure 24-2. This example simply generates status messages for most of the methods, but it does correctly process the drop event in the drop( ) method. Later examples put more of the listener methods to use. /* * DropTest.java * A simple drop tester application */ import java.awt.*; import java.awt.dnd.*; import java.awt.datatransfer.*; import java.awt.event.*; import java.io.*; import java.util.*; import javax.swing.*; public class DropTest extends JFrame { DropTarget dt; JTextArea ta; public DropTest( ) { super("Drop Test"); setSize(300,300); setDefaultCloseOperation(EXIT_ON_CLOSE); // Make a quick label for instructions and create the // text area component. getContentPane( ).add( new JLabel("Drop a list from your file chooser here:"), BorderLayout.NORTH); ta = new JTextArea( ); getContentPane( ).add(ta, BorderLayout.CENTER); // Set up our text area to recieve drops. // This class handles drop events. dt = new DropTarget(ta, new DebugDropListener( )); setVisible(true); } public class DebugDropListener implements DropTargetListener { // For now, we'll just report ancilliary events to the console, // including dragEnter, dragExit, dragOver, and dropActionChanged. public void dragEnter(DropTargetDragEvent dtde) { System.out.println("Drag Enter"); } public void dragExit(DropTargetEvent dte) { System.out.println("Drag Exit"); } public void dragOver(DropTargetDragEvent dtde) { System.out.println("Drag Over"); } public void dropActionChanged(DropTargetDragEvent dtde) { System.out.println("Drop Action Changed"); } public void drop(DropTargetDropEvent dtde) { try { // Get the dropped object and try to figure out what it is. Transferable tr = dtde.getTransferable( ); DataFlavor[] flavors = tr.getTransferDataFlavors( ); for (int i = 0; i < flavors.length; i++) { System.out.println("Possible flavor: " + flavors[i].getMimeType( )); // Check for file lists specifically. if (flavors[i].isFlavorJavaFileListType( )) { // Great! Accept copy drops. dtde.acceptDrop(DnDConstants.ACTION_COPY); ta.setText("Successful file list drop.\n\n"); // Add the list of filenames to our text area. java.util.List list = (java.util.List)tr.getTransferData(flavors[i]); for (int j = 0; j < list.size( ); j++) { ta.append(list.get(j) + "\n"); } // If we made it this far, everything worked. dtde.dropComplete(true); return; } } // Hmm, the user must not have dropped a file list. System.out.println("Drop failed: " + dtde); dtde.rejectDrop( ); } catch (UnsupportedFlavorException e) { e.printStackTrace( ); dtde.rejectDrop( ); } catch (InvalidDnDOperationException e) { e.printStackTrace( ); dtde.rejectDrop( ); } catch (IOException e) { e.printStackTrace( ); dtde.rejectDrop( ); } } } public static void main(String args[]) { new DropTest( ); } } The interesting code for this application is found in two methods: the constructor and the drop( ) method. In the constructor, we build a simple application with a label and a text area. Only one line is needed to create a drop target and associate it with the text area: dt = new DropTarget(ta, new DebugDropListener( )); Now we have a drop target, and its associated component is the text area ta. The second argument is the DropTargetListener for the target. (We use an inner class to handle events in our example.) The drop( ) method deconstructs the dropped information. As mentioned before, this happens in a series of steps:
In our example, the first step is handled using a convenience method from the DataFlavor class. We look for a list of files specifically; if we find it, we proceed to the second step and call acceptDrop( ). Pulling the data out of the Transferable object is straightforward here, but we still must cast it as the appropriate type to do anything with it. (We'll see examples of other types of transferable data in the next section.) Finally, if nothing goes wrong, we mark the drop a success by calling dropComplete(true). If we didn't find a file list, or if an error occurred while trying to retrieve the data, we call rejectDrop( ). 24.2.10 Transferable ContentsThe DnD API uses the foundations laid by the system clipboard support in the java.awt.datatransfer package. We won't go into the gory details of data transfer here (we will see more of this package later), but we will expand our example to handle more than just a list of files: public void drop(DropTargetDropEvent dtde) { try { // Get the dropped object and try to figure out what it is. Transferable tr = dtde.getTransferable( ); DataFlavor[] flavors = tr.getTransferDataFlavors( ); for (int i = 0; i < flavors.length; i++) { System.out.println("Possible flavor: " + flavors[i].getMimeType( )); // Check for file lists specifically. if (flavors[i].isFlavorJavaFileListType( )) { // Great! Accept copy drops... dtde.acceptDrop(DnDConstants.ACTION_COPY); ta.setText("Successful file list drop.\n\n"); // Add the list of filenames to our text area. java.util.List list = (java.util.List)tr.getTransferData(flavors[i]); for (int j = 0; j < list.size( ); j++) { ta.append(list.get(j) + "\n"); } // If we made it this far, everything worked. dtde.dropComplete(true); return; } // Is it another Java object? else if (flavors[i].isFlavorSerializedObjectType( )) { dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); ta.setText("Successful Object drop.\n\n"); Object o = tr.getTransferData(flavors[i]); // Normally, we would try to cast o as something useful. For now, we just // want to print out a success message. ta.append("Object: " + o); dtde.dropComplete(true); return; } // How about an input stream? else if (flavors[i].isRepresentationClassInputStream( )) { dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE); ta.setText("Successful stream drop.\n\n"); ta.read(new InputStreamReader((InputStream)tr.getTransferData(flavors[i])), "from a drop"); dtde.dropComplete(true); return; } } System.out.println("Drop failed: " + dtde); dtde.rejectDrop( ); } catch (Exception e) { e.printStackTrace( ); dtde.rejectDrop( ); } } In this expanded drop( ) method, we check for two other common data transfer types: a Java Object and an InputStream. If the type is a Java Object, we can simply grab the transfer data directly using getTransferData( ). Presumably, your application knows which types of objects the user might be dragging in and can use instanceof tests to determine what it received. If the type is not an Object, we check to see if it can be represented by a stream. If so, we open an InputStreamReader and grab the data. Text built outside of another Java application can be transferred in this way. 24.2.11 The TransferHandler ClassIf you are working with SDK 1.4 or later, recall that you can handle incoming drops by simply setting the transferHandler property. A more complete example of this class in action appears later in this chapter in the Section 24.5 section, where we show you how to create your own Transferable type. 24.2.11.1 ConstantsSeveral constants are defined for the type of transfer you're performing. Those constants are shown in Table 24-10.
24.2.11.2 PropertiesThe TransferHandler class comes with several read-only properties shown in Table 24-11. The copyAction , cutAction, and pasteAction properties all return Action objects that behave like their namesakes. For example, getCutAction( ) returns an action that calls the exportToClipboard( ) method with a MOVE type. The sourceActions property provides a way to track the operations supported by the source of the drag. As an example, your drag source may not support a delete operation (which is required for the move action), so you could report only the COPY type. The visualRepresentation property offers you the opportunity to display a custom icon during the drag (but support for this is not guaranteed).
24.2.11.3 Constructors
24.2.11.4 Methods
![]() |
I l@ve RuBoard |
![]() ![]() |