Home | Contact Us | FAQ | Search & Site Map | Link to Us
Sign In | Join | Other 45 Sites in Network
HomeAnnouncementsWhite Papers
Discussion GroupsFirst AidDatabasesJavaBeansGUIJava 3DVirtual MachineCORBASecurityToolsGeneral
Java DirectoryOpen Source ProjectsSample Book ChaptersUser GroupsWeb Resources
Related Topics
Databases.NETMore Topics ...

Java Forum / GUI / July 2007

Tip: Looking for answers? Try searching our database.

JTree not updating after deleting a node in DnD

Thread view: 
Qu0ll - 22 Jul 2007 14:22 GMT
My first attempt at implementing drag and drop in Swing has been painful to
say the least.  I am trying to setup DnD in a JTree and the first thing I am
attempting is to simply delete the node that is being moved from its parent.
Sounds simple?  Well I can't get it to do even that.

The SSCCE attached below shows what I am talking about.  I have a method
removeChildNode() which deletes the child node from the parent and notifies
the tree and I know it works because if you run the program the first time
and click on the button then node C is deleted and the tree updates.
However, when I call the same method on the same nodes in the transfer
handler nothing appears to happen so if you run the program again and drag C
on to B for example nothing happens.  C should be deleted.

From debug it appears that the model is updating correctly but the tree
itself is not.  Can anyone suggest why this might be?  Lines 95-98 in the
importData() method seem to be the key but you'll see that the debug there
prints out the correct node data.

Any help much appreciated.

Signature

And loving it,

-Q
_________________________________________________
Qu0llSixFour@gmail.com
(Replace the "SixFour" with numbers to email me)

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

public class TreeDnDTestFrame extends JFrame {

public static final DataFlavor MY_FLAVOR = new DataFlavor(String.class,
"MyFlavor");

class MyTransferable implements Transferable {
 private final DataFlavor[] flavors = {MY_FLAVOR};
 private DefaultMutableTreeNode data;

 public MyTransferable(final DefaultMutableTreeNode data) {
  this.data = data;
 }

 @Override
 public Object getTransferData(DataFlavor flavor)
  throws UnsupportedFlavorException, IOException {
  if (flavor.equals(flavors[0])) {
   return (Object)data;
  } else {
   throw new UnsupportedFlavorException(flavor);
  }
 }

 @Override
 public DataFlavor[] getTransferDataFlavors() {
  return (DataFlavor[])flavors.clone();
 }

 @Override
 public boolean isDataFlavorSupported(DataFlavor flavor) {
  for (DataFlavor df : flavors) {
   if (df.equals(flavor)) {
    return true;
   }
  }
  return false;
 }
}

class MyTransferHandler extends TransferHandler {
 @Override
 protected Transferable createTransferable(JComponent c) {
  JTree tree = (JTree)c;
  Object o = tree.getSelectionPath().getLastPathComponent();
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)o;
  return new MyTransferable(node);
 }

 @Override
 public boolean importData(TransferSupport support) {
  Point dropPoint = support.getDropLocation().getDropPoint();
  JTree tree = (JTree)support.getComponent();
  DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
  TreePath path = tree.getPathForLocation(dropPoint.x, dropPoint.y);
  if (path == null) {
   return false;
  } else {
   DefaultMutableTreeNode target =
(DefaultMutableTreeNode)path.getLastPathComponent();
   DefaultMutableTreeNode data = null;
   try {
    data =
     (DefaultMutableTreeNode)support.getTransferable().getTransferData(MY_FLAVOR);
   }
   catch (UnsupportedFlavorException ufe) {
    return false;
   }
   catch (IOException ioe) {
    return false;
   }
   DefaultMutableTreeNode parent =
(DefaultMutableTreeNode)data.getParent();
   System.out.println("data = " + data + ", target = " + target + ", parent
= " + parent);
   if (parent != null) {
    removeChildNode(parent, data);
   }
  }
  return true;
 }

 @Override
 public boolean canImport(TransferSupport support) {
  return true;
 }

 @Override
 public int getSourceActions(JComponent c) {
  return MOVE;
 }
}

private DefaultMutableTreeNode root, a, b, c;
private JTree tree;

public TreeDnDTestFrame() {
 setupModel();
 defineGUI();
}

public void setupModel() {
 root = new DefaultMutableTreeNode("ROOT");
 a = new DefaultMutableTreeNode("A");
 root.add(a);
 b = new DefaultMutableTreeNode("B");
 root.add(b);
 c = new DefaultMutableTreeNode("C");
 a.add(c);
}

public void removeChildNode(DefaultMutableTreeNode parent,
DefaultMutableTreeNode child) {
 final int[] childIndices = {parent.getIndex(child)};
 final Object[] removedChildren = {child};
 parent.remove(child);
 DefaultTreeModel model = (DefaultTreeModel)tree.getModel();
 model.nodesWereRemoved(parent, childIndices, removedChildren);
}

public void defineGUI() {
 tree = new JTree();
 JScrollPane scrollPane = new JScrollPane();
 JPanel panel = new JPanel();
 setPreferredSize(new Dimension(800, 600));
 setMinimumSize(new Dimension(800, 600));
 setLayout(new BorderLayout());
 tree = new JTree(root);
 tree.setDragEnabled(true);
 tree.setTransferHandler(new MyTransferHandler());
 scrollPane = new JScrollPane(tree);
 scrollPane.setBorder(BorderFactory.createEmptyBorder());
 JButton b = new JButton("Delete C");
 b.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent ae) {
   removeChildNode(a, c);
  }
 });
 panel = new JPanel();
 panel.setLayout(new CardLayout(8, 8));
 panel.add(scrollPane, "Tree");
 add(panel, BorderLayout.CENTER);
 add(b, BorderLayout.SOUTH);
 pack();
 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 setLocationRelativeTo(null);
}

