Newsletter sign-up
View all newsletters

Sign up for our technology specific newsletters.

Enterprise Java
Email Address:

Java Tip 97: Add drag and drop to your JTrees

Improve JTree usability with drag and drop

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone

Page 3 of 4

Transferable

The Transferable interface is where the supported DataFlavors are defined and is used to obtain the transferable data. The data flavors are simply the different formats of data that can be transferred. They are most useful when transferring data, via drag and drop, out of or into the JVM environment. Transfers outside of the JVM use MIME formats, which are an Internet standard system for identifying types of encapsulated data.

Here are some examples of data flavors taken from java.awt.datatransfer.DataFlavor:

  • javaFileListFlavor: To transfer a list of files to/from Java (and the underlying platform), you use a DataFlavor of this type/subtype and representation class of java.util.List.
  • javaJVMLocalObjectMimeType: This flavor is used to transfer an arbitrary Java object reference with no associated MIME type within the same JVM.
  • javaRemoteObjectMimeType: To pass a live link to a remote object via a drag-and-drop ACTION_LINK operation, you should use a Mime Content Type of application/x-java-remote-object, in which the representation class of the DataFlavor represents the type of remote interface to be transferred.
  • javaSerializedObjectMimeType: A MIME Content Type of application/x-java-serialized-object represents a graph of Java object(s) that have been made persistent.
  • plainTextFlavor: The DataFlavor representing plain text with unicode encoding, where:

                  representationClass = InputStream
                  mimeType = "text/plain; charset=unicode"
    


  • stringFlavor: The DataFlavor representing a Java Unicode String class, where:

                  representationClass = java.lang.String
                  mimeType = "application/x-java-serialized-object"
    


The Transferable interface is implemented by any object hat can be transferred using drag and drop. However, if you are only transferring text, you can use the Java-predefined DataFlavor, java.awt.datatransfer.StringSelection. Otherwise, your new Transferable object must contain implementations of several methods, similar to the code fragments below:

     1   public Object getTransferData(DataFlavor df)
     2       throws UnsupportedFlavorException, IOException {
     3     if (df.equals( <SUPPORTED FLAVOR> ) == false)
     4       throw new UnsupportedFlavorException(df)
     5     return this;
     6     }


Line 3 compares the DataFlavor parameter with the flavor that class supports. The class only supports one flavor, so it makes one comparison. However, it could support several flavors. If that were the case, it would cycle through checking for equality. Upon finding the right flavor, it would return the corresponding data. In line 5, you return the data associated with the DataFlavor parameter. In this example, you simply return this, but that assumes the class is the data. The data could have been anything you wanted (as described by the DataFlavor).

The two other methods to implement are getTransferDataFlavors and isDataFlavorSupported. The purpose of those two methods is to return an array of supported flavors and return whether or not a specified flavor is supported.

     //Returns an array of DataFlavor objects indicating the
     //flavors the data can be provided in.
     DataFlavor[] getTransferDataFlavors() {
       return Flavors;
     }
     //Returns whether or not the specified data flavor is supported for
this object. boolean isDataFlavorSupported(DataFlavor flavor) { for (int i=0; i < Flavors.length; i++) if ( Flavor[i].equals(flavor) ) return true; return false; }


Using DataFlavor objects can be tricky. So here is an example of what Flavors and <SUPPORTED FLAVOR> might look like:

     //<SUPPORTED FLAVOR> example: Assumes that the transferable data in of type Integer
     final public static DataFlavor INTEGER_FLAVOR = new
         DataFlavor(Integer.class, "Integer Type");
     //An array of supported Flavors.
     static DataFlavor Flavors[] = {INTEGER_FLAVOR, <OTHER SUPPORTED FLAVORS> };


For each supported flavor in getTransferData, you should have a statement to return the flavor if it is requested.

Putting it all together

Now that you understand the basic elements, let's study how they work together. First, I'll take a closer look at drop-location-dependent cursor changes. For my example to be consistent, a male node may never be a valid drop-location, and hence you ought to change the cursor appropriately. Unfortunately in JTrees, that is not so simple. It is possible to change the cursor, using the DragSourceEvent received in the DragSourceListener interface methods, but the DragSourceContext also implements that interface and automatically updates the cursor. (I checked the DragSourceContext source code, and it does check to determine whether the cursor has been manually changed. However, that change was not evident in the cursor after running a test program). To fix the problem, you override the updateCurrentCursor method from DragSourceContext when you create the DragSource object. Lines 8 through 9 of the following code illustrate the patch.

      1     dragSource = new DragSource() {
      2       protected DragSourceContext createDragSourceContext(
      3          DragSourceContextPeer dscp, DragGestureEvent dgl, Cursor
dragCursor,
      4          Image dragImage, Point imageOffset, Transferable t,
      5          DragSourceListener dsl) {
      6            return new DragSourceContext(dscp, dgl, dragCursor,
dragImage, 
      7                                                      imageOffset,
t, dsl) {
      8                    protected void updateCurrentCursor(int dropOp,
      9                                      int targetAct, int status) {}
     10         };
     11       }
     12     };


You must overcome a second obstacle to make drop-location-dependent cursor changes; you need both the DragSourceListener and DropTargetListener events to change the cursor, based on location. You get the DragSourceContext object from the DragSourceListener dragOver event, which is the object used to set the cursor type, and you get the location of the cursor from the DropTargetListener dragOver event, which is used to determine the appropriate cursor type. My solution isn't elegant, but it works. I set a class global variable to the point obtained from DropTargetListener dragOver and then use that point to determine and set the cursor type in the DragSourceListener dragOver and dragEnter methods.

To carry out your drag-and-drop operations, you will build three classes called PersonalInfo, PersonNode, and DnDJTree. PersonalInfo will contain the Transferable data and PersonNode will subclass DefaultMutableTreeNode and implement the gender-specific GUI characteristics. Lastly, DnDJTree will subclass JTree and implement the drag-and-drop capabilities. You can implement a drag-and-drop JTree with fewer classes, but using a three-class scheme leaves less gray area between the GUI and data layers. The GUI layer consists of the PersonNode and DnDJTree classes. They concern only visual representation and contain no data about the content of each node. The data layer consists of just the PersonalInfo class, which is the Transferable object, and is the only data that gets transferred (see code>DnDJTree.dragGestureRecognized). The PersonNode class could subclass DefaultMutableTreeNode and include the PersonalInfo attributes. However, my scheme makes it easy to use a GUI other than JTree to represent the data, and since PersonalInfo is the Transferable class, it will be easy to implement drag and drop in an alternate GUI.

  • Digg
  • Reddit
  • SlashDot
  • Stumble
  • del.icio.us
  • Technorati
  • dzone
Comment
Login
Forgot your account info?
Add comment
Anonymous comments subject to approval. Register here for member benefits.
Have a JavaWorld account? Log in here. Register now for a free account.
Resources