Welcome to the Java Programming Forums


The professional, friendly Java community. 21,500 members and growing!


The Java Programming Forums are a community of Java programmers from all around the World. Our members have a wide range of skills and they all have one thing in common: A passion to learn and code Java. We invite beginner Java programmers right through to Java professionals to post here and share your knowledge. Become a part of the community, help others, expand your knowledge of Java and enjoy talking with like minded people. Registration is quick and best of all free. We look forward to meeting you.


>> REGISTER NOW TO START POSTING


Members have full access to the forums. Advertisements are removed for registered users.

Results 1 to 4 of 4

Thread: JTree DnD Problem

  1. #1
    Junior Member
    Join Date
    Feb 2011
    Posts
    13
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default JTree DnD Problem

    I've implemented drag and drop functionality in my JTree so I can reorder the nodes. I also have two methods for adding nodes; one is with a button, and another is a context menu that is triggered by the node. Button method works fine, but once I add a node with the context menu, drag and drop starts malfunctioning (nodes start dissapearing, arrayindexoutofbounds exceptions..). I don't understand why its doing this because both codes are pretty much the same.

    DnDProblem.java (main)
    package dndproblem;
     
    import java.awt.BorderLayout;
    import java.awt.EventQueue;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
     
    public class DnDProblem extends JFrame{
     
    	private DnDJTree tree;
    	private JButton addButton;
     
    	public static void main(String[] args) {
    		EventQueue.invokeLater(new Runnable() {
    			@Override
    			public void run() {
    				DnDProblem dndprb = new DnDProblem();
    			}
    		});
    	}
     
    	public DnDProblem() {
    		setSize(300, 400);
    		setLocationRelativeTo(null);
    		setDefaultCloseOperation(EXIT_ON_CLOSE);
    		setLayout(new BorderLayout());
     
    		tree = new DnDJTree();
    		add(tree, BorderLayout.CENTER);
     
    		addButton = new JButton("Add");
    		addButton.addActionListener(new ActionListener() {
    			@Override
    			public void actionPerformed(ActionEvent e) {
    				((DefaultTreeModel)tree.getModel()).insertNodeInto(new DefaultMutableTreeNode(new NodeData("New Node")),
    																   (DefaultMutableTreeNode)tree.getModel().getRoot(),
    																   0);
    			}
    		});
    		add(addButton, BorderLayout.SOUTH);
     
     
    		setVisible(true);
    	}
    }

    DnDJTree.java
    package dndproblem;
     
    import javax.swing.DropMode;
    import javax.swing.JTree;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
     
    public class DnDJTree extends JTree{
    	public DnDJTree() {
    		setDragEnabled(true);
    		setDropMode(DropMode.ON_OR_INSERT);
    		setTransferHandler(new DnDTransferHandler());
     
    		DefaultMutableTreeNode root = new DefaultMutableTreeNode("Root");
    		root.add(new DefaultMutableTreeNode(new NodeData("Child 1")));
    		root.add(new DefaultMutableTreeNode(new NodeData("Child 2")));
    		root.add(new DefaultMutableTreeNode(new NodeData("Child 3")));
    		root.add(new DefaultMutableTreeNode(new NodeData("Child 4")));
     
    		setModel(new DefaultTreeModel(root));
    		addMouseListener(new MouseMenuAdapter());
     
    		Common.tree = this;
    	}
    }

    DnDTransferHandler.java
    package dndproblem;
     
    import java.awt.datatransfer.DataFlavor;
    import java.awt.datatransfer.Transferable;
    import java.awt.datatransfer.UnsupportedFlavorException;
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import javax.swing.JComponent;
    import javax.swing.JTree;
    import javax.swing.TransferHandler;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
     
     
    public class DnDTransferHandler extends TransferHandler{
     
    	public static final DataFlavor NODE_FLAVOR = new DataFlavor(PackedNode.class, "Node Flavor");
     
    	public DnDTransferHandler() {
    		super();
    	}
     
    	@Override
    	public int getSourceActions(JComponent c) {
    		return MOVE;
    	}
     
    	@Override
    	public boolean canImport(TransferSupport support) {
    		try {
    			//get the nodes involved in the move
    			DefaultTreeModel model = (DefaultTreeModel) ((JTree)support.getComponent()).getModel();
    			JTree.DropLocation dloc = (JTree.DropLocation) support.getDropLocation();
    			DefaultMutableTreeNode child = (DefaultMutableTreeNode) support.getTransferable().getTransferData(NODE_FLAVOR);
    			DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dloc.getPath().getLastPathComponent();
     
    			//I dont want to be able to drag the root node, and the root has no parent, so...
    			if(child.getParent() == null) return false;
    			//I dont want to be able to drag a node inside itself ...
    			if(parent.isNodeAncestor(child)) return false;
    		} catch (IOException e) {
     
    		} catch (UnsupportedFlavorException e) {
     
    		}
     
    		return true;
    	}
     
    	@Override 
    	public Transferable createTransferable(JComponent c) {
    		DefaultMutableTreeNode node = (DefaultMutableTreeNode)((JTree)c).getLastSelectedPathComponent();
    		return new PackedNode(node);
    	}
     
    	@Override
    	public boolean importData(TransferSupport support) {
    		if(canImport(support)) {
    			try {
    				//get the nodes involved in the move
    				DefaultTreeModel model = (DefaultTreeModel) ((JTree)support.getComponent()).getModel();
    				JTree.DropLocation dloc = (JTree.DropLocation) support.getDropLocation();
    				DefaultMutableTreeNode child = (DefaultMutableTreeNode) support.getTransferable().getTransferData(NODE_FLAVOR);
    				DefaultMutableTreeNode parent = (DefaultMutableTreeNode) dloc.getPath().getLastPathComponent();
     
    				//drop index
    				int destIndex = dloc.getChildIndex();
    				if(destIndex == -1) destIndex = 0;
     
    				//get a deep copy of the node being moved. Then I can delete the node referenced in
    				//the Transferable safely
    				ByteArrayOutputStream bos = new ByteArrayOutputStream();
    				ObjectOutputStream out = new ObjectOutputStream(bos);
    				out.writeObject(child);
    				out.flush();
    				out.close();
    				ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
    				Object obj = in.readObject();
    				in.close();
     
    				//insert node
    				model.insertNodeInto((DefaultMutableTreeNode)obj, parent, destIndex);
    			} catch(IOException e) {
    				return false;
    			} catch(ClassNotFoundException e) {
    				return false;
    			} catch(UnsupportedFlavorException e) {
    				return false;
    			}
     
    			return true;
    		} else {
    			return false;
    		}
    	}
     
    	@Override
    	public void exportDone(JComponent c, Transferable t, int action) {
    		if(action == MOVE) {
    			try {
    				DefaultTreeModel model = (DefaultTreeModel)((JTree)c).getModel();
    				DefaultMutableTreeNode child = (DefaultMutableTreeNode) t.getTransferData(NODE_FLAVOR);
    				model.removeNodeFromParent(child);
    			} catch(UnsupportedFlavorException e) {
     
    			} catch(IOException e) {
     
    			}
    		}
    	}
    }

    PackedNode.java
    package dndproblem;
     
    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 PackedNode implements Transferable{
     
    	private DefaultMutableTreeNode data;
     
    	public PackedNode(DefaultMutableTreeNode node) {
    		data = node;
    	}
     
    	@Override
    	public DataFlavor[] getTransferDataFlavors() {
    		return new DataFlavor[] {DnDTransferHandler.NODE_FLAVOR};
    	}
     
    	@Override
    	public boolean isDataFlavorSupported(DataFlavor flavor) {
    		if(flavor == DnDTransferHandler.NODE_FLAVOR) {
    			return true;
    		} else {
    			return false;
    		}
    	}
     
    	@Override
    	public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
    		if(flavor == DnDTransferHandler.NODE_FLAVOR) {
    			return data;
    		} else {
    			throw new UnsupportedFlavorException(flavor);
    		}
    	}
     
    }

    NodeData.java
    package dndproblem;
     
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    import java.io.Serializable;
    import javax.swing.JMenuItem;
    import javax.swing.JOptionPane;
    import javax.swing.JPopupMenu;
    import javax.swing.JTextField;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.DefaultTreeModel;
     
    public class NodeData implements   ActionListener,
    								   Serializable{
    	private String name;
     
    	private JPopupMenu context;
    	private JMenuItem newNode;
     
    	public NodeData(String n) {
    		name = n;
     
    		//setup context menu for this data type
    		context = new JPopupMenu();
    		newNode = new JMenuItem("New folder");
    		newNode.addActionListener(this);
    		context.add(newNode);
    	}
     
    	public void showMenu(int x, int y) {
    		context.show(Common.tree, x, y);
    	}
     
    	@Override
    	public String toString() {
    		return name;
    	}
     
    	@Override
    	public void actionPerformed(ActionEvent e) {
    		if(e.getSource() == newNode) {
    			JTextField tf = new JTextField();
    			String n = JOptionPane.showInputDialog(tf, "Folder name:", "New Folder", JOptionPane.PLAIN_MESSAGE);
     
    			((DefaultTreeModel)Common.tree.getModel()).insertNodeInto(new DefaultMutableTreeNode(new NodeData(n)),
    																   (DefaultMutableTreeNode)Common.tree.getModel().getRoot(),
    																   0);
    		}
    	}
    }

    MouseMenuAdapter.java
    package dndproblem;
     
    import java.awt.event.MouseAdapter;
    import java.awt.event.MouseEvent;
    import javax.swing.JTree;
    import javax.swing.tree.DefaultMutableTreeNode;
    import javax.swing.tree.TreePath;
     
    public class MouseMenuAdapter extends MouseAdapter{
    	@Override
    	public void mousePressed(MouseEvent event) {
    		if(event.getButton() == 3) {
    			int x = event.getX();																	//get mouse cooridnates
    			int y = event.getY();
    			JTree tree = (JTree) event.getSource();													//get the tree that was clicked
    			TreePath path = tree.getPathForLocation(x, y);											//get a path to the clicked node
     
    			if(path == null) return;																//if we missed, return
     
    			tree.setSelectionPath(path);															//set the selected node to the one we clicked
    			DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();		//get the node we clicked
    			Object data = node.getUserObject();														//get the data object from node
     
    			if(data instanceof NodeData) ((NodeData)data).showMenu(x, y);							//show the context menu
    		}
    	}
    }

    Common.java
    package dndproblem;
     
    import javax.swing.JTree;
     
    public class Common {
    	public static JTree tree;
    }


  2. #2
    Junior Member
    Join Date
    Feb 2011
    Posts
    13
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: JTree DnD Problem

    Ok, an update to this problem. The problem occurs right after opening a popup menu. If I so much as open then close a popup menu, DnD starts malfunctioning....

  3. #3
    Administrator copeg's Avatar
    Join Date
    Oct 2009
    Location
    US
    Posts
    5,318
    Thanks
    181
    Thanked 833 Times in 772 Posts
    Blog Entries
    5

    Default Re: JTree DnD Problem

    Don't have time to test your code at the moment (tis a lot of code to test), but your last post suggests there may be a focus problem...try calling requestFocus on the component you want to have focus after the popup menu is closed.

  4. #4
    Junior Member
    Join Date
    Feb 2011
    Posts
    13
    Thanks
    0
    Thanked 0 Times in 0 Posts

    Default Re: JTree DnD Problem

    I've found a solution that seems to have solved it. It is caused by the JPopupmenu...existing? If I set the menu to null when it is cancelled or will become invisible, then everything works fine. I just re-instantiate it before showing it again.

    Sounds like a bug, I dunno. But at least its working now.

Similar Threads

  1. JTree Selection Order Icons
    By pradeeprkm in forum AWT / Java Swing
    Replies: 1
    Last Post: August 1st, 2011, 07:58 AM
  2. JTree not expanding?
    By captain alge in forum AWT / Java Swing
    Replies: 1
    Last Post: April 15th, 2011, 03:32 AM
  3. JTree - Remove All Nodes
    By aussiemcgr in forum Java Theory & Questions
    Replies: 4
    Last Post: December 9th, 2010, 05:27 PM
  4. [SOLVED] Jtree help
    By sman36 in forum AWT / Java Swing
    Replies: 1
    Last Post: December 6th, 2010, 09:39 AM
  5. application Task problem - updating JTree
    By idandush in forum AWT / Java Swing
    Replies: 2
    Last Post: June 18th, 2009, 03:15 AM