public static void main(String[] args) {
 EventQueue.invokeLater(new Runnable() {
  public void run() {
   new TreeDnDTestFrame().setVisible(true);
  }
 });
}
}

Qu0ll - 22 Jul 2007 14:45 GMT
> My first attempt at implementing drag and drop in Swing has been painful
> to > say the least.

I should have mentioned that I am using Java 6.0 Update 2.

Signature

And loving it,

-Q
_________________________________________________
Qu0llSixFour@gmail.com
(Replace the "SixFour" with numbers to email me)

Roedy Green - 23 Jul 2007 01:59 GMT
>However, when I call the same method on the same nodes in the transfer
>handler nothing appears to happen so if you run the program again and drag C
>on to B for example nothing happens.  C should be deleted.

did your remember to call
 public void fireTreeStructureChanged( Object source, TreePath path )
or
protected void fireTreeNodesChanged(
                                     Object source,
                                     Object[] path,
                                     int[] childIndices,
                                     Object[] children )

Signature

Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com

Qu0ll - 23 Jul 2007 07:32 GMT
>>However, when I call the same method on the same nodes in the transfer
>>handler nothing appears to happen so if you run the program again and drag
[quoted text clipped - 9 lines]
>                                      int[] childIndices,
>                                      Object[] children )

1. What's the difference between nodesRemoved() which I already call and
fireTreeNodesRemoved()?
2. Why do there seem to be groups of 2 methods to do the same thing such as
those in (1) and those of the form nodesXXXX() and fireNodesXXXX()?
3. When I add either of these 2 lines to the code, A is deleted as well as C
which is still not correct:

fireTreeNodesRemoved(parent, new TreePath(root).getPath(), childIndices,
removedChildren);
fireTreeStructureChanged(parent, new TreePath(root).getPath(), childIndices,
removedChildren);

4. I have tried many combinations of the methods in (2) and none of them
gives the correct result.  How do I get this to work inside the context of a
transfer handler? (Some do work from the button).

Revised code follows.

Signature

And loving it,

-Q
_________________________________________________
Qu0llSixFour@gmail.com
(Replace the "SixFour" with numbers to email me)

import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.TransferHandler;
import javax.swing.WindowConstants;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

public class TreeDnDTestFrame extends JFrame {

public static final DataFlavor MY_FLAVOR = new DataFlavor(String.class,
"MyFlavor");

class MyTransferable implements Transferable {
 private final DataFlavor[] flavors = {MY_FLAVOR};
 private DefaultMutableTreeNode data;

 public MyTransferable(final DefaultMutableTreeNode data) {
  this.data = data;
 }

 @Override
 public Object getTransferData(DataFlavor flavor)
  throws UnsupportedFlavorException, IOException {
  if (flavor.equals(flavors[0])) {
   return (Object)data;
  } else {
   throw new UnsupportedFlavorException(flavor);
  }
 }

 @Override
 public DataFlavor[] getTransferDataFlavors() {
  return (DataFlavor[])flavors.clone();
 }

 @Override
 public boolean isDataFlavorSupported(DataFlavor flavor) {
  for (DataFlavor df : flavors) {
   if (df.equals(flavor)) {
    return true;
   }
  }
  return false;
 }
}

class MyTransferHandler extends TransferHandler {
 @Override
 protected Transferable createTransferable(JComponent c) {
  JTree tree = (JTree)c;
  Object o = tree.getSelectionPath().getLastPathComponent();
  DefaultMutableTreeNode node = (DefaultMutableTreeNode)o;
  return new MyTransferable(node);
 }

