24.2 The Drop API


This 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
figs/swng2.2403.gif

24.2.1 The DropTarget Class

An 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 Properties

Table 24-4 lists five properties of the DropTarget class that govern the context of this target and its data transfer capabilities.

Table 24-4. DropTarget properties

Property

Data type

get

is

set

Default value

active

boolean

 

·

·

true

component

Component

·

 

·

null

defaultActions

int

·

 

·

ACTION_COPY_OR_MOVE

dropTargetContext

DropTargetContext

·

   

null

flavorMap

FlavorMap

·

 

·

SystemFlavorMap.getDefaultFlavorMap( )

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 Events

A 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.

public void addDropTargetListener(DropTargetListener l) throws TooManyListenersException
public void removeDropTargetListener(DropTargetListener l)

These methods allow you to add or remove one listener at a time. Attempting to add more than one listener results in the TooManyListenersException.

The DropTarget class itself implements the DropTargetListener interface (discussed in detail later), so it defines the following methods:

public void dragEnter(DropTargetDragEvent dtde)
public void dragExit(DropTargetEvent dte)
public void dragOver(DropTargetDragEvent dtde)
public void drop(DropTargetDropEvent dtde)
public void dropActionChanged(DropTargetDragEvent dtde)

These methods all trap drop events before the registered listener gets them. If the drop target is not active, these methods return without notifying the listener. Otherwise, the registered listener is notified, and the autoscroll mechanism is updated. (More on autoscrolling later in this chapter.)

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 Constructors

Several 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.

public DropTarget( )

This constructor creates an "empty" drop target. It is currently an active target that supports copy or move operations. You still need to supply a component and a listener to make it useful.

public DropTarget(Component c, DropTargetListener dtl)
public DropTarget(Component c, int ops, DropTargetListener dtl)
public DropTarget(Component c, int ops, DropTargetListener dtl, boolean act)
public DropTarget(Component c, int ops, DropTargetListener dtl, boolean act, FlavorMap fm)

These methods supply various parts of a typical target. The arguments include the component associated with the drop target (c), the listener responding to drop events (dtl), the drop operations supported (ops copy and move are both supported by default; see Table 24-5 for the constant values to use for this argument), whether the target is active (act, which is true by default), and a flavor map to use (fm, which is null by default).

Table 24-5. DnDConstants

Constant

Data type

Description

ACTION_COPY

int

Supports copying of dragged item(s).

ACTION_COPY_OR_MOVE

int

Supports copying or moving of dragged item(s).

ACTION_LINK

ACTION_REFERENCE

int

Supports referencing dragged item(s). The definition of "reference" depends on your application and the native platform. (For example, you might use this operation to create a symbolic link or a shortcut to a file in a file browser application.) No direct mouse or keyboard action starts a link operation in the current implementation.

ACTION_MOVE

int

Supports moving dragged item(s).

ACTION_NONE

int

Does not support anything (i.e., ignore this action).

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 Class

This 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 Class

As 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 Properties

Reflecting the fact that this class serves primarily as peer support, many of its properties are protected (see Table 24-6).

Table 24-6. DropTargetContext properties

Property

Data type

get

is

set

Default value

component

Component

·

     

currentDataFlavorsp

DataFlavor[]

·

     

currentDataFlavorsAsListp

List

·

     

dropTarget

DropTarget

·

     

targetActionsp

int

·

 

·

 

transferablep

Transferable

·

     

pprotected

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 Interface

If 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 Methods

The 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.

public void dragEnter(DropTargetDragEvent dtde)

Called when the user drags an item over an active drop target. If you implement this method, you should indicate at some point in the code whether you will accept a drop using either the acceptDrag( ) or rejectDrag( ) method of dtde.

public void dragExit(DropTargetEvent dte)

Called when the user drags an item out of an active drop target.

public void dragOver(DropTargetDragEvent dtde)

Called continuously while the user is over (inside the boundaries of) an active drop target. If you implement this method, at some point in the code you should indicate whether you will accept a drop using the acceptDrag( ) or rejectDrag( ) method of dtde.

public void drop(DropTargetDropEvent dtde)

Called when the user drops a dragged item on an active drop target. All the hard work involved in accepting a drop starts here. As with the dragEnter( ) and dragOver( ) methods, you can initially accept or reject the drop, but eventually, you should terminate the drop process with a call to the dropComplete( ) method. Several examples of this method are presented throughout the rest of this chapter.

public void dropActionChanged(DropTargetDragEvent dtde)

Called when the user changes the type of the drop. The most common change is from copy to move (or vice versa) by pressing (or releasing) a modifier key during the drag operation.

24.2.5 The DropTargetAdapter Class

As 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 Class

The 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.

Table 24-7. DropTargetEvent property

Property

Data type

get

is

set

Default value

dropTargetContext

DropTargetContext

·

   

From constructor

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 Class

As 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 Properties

The 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.

Table 24-8. DropTargetDragEvent properties

Property

Data type

get

is

set

Default value

currentDataFlavors

DataFlavor[]

·

     

currentDataFlavorsAsList

List

·

     

dropAction

int

·

     

location

Point

