[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

Jtree Drag&drop

Posted on 2000-01-13
9
Medium Priority
?
832 Views
Last Modified: 2008-03-17
Can anyone offer any insight on how to implement drag & drop in Jtree, so I can drag a node into a different location in the tree? (Java2)
tia for any help.
0
Comment
Question by:acsmith
9 Comments
 
LVL 5

Expert Comment

by:Jod
ID: 2351271
Try this example from the mining company...it is made up of a number of parts:

Draggable Tree
TransferableTreeNode
DroppableList
CustomCellRenderer

and a test program TreeTester.

Examples and explanation follows...




Draggable Tree

The DraggableTree implements the DragGestureListener interface and has a dragGestureRecognized(DragGestureEvent) method. This basically says when the start of the dragging has been recognized, find the selected node to drag and setup what you are dragging. I still haven't found a good reason for the DragSourceListener, so provide an empty stub. Unfortunately, you cannot just pass a null listener without getting a few exceptions thrown when dragging.

import javax.swing.*;
import javax.swing.tree.*;
import java.awt.*;
import java.awt.dnd.*;
import java.io.IOException;

public class DraggableTree extends JTree
    implements DragGestureListener {
  DragSource dragSource =
    DragSource.getDefaultDragSource();

  final static DragSourceListener dragSourceListener
    = new MyDragSourceListener();

  static class MyDragSourceListener
      implements DragSourceListener {
    public void dragDropEnd(
        DragSourceDropEvent DragSourceDropEvent) {
    }
    public void dragEnter(
        DragSourceDragEvent DragSourceDragEvent) {
    }
    public void dragExit(
        DragSourceEvent DragSourceEvent) {
    }
    public void dragOver(
        DragSourceDragEvent DragSourceDragEvent) {
    }
    public void dropActionChanged(
        DragSourceDragEvent DragSourceDragEvent) {
    }
  }

  public DraggableTree () {
    dragSource.createDefaultDragGestureRecognizer(
      this, DnDConstants.ACTION_COPY_OR_MOVE, this);
  }

  public DraggableTree (TreeModel model) {
    super (model);
    dragSource.createDefaultDragGestureRecognizer(
      this, DnDConstants.ACTION_COPY_OR_MOVE, this);
  }

  // DragGestureListener

  public void dragGestureRecognized(
      DragGestureEvent dragGestureEvent) {
    TreePath path = getSelectionPath();
    if (path == null) {
      // Nothing selected, nothing to drag
      System.out.println ("Nothing selected - beep");
      getToolkit().beep();
    } else {
      DefaultMutableTreeNode selection =
        (DefaultMutableTreeNode)
        path.getLastPathComponent();
      TransferableTreeNode node =
        new TransferableTreeNode(selection);
      dragSource.startDrag(
        dragGestureEvent,
        DragSource.DefaultCopyDrop,
        node,
        dragSourceListener);
    }
  }
}


TransferableTreeNode

The TransferableTreeNode is what is dragged around. Any DefaultMutableTreeNode can be dragged around, so you do not have to do anything special to create tree nodes just for draggable trees. Its primary job is to return the proper object from public Object getTransferData(DataFlavor flavor), based upon the requested flavor from the drop point. To support our custom flavor, we need to create a DataFlavor and be sure public DataFlavor[] getTransferDataFlavors() returns it, along with the other supported flavors.

Contrary to a popular rumor that is floating around, the draggable item (implementing the Transferable interface) does not have to support DataFlavor.stringFlavor. It can support only your custom flavor.

import javax.swing.tree.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.io.*;

public class TransferableTreeNode
    extends DefaultMutableTreeNode
    implements Transferable {
  final static int TREE = 0;
  final static int STRING = 1;
  final static int PLAIN_TEXT = 2;

  final public static DataFlavor
    DEFAULT_MUTABLE_TREENODE_FLAVOR =
      new DataFlavor(
        DefaultMutableTreeNode.class,
        "Default Mutable Tree Node");

  static DataFlavor flavors[] = {
    DEFAULT_MUTABLE_TREENODE_FLAVOR,
    DataFlavor.stringFlavor,
    DataFlavor.plainTextFlavor};
/* The following works fine
  static DataFlavor flavors[] = {
    DEFAULT_MUTABLE_TREENODE_FLAVOR
  };
*/

  private DefaultMutableTreeNode data;
                                       
  public TransferableTreeNode(
      DefaultMutableTreeNode data) {
    this.data = data;
  }

  public DataFlavor[] getTransferDataFlavors() {
   return flavors;
  }

  public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException,
      IOException {
    Object returnObject;
    if (flavor.equals(flavors[TREE])) {
      Object userObject = data.getUserObject();
      if (userObject == null) {
        returnObject = data;
      } else {
        returnObject = userObject;
      }
    } else if (flavor.equals(flavors[STRING])) {
      Object userObject = data.getUserObject();
      if (userObject == null) {
        returnObject = data.toString();
      } else {
        returnObject = userObject.toString();
      }
    } else if (flavor.equals(flavors[PLAIN_TEXT])) {
      Object userObject = data.getUserObject();
      String string;
      if (userObject == null) {
        string = data.toString();
      } else {
        string = userObject.toString();
      }
      returnObject = new ByteArrayInputStream(
        string.getBytes());
    } else {
      throw new UnsupportedFlavorException(flavor);
    }
    return returnObject;
  }
  public boolean isDataFlavorSupported(
      DataFlavor flavor) {
    boolean returnValue = false;
    for (int i=0, n=flavors.length; i<n; i++) {
      if (flavor.equals(flavors[i])) {
        returnValue = true;
        break;
      }
    }
    return returnValue;
  }
}


DroppableList

The DroppableList has changed a little from the last example. Instead of managing the data model within a Vector, it now uses a DefaultListModel to manage the elements. In addition, the list now supports the dropping of our custom flavor as well as DataFlavor.javaFileListFlavor. When getting a file list, the object dropped is a java.util.List of File objects.

import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import javax.swing.*;
import java.io.*;
import java.util.*;
import java.util.List;

public class DroppableList extends JList
    implements DropTargetListener {

  DropTarget dropTarget;

  public DroppableList() {
    dropTarget = new DropTarget (this, this);
    setModel(new DefaultListModel());
  }

  public void dragEnter (
      DropTargetDragEvent dropTargetDragEvent) {
    dropTargetDragEvent.acceptDrag (
      DnDConstants.ACTION_COPY_OR_MOVE);
  }

  public void dragExit (
      DropTargetEvent dropTargetEvent) {
  }

  public void dragOver (
      DropTargetDragEvent dropTargetDragEvent) {
  }

  public void dropActionChanged (
      DropTargetDragEvent dropTargetDragEvent) {
  }

  public synchronized void drop (
      DropTargetDropEvent dropTargetDropEvent) {
    try {
      Transferable tr =
        dropTargetDropEvent.getTransferable();
      if (tr.isDataFlavorSupported(
          TransferableTreeNode.
            DEFAULT_MUTABLE_TREENODE_FLAVOR)) {
        dropTargetDropEvent.acceptDrop (
          DnDConstants.ACTION_COPY_OR_MOVE);
        Object userObject = tr.getTransferData(
          TransferableTreeNode.
            DEFAULT_MUTABLE_TREENODE_FLAVOR);
        ((DefaultListModel)getModel()).
          addElement(userObject);
        dropTargetDropEvent.getDropTargetContext().
          dropComplete(true);
      } else if (tr.isDataFlavorSupported (
          DataFlavor.stringFlavor)) {
        dropTargetDropEvent.acceptDrop (
          DnDConstants.ACTION_COPY_OR_MOVE);
        String string = (String)tr.getTransferData (
          DataFlavor.stringFlavor);
        ((DefaultListModel)getModel()).addElement(
          string);
        dropTargetDropEvent.getDropTargetContext().
          dropComplete(true);
      } else if (tr.isDataFlavorSupported (
          DataFlavor.plainTextFlavor)) {
        dropTargetDropEvent.acceptDrop (
          DnDConstants.ACTION_COPY_OR_MOVE);
        Object stream = tr.getTransferData(
          DataFlavor.plainTextFlavor);
        if (stream instanceof InputStream) {
          InputStreamReader isr =
            new InputStreamReader((InputStream)stream);
          BufferedReader reader =
            new BufferedReader(isr);
          String line;
          while ((line = reader.readLine()) != null) {
            ((DefaultListModel)getModel()).
              addElement(line);
          }
          dropTargetDropEvent.getDropTargetContext().
            dropComplete(true);
        } else if (stream instanceof Reader) {
          BufferedReader reader =
            new BufferedReader((Reader)stream);
          String line;
          while ((line = reader.readLine()) != null) {
            ((DefaultListModel)getModel()).
              addElement(line);
          }
          dropTargetDropEvent.getDropTargetContext().
            dropComplete(true);
        } else {
          System.err.println ("Unknown type: " +
            stream.getClass());
          dropTargetDropEvent.rejectDrop();
        }
      } else if (tr.isDataFlavorSupported (
          DataFlavor.javaFileListFlavor)) {
        dropTargetDropEvent.acceptDrop (
          DnDConstants.ACTION_COPY_OR_MOVE);
        List fileList = (List)tr.getTransferData(
          DataFlavor.javaFileListFlavor);
        Iterator iterator = fileList.iterator();
        while (iterator.hasNext()) {
          File file = (File)iterator.next();
          Hashtable hashtable = new Hashtable();
          hashtable.put("name", file.getName());
          hashtable.put("url",
            file.toURL().toString());
          ((DefaultListModel)getModel()).
            addElement(hashtable);
        }
        dropTargetDropEvent.getDropTargetContext().
          dropComplete(true);
      } else {
        System.err.println ("Rejected");
        dropTargetDropEvent.rejectDrop();
      }
    } catch (IOException io) {
      io.printStackTrace();
      dropTargetDropEvent.rejectDrop();
    } catch (UnsupportedFlavorException ufe) {
      ufe.printStackTrace();
      dropTargetDropEvent.rejectDrop();
    }
  }
}


CustomCellRenderer

The CustomCellRenderer is what displays the tree and list cells. It handles the display of Hashtable for the tree's user data within the JTree or JList.

import java.awt.Component;
import java.util.Hashtable;
import javax.swing.*;
import javax.swing.tree.*;

public class CustomCellRenderer
    implements ListCellRenderer, TreeCellRenderer {
  DefaultListCellRenderer listCellRenderer =
    new DefaultListCellRenderer();
  DefaultTreeCellRenderer treeCellRenderer =
    new DefaultTreeCellRenderer();
  public Component getListCellRendererComponent(
      JList list, Object value, int index,
      boolean selected, boolean hasFocus) {
    listCellRenderer.getListCellRendererComponent(
      list, value, index, selected, hasFocus);
    listCellRenderer.setText(getValueString(value));
    return listCellRenderer;
  }
  public Component getTreeCellRendererComponent(
    JTree tree, Object value, boolean selected,
    boolean expanded, boolean leaf, int row,
    boolean hasFocus) {
    treeCellRenderer.getTreeCellRendererComponent(
      tree, value, selected, expanded, leaf, row,
      hasFocus);
    if (value instanceof DefaultMutableTreeNode) {
      DefaultMutableTreeNode node =
        (DefaultMutableTreeNode)value;
      value = node.getUserObject();
    }
    treeCellRenderer.setText(getValueString(value));
    return treeCellRenderer;
  }
  private String getValueString(Object value) {
    String returnString = "null";
    if (value != null) {
      if (value instanceof Hashtable) {
        Hashtable h = (Hashtable)value;
        String name = (String)h.get("name");
        String url = (String)h.get("url");
        returnString = name + " ==> " + url;
      } else {
        returnString = "X: " + value.toString();
      }
    }
    return returnString;
  }
}


TreeTester

The TreeTester testing program is a little different this time. The tree nodes need to be created and a renderer applied to the JTree and JList. Also, this time the draggable and droppable components are placed within a JSplitPane.

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.tree.*;
import java.util.Hashtable;

public class TreeTester {
  public static void main (String args[]) {
    JFrame f = new JFrame("Tree Dragging Tester");
    CustomCellRenderer renderer =
      new CustomCellRenderer();
    DraggableTree tree = new DraggableTree();
    tree.setModel(getDefaultTreeModel());
    tree.setCellRenderer(renderer);
    JScrollPane leftPane = new JScrollPane(tree);
    DroppableList list = new DroppableList();
    list.setCellRenderer(renderer);
    JScrollPane rightPane = new JScrollPane(list);
    JSplitPane splitPane = new JSplitPane(
      JSplitPane.HORIZONTAL_SPLIT, leftPane,
      rightPane);
    f.getContentPane().add (splitPane,
      BorderLayout.CENTER);
    f.setSize (400, 300);
    f.addWindowListener (new WindowAdapter() {
      public void windowClosing(WindowEvent e) {
        System.exit(0);
      }
    });
    f.setVisible (true);
  }
  private static TreeModel getDefaultTreeModel() {
    DefaultMutableTreeNode root =
      new DefaultMutableTreeNode("Drag Me");
    DefaultMutableTreeNode parent;

    parent = new DefaultMutableTreeNode("Auctions");
    root.add(parent);
    parent.add(new DefaultMutableTreeNode(
      makeNode("eBay", "http://www.ebay.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("EggHead", "http://www.egghead.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("First Auction",
        "http://www.firstauction.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("uBid", "http://www.ubid.com")));

    parent = new
      DefaultMutableTreeNode("Search Engines");
    root.add(parent);
    parent.add(new DefaultMutableTreeNode(
      makeNode("HotBot",
        "http://www.hotbot.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("Infoseek",
        "http://www.infoseek.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("Lycos",
        "http://www.lycos.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("Yahoo",
        "http://www.yahoo.com")));

    parent = new DefaultMutableTreeNode("Java");
    root.add(parent);
    parent.add(new DefaultMutableTreeNode(
      makeNode("Focus on Java",
        "http://java.about.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("JavaWorld",
        "http://www.javaworld.com")));
    parent.add(new DefaultMutableTreeNode(
      makeNode("Sun",
        "http://java.sun.com")));

    return new DefaultTreeModel(root);
  }
  private static Hashtable makeNode(String name,
      String url) {
    Hashtable hashtable = new Hashtable();
    hashtable.put("name", name);
    hashtable.put("url", url);
    return hashtable;
  }
}
0
 
LVL 1

Author Comment

by:acsmith
ID: 2352752
Thanks for this Jod. It's certainly a good starting point and has helped me understand a bit more about what's going on. I'll reject for the moment though, (although some points are coming your way!) because although I can now drag a node from a tree to another component, I need to be able to drag a node to a different place in the same tree. I have made the tree a drop target, and in the drop method, I have the node I'm dragging, but I can't work out how to add it to the node i'm dropping it onto (that is, I can't seem to get a reference to the node i'm dropping onto). Could you offer any more help?
TIA.
0
The new generation of project management tools

With monday.com’s project management tool, you can see what everyone on your team is working in a single glance. Its intuitive dashboards are customizable, so you can create systems that work for you.

 
LVL 2

Expert Comment

by:shaveri
ID: 2355379
0
 
LVL 1

Author Comment

by:acsmith
ID: 2358664
thanks shaveri, but i'm afraid this doesn't tell me anything that jod's original answer didn't.
Jod: please repost an answer to collect your points.
0
 
LVL 5

Expert Comment

by:Jod
ID: 2359022
I'm gonna have to build and develop this to get it to do exactly what you want, but I'm a bit short of time at the mo...I'll be back soon...
0
 
LVL 5

Accepted Solution

by:
Jod earned 800 total points
ID: 2370944
Bad news and Good news acsmith...first the bad news...

Firstly I have not had time to try and get this working. I also get the feeling it will not be as easy as it should be.


Secondly, there is a DnD bug in JTree, so check this out first before continuing:

http://developer.java.sun.com/developer/bugParade/bugs/4165577.html


Finally, though, if you need to know more about how to do it, the answer is in here - just needs to be implemented in the above example:

http://java.sun.com/products/jfc/tsc/articles/jtree/index.html
0
 
LVL 1

Author Comment

by:acsmith
ID: 2371427
Thanks Jod. I've managed to glean enough from your original answer to get me up and running. Now i've got my head round it, it's relatively easy.
cheers!
0
 
LVL 5

Expert Comment

by:Jod
ID: 2373908
As with all things Swing it's just a case of finding out which listeners to attach to what to generate the appropriate events.

The info could be more accessible though with some clearer examples.
0

Featured Post

Never miss a deadline with monday.com

The revolutionary project management tool is here!   Plan visually with a single glance and make sure your projects get done.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This was posted to the Netbeans forum a Feb, 2010 and I also sent it to Verisign. Who didn't help much in my struggles to get my application signed. ------------------------- Start The idea here is to target your cell phones with the correct…
In this post we will learn how to connect and configure Android Device (Smartphone etc.) with Android Studio. After that we will run a simple Hello World Program.
Viewers will learn about if statements in Java and their use The if statement: The condition required to create an if statement: Variations of if statements: An example using if statements:
The viewer will learn how to implement Singleton Design Pattern in Java.
Suggested Courses
Course of the Month9 days, 13 hours left to enroll

591 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question