Hi Gurus,
I am implementing simple drag n drop for mutabletreenodes between
JTrees. I simply want to copy nodes from on JTree to another by
dragging them over.
Now this works fine when I am using String as userObject in the
mutabletreenode. But if I use my own complex userObject (that overrides
toString) the node gets copied over to the destination tree, but its
userObject is set to null. So there isnt any label displayed beside the
node.
I am thinkin one of 2 things :
1. some dumb stupid mistake on my part.
2. objects that are being d-n-ded must be Serializable
Here is a sample program that demos the problem.
Thanks a lot in advance.
3 files total
--------------------------------------------------------------------
DNDTreeTest.java
import java.awt.FlowLayout;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class DNDTreeTest extends JFrame {
public static void main(String[] args) {
DNDTreeTest demo = new DNDTreeTest();
Object[] hierarchy =
{ "javax.swing",
"javax.swing.border",
"javax.swing.colorchooser",
"javax.swing.event",
"javax.swing.filechooser",
new Object[] { "javax.swing.plaf",
"javax.swing.plaf.basic",
"javax.swing.plaf.metal",
"javax.swing.plaf.multi" },
"javax.swing.table",
new Object[] { "javax.swing.text",
new Object[] { "javax.swing.text.html",
"javax.swing.text.html.parser"
},
"javax.swing.text.rtf" },
"javax.swing.tree",
"javax.swing.undo" };
DefaultMutableTreeNode root = demo.processHierarchy(hierarchy);
demo.setTitle("DND Tree Demo");
DragSourceJTree src = new DragSourceJTree(new
DefaultTreeModel(root));
DropTargetJTree dest = new DropTargetJTree(new
DefaultTreeModel(root));
demo.getContentPane().setLayout(new FlowLayout());
demo.getContentPane().add(src);
demo.getContentPane().add(dest);
demo.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent arg0) {
System.exit(0);
}
});
demo.pack();
demo.show();
}
private DefaultMutableTreeNode processHierarchy(Object[] hierarchy)
{
MyConcept c = new MyConcept();
c.setText(hierarchy[0].toString());
DefaultMutableTreeNode node =
new DefaultMutableTreeNode(c);
DefaultMutableTreeNode child;
for(int i=1; i<hierarchy.length; i++) {
Object nodeSpecifier = hierarchy[i];
if (nodeSpecifier instanceof Object[]) // Ie node with children
child = processHierarchy((Object[])nodeSpecifier);
else
{
MyConcept c1 = new MyConcept();
c1.setText(nodeSpecifier.toString());
child = new DefaultMutableTreeNode(c1); // Ie Leaf
// child = new DefaultMutableTreeNode(nodeSpecifier); // Ie Leaf
}
node.add(child);
}
return(node);
}
public class MyConcept
{
public String toString()
{
return pt;
}
String pt;
MyConcept()
{
pt = "";
}
public void setText(String text) {
this.pt = text;
}
}
}
-------------------------------------------------------------------
DragSourceJTree.java
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
public class DragSourceJTree extends JTree implements
DragGestureListener,
DragSourceListener {
public DragSourceJTree() {
this(new DefaultTreeModel(new DefaultMutableTreeNode()));
}
/**
* @param model
*/
public DragSourceJTree(DefaultTreeModel model) {
super(model);
DragSource dragSource = DragSource.getDefaultDragSource();
// creating the recognizer is all that's necessary - it
// does not need to be manipulated after creation
dragSource.createDefaultDragGestureRecognizer(this, // component
where drag originates
DnDConstants.ACTION_COPY, // actions
this); // drag gesture listener
}
public void dragGestureRecognized(DragGestureEvent e) {
System.out.println("drag started");
TreePath path =
this.getPathForLocation(e.getDragOrigin().x,e.getDragOrigin().y);
DefaultMutableTreeNode node =
(DefaultMutableTreeNode)path.getLastPathComponent();
if(node.getUserObject()==null)
System.out.println("userobj null at source");
else
System.out.println("dragging " + node.getUserObject().toString());
// e.startDrag(DragSource.DefaultCopyDrop, // cursor
// new TransferableNode(node.getUserObject()), // transferable
// this); // drag source listener
e.startDrag(DragSource.DefaultCopyDrop, // cursor
new TransferableNode(node), // transferable
this); // drag source listener
}
public void dragDropEnd(DragSourceDropEvent e) {}
public void dragEnter(DragSourceDragEvent e) {}
public void dragExit(DragSourceEvent e) {}
public void dragOver(DragSourceDragEvent e) {}
public void dropActionChanged(DragSourceDragEvent e) {}
}
------------------------------------------------------------------
DropTargetJTree.java
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
public class DropTargetJTree extends JTree implements
DropTargetListener {
public DropTargetJTree() {
this(new DefaultTreeModel(new DefaultMutableTreeNode()));
}
/**
* @param model
*/
public DropTargetJTree(DefaultTreeModel model) {
super(model);
new DropTarget(this, this);
}
public void dragEnter(DropTargetDragEvent arg0) {
System.out.println("drag enter");
}
public void dragOver(DropTargetDragEvent arg0) {}
public void dropActionChanged(DropTargetDragEvent arg0) {}
public void drop(DropTargetDropEvent e) {
try {
System.out.println("dropped");
Transferable tr = e.getTransferable();
//flavor not supported, reject drop
if (!tr.isDataFlavorSupported(
TransferableNode.TREENODE_FLAVOR)) e.rejectDrop();
//cast into appropriate data type
DefaultMutableTreeNode node =
(DefaultMutableTreeNode) tr.getTransferData(
TransferableNode.TREENODE_FLAVOR );
//System.out.println(node.getChildCount());
//System.out.println(node.getUserObject().toString());
//DefaultMutableTreeNode newnode = new
DefaultMutableTreeNode();
//newnode.setUserObject(tr.getTransferData(
TransferableNode.TREENODE_FLAVOR));
DefaultMutableTreeNode newnode = new
DefaultMutableTreeNode(node);
DefaultMutableTreeNode root =
(DefaultMutableTreeNode)this.getModel().getRoot();
((DefaultTreeModel)this.getModel()).insertNodeInto(newnode,root,root.getChildCount());
root.add(newnode);
try {
e.acceptDrop (DnDConstants.ACTION_COPY);
}
catch (java.lang.IllegalStateException ils) {
e.rejectDrop();
}
e.getDropTargetContext().dropComplete(true);
}
catch (IOException io) { e.rejectDrop(); }
catch (UnsupportedFlavorException ufe) {e.rejectDrop();}
} //end of method
public void dragExit(DropTargetEvent arg0) {
System.out.println("drag exit");
}
}
========================================================================
Andrew Thompson - 29 Sep 2004 19:46 GMT
> Here is a sample program that demos the problem.
No it doesn't. Your example is broke, it does not compile.
Even after I split it into three files and fixed the line wrap,
the compiler complains of a missing 'Transferable' class.
For tips on preparing code that others might help you with..
<http://www.physci.org/codes/sscce.jsp>
Try again, get the total lines under 200, and I will
have another look.

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.lensescapes.com/ Images that escape the mundane
Girish Chavan - 29 Sep 2004 19:54 GMT
Hi Andrew,
thanks a lot for taking a look. Yeah I missed one class there. Sorry
about that.
Anyways, I just solved the problem. It turns out, my initial hunch was
right, the objects that are being dragged n dropped need to be
Serializable. I just added a Serializable interface to my userObject
and it worked .perfectly.
Here is the missing class, Andrew was talking about in case anybody
wants to try out the example
--------------
TransferableNode.java
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import javax.swing.tree.DefaultMutableTreeNode;
public class TransferableNode implements Transferable {
Object node;
public TransferableNode(Object node) {
super();
this.node = node;
}
final public static DataFlavor TREENODE_FLAVOR =
new DataFlavor(DefaultMutableTreeNode.class, "Default Mutable Tree
Node");
static DataFlavor flavors[] = {TREENODE_FLAVOR };
public DataFlavor[] getTransferDataFlavors() {
return flavors;
}
public boolean isDataFlavorSupported(DataFlavor df) {
return(df.equals(TREENODE_FLAVOR));
}
public Object getTransferData(DataFlavor df)
throws UnsupportedFlavorException, IOException {
if(df.equals(TREENODE_FLAVOR))
{
//System.out.println("userobj while sending to droptarget:" +
((DefaultMutableTreeNode)node).getUserObject().toString());
return node;
}
else
throw new UnsupportedFlavorException(df);
}
}
-----------------------------------------------------------------------------------
Andrew Thompson - 29 Sep 2004 20:13 GMT
> thanks a lot for taking a look.
You're welcome.
>..Yeah I missed one class there. Sorry about that.
The link I gave you has some good tips though, I recommend
you read it. Not only does it help you to check that the
example is 'ready to go', but gives tips on how to make it so.
For example, there would be no need to separate the Java
source into separate files if the classes were all declared
'default'.
> Anyways, I just solved the problem.
That's the main thing. :-)

Signature
Andrew Thompson
http://www.PhySci.org/codes/ Web & IT Help
http://www.PhySci.org/ Open-source software suite
http://www.1point1C.org/ Science & Technology
http://www.lensescapes.com/ Images that escape the mundane