·

     

sourceActions

int

·

     

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 methods

Once 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.

public void acceptDrag(int dragOperation)

This method indicates that you will accept the drag. The dragOperation argument dictates which operation is acceptable. If you want to accept both copy and move operations, you need to figure out which operation is currently underway and accept that operation. While you can syntactically accept an ACTION_COPY_OR_MOVE operation, you probably won't do the same thing for a copy as you would for a move. The example in Section 24.6 later in this chapter implements both copy and move logic.

public void rejectDrag( )

This method indicates that you will not accept the drag.

24.2.7.3 Transfer data method

The DropTargetDragEvent also carries some information about the data wrapped up in the event.

public boolean isDataFlavorSupported(DataFlavor df)

Similar to the currentDataFlavors property, this method gives you access to the method of the same name in the DropTargetContext. It returns true if df is a flavor that the drop target accepts.

24.2.8 The DropTargetDropEvent Class

This 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 Properties

Similar to the drag version, the properties for DropTargetDropEvent shown in Table 24-9 give you access to just about everything that's useful.

Table 24-9. DropTargetDropEvent properties

Property

Data type

get

is

set

Default value

currentDataFlavors

DataFlavor[]

·

     

currentDataFlavorsAsList

List

·

     

dropAction

int

·

     

localTransfer

boolean

 

·

   

location

Point

·

     

sourceActions

int

·

     

transferable

Transferable

·

     

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 methods

As with the drag events, you can accept or reject drops.

public void acceptDrop(int dropAction)

This method accepts a drop of type dropAction. If you decide to accept the drop, call this method, process the drop, and then call the dropComplete( ) method.

public void rejectDrop( )

This method rejects the drop.

public void dropComplete(boolean success)

This method tells the source of the drag that the drop is complete. The success argument should be true if the drop is successful; otherwise, it should be false.

24.2.8.3 Transfer data method

As with the drag events, you can check for specific data flavor support.

public boolean isDataFlavorSupported(DataFlavor df)

This method gives you access to the method of the same name in the DropTargetContext. It returns true if df is a flavor the drop target accepts.

24.2.9 Drop Example

Well, 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:

  1. Check the MIME type of the transfer to make sure it's acceptable.

  2. Accept the drop.

  3. Retrieve the data from the Transferable object.

  4. Mark the drop complete.

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 Contents

The 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 Class

If 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 Constants

Several constants are defined for the type of transfer you're performing. Those constants are shown in Table 24-10.

Table 24-10. TransferHandler constants

Constant

Data type

Description

COPY

int

Supports copying of dragged item(s)

COPY_OR_MOVE

int

Supports copying or moving dragged item(s)

MOVE

int

Supports moving dragged item(s)

NONE

int

Does not support anything (disables dragging)

24.2.11.2 Properties

The 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).

Table 24-11. TransferHandler properties

Property

Data type

get

is

set

Default value

copyActions

Action

·

   

Package-private subclass of AbstractAction

cutActions

Action

·

   

Package-private subclass of AbstractAction

pasteActions

Action

·

   

Package-private subclass of AbstractAction

sourceActions

int

·

   

NONE if no property descriptor; COPY otherwise

visualRepresentation

Icon

·

   

null

sstatic

24.2.11.3 Constructors
protected TransferHandler( )

A convenience constructor for subclasses.

public TransferHandler(String property)

This constructor sets up a handler based on the specified property. For example, the JColorChooser uses this type of handler for the color property. If you have JavaBeans, this is a simple way to drag and drop properties from one bean to another.

24.2.11.4 Methods
public void exportAsDrag(JComponent comp, InputEvent e, int action)
public void exportToClipboard(JComponent comp, Clipboard clip, int action)

These methods both start the export process. In the case of exportAsDrag( ), this means starting a drag event. In the case of exportToClipboard( ), this means using the specified clipboard (clip). The source of the event is given with comp, and the action (copy, link, or move) is also given.

protected void exportDone(JComponent source, Transferable data, int action)

This method is called at the completion of an export event. For drag events, this happens when the drop is completed (or rejected). For clipboards, it is called as soon as the clipboard contains the exported data (cut and copy actions do not wait for a corresponding paste action).

public boolean importData(JComponent comp, Transferable t)

This method does all the work on the import side. The source of the event (or target of the drop, if you prefer) is comp. The data is encapsulated in the Transferable object t. From t you can learn how to import the data or whether importing the data is necessary. You can then work with comp to accomplish the import. If the import succeeds, you should return true; return false otherwise.

public boolean canImport(JComponent comp, DataFlavor[] transferFlavors)

This straightforward method should return true if comp can import data via one of the flavors listed in transferFlavors. It should return false in all other cases.

protected Transferable createTransferable(JComponent c)

This is the workhorse for the export side. You should build (and return) a Transferable object based on the given component c. (If your handler is strictly for importing data, you can leave the default version of this method intact.)



Java Swing
Graphic Java 2: Mastering the Jfc, By Geary, 3Rd Edition, Volume 2: Swing
ISBN: 0130796670
EAN: 2147483647
Year: 2001
Pages: 289
Authors: David Geary

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net