 @Override
 public boolean importData(TransferSupport support) {
  Point dropPoint = support.getDropLocation().getDropPoint();
  JTree tree = (JTree)support.getComponent();
  TreePath path = tree.getPathForLocation(dropPoint.x, dropPoint.y);
  if (path == null) {
   return false;
  } else {
   DefaultMutableTreeNode target =
(DefaultMutableTreeNode)path.getLastPathComponent();
   DefaultMutableTreeNode data = null;
   try {
    data =
     (DefaultMutableTreeNode)support.getTransferable().getTransferData(MY_FLAVOR);
   }
   catch (UnsupportedFlavorException ufe) {
    return false;
   }
   catch (IOException ioe) {
    return false;
   }
   DefaultMutableTreeNode parent =
(DefaultMutableTreeNode)data.getParent();
   System.out.println("data = " + data + ", target = " + target + ", parent
= " + parent);
   if (parent != null) {
    model.removeChildNode(parent, data);
   }
  }
  return true;
 }

 @Override
 public boolean canImport(TransferSupport support) {
  return true;
 }

 @Override
 public int getSourceActions(JComponent c) {
  return MOVE;
 }
}

private DefaultMutableTreeNode root, a, b, c;
private JTree tree;
private MyTreeModel model;

public TreeDnDTestFrame() {
 setupModel();
 defineGUI();
}

public void setupModel() {
 root = new DefaultMutableTreeNode("ROOT");
 a = new DefaultMutableTreeNode("A");
 root.add(a);
 b = new DefaultMutableTreeNode("B");
 root.add(b);
 c = new DefaultMutableTreeNode("C");
 a.add(c);
}

class MyTreeModel extends DefaultTreeModel {
 public MyTreeModel(DefaultMutableTreeNode root) {
  super(root);
 }

 public void removeChildNode(DefaultMutableTreeNode parent,
DefaultMutableTreeNode child) {
  final int[] childIndices = {parent.getIndex(child)};
  final Object[] removedChildren = {child};
  parent.remove(child);
  nodesWereRemoved(parent, childIndices, removedChildren);
  fireTreeNodesRemoved(parent, new TreePath(root).getPath(), childIndices,
removedChildren);
 }
}

public void defineGUI() {
 tree = new JTree();
 JScrollPane scrollPane = new JScrollPane();
 JPanel panel = new JPanel();
 setPreferredSize(new Dimension(800, 600));
 setMinimumSize(new Dimension(800, 600));
 setLayout(new BorderLayout());
 model = new MyTreeModel(root);
 tree = new JTree(root);
 tree.setModel(model);
 tree.setDragEnabled(true);
 tree.setTransferHandler(new MyTransferHandler());
 scrollPane = new JScrollPane(tree);
 scrollPane.setBorder(BorderFactory.createEmptyBorder());
 JButton b = new JButton("Delete C");
 b.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent ae) {
   model.removeChildNode(a, c);
  }
 });
 panel = new JPanel();
 panel.setLayout(new CardLayout(8, 8));
 panel.add(scrollPane, "Tree");
 add(panel, BorderLayout.CENTER);
 add(b, BorderLayout.SOUTH);
 pack();
 setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
 setLocationRelativeTo(null);
}

public static void main(String[] args) {
 EventQueue.invokeLater(new Runnable() {
  public void run() {
   new TreeDnDTestFrame().setVisible(true);
  }
 });
}
}

Roedy Green - 24 Jul 2007 08:41 GMT
>1. What's the difference between nodesRemoved() which I already call and
>fireTreeNodesRemoved()?
The idea is you make a great string of changes to your model. You
DON'T repaint until you are done.  Then when you are done all your
tinkering, you tell the GUI what you changed with the firexxx methods
so it can do all the repainting in one quick pass.
Signature

Roedy Green Canadian Mind Products
The Java Glossary
http://mindprod.com



Free Magazines

Get these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...

Oracle MagazineNetwork ComputingComputer WorldBio-IT WorldeWeekInformation WeekInfosecurity
 
Sign In
Join
My Latest Posts
My Monitored Threads
My Blog
My Photo Gallery
My Profile
My Homepage

Start New Thread
Enable EMail Alerts
Rate this Thread



©2008 Advenet LLC   Privacy Policy - Terms of Use
This website includes both content owned or controlled by Advenet as well as content owned or controlled by third parties.