Change name of node in JTree

user489041 picture user489041 · Feb 25, 2011 · Viewed 14.7k times · Source

I am trying to change the name of a node in my JTree. I use the following code to do so:


    /**
     * Change the name of the currently selected node
     * @param newName Name to change the node too
     */
    public void changeNodeName(String newName) {
        //get the path to the selected nod
        TreePath selectedPath = mainWindow.getStructureTree().getSelectionPath() ;
        //make sure there is no other node with this name
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedPath.getLastPathComponent();
        //change its name
        node.setUserObject(newName);
}

This code works ok. So say I want to rename node b in the picture below to c. The code does it correctly as the pictures illustrate.

enter image description here enter image description here

However, if I then drag the node and place it somewhere else in the tree, its name returns to the original name of b.

enter image description here enter image description here

So obviously I am not changing something correctly here. How do I or what do I change so the nodes value stays changed?

Thanks

EDIT:

I have a class which extends DefaultMutableTreeNode. Here is the source


package Structure;

import GUI.Window;
import Logging.LogRunner;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

/**
 * This class provides the basic functionality that all subclass of the structre
 * will need such as a pop up menu, and adding new nodes.
 * @author dvargo
 */
public abstract class BCStructure extends DefaultMutableTreeNode
{
    /**
     * The root node to which this class belongs
     */
    DefaultMutableTreeNode root;
    /**
     * Reference to the main window
     */
    Window mainWindow;
    /**
     * Name of this node
     */
    String name;
    /**
     * The pop up menu
     */
    JPopupMenu Pmenu;
    /**
     * The pop up menu intems
     */
    JMenuItem deleteMenuItem,renameMenuItem,newSectionMenuItem,newPageMenuItem;

    /**
     * What type of node this is
     */
    String type;

    /**
     * Basic constructor that adds a pop up menu, sets the name, and initalizes values
     * @param newName - Name for this node
     * @param inWindow - Reference to the main window.
     */
    public BCStructure(String newName,Window inWindow)
    {
        this(newName,inWindow,true);      
    }

    /**
     * Returns the type of node this is
     * @return Page if the node is a page, Module if the node is a module, Section
     * if the node is a section
     */
    public String getType()
    {
        return type;
    }


    /**
     * Returns a copy of this node
     * @return
     */
    public abstract BCStructure copy();

    /**
     * If this is a page, this constructor should be called, it will not allof a page to
     *have any children
     * @param newName - Name for the page
     * @param inWindow - Refernce to the main window
     * @param letChildren - False to disallow this node from having children
     */
    public BCStructure(String newName,Window inWindow,boolean letChildren)
    {
        super(newName,letChildren);
        mainWindow = inWindow;
        name = newName;
        //add the popup menu
        addPopUp();
    }



    /**
     * Updates a specific node
     * @param parentNode The parent node to update
     */
    public void update(DefaultMutableTreeNode parentNode)
    {
        ((DefaultTreeModel)mainWindow.getStructureTree().getModel()).reload(parentNode);
        mainWindow.getStructureTree().repaint();

    }

    /**
     * Returns the node that is currently selected (by being clicked on) in the tree
     * @return Node that is selected in the tree
     */
    public DefaultMutableTreeNode getSelectedNode()
    {
        return (DefaultMutableTreeNode)mainWindow.getStructureTree().getLastSelectedPathComponent();
    }

    /**
     * Returns the TreePath to this node
     * @return The TreePath to this node
     */
    public TreePath getTreePath()
    {
        return new TreePath(this.getPath());
    }

    /**
     * Sets the selected node in the tree
     * @param node The node to set selected in the tree
     */
    public void setSelectedNode(BCStructure node)
    {
        mainWindow.getStructureTree().setSelectionPath(new TreePath(node.getPath()));
        update(node);
    }

    /**
     * Change the name of the currently selected node
     * @param newName Name to change the node too
     */
    public void changeNodeName(String newName) {
        //get the path to the selected nod
        TreePath selectedPath = mainWindow.getStructureTree().getSelectionPath() ;
        //make sure there is no other node with this name
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedPath.getLastPathComponent();
        DefaultMutableTreeNode nodeParent = (DefaultMutableTreeNode) node.getParent();
        if(nodeParent != null)
        {
            for(int i = 0; i lt nodeParent.getChildCount(); i++)
            {
                DefaultMutableTreeNode currNode = (DefaultMutableTreeNode) nodeParent.getChildAt(i);
                if(currNode.getUserObject().equals(newName))
                {
                    JOptionPane.showMessageDialog(mainWindow,"Another page or section already has this name in this level. Please select another.");
                    return;
                }
            }
        }
        //change its name
        node.setUserObject(newName);
        //mainWindow.getStructureTree().getModel().valueForPathChanged(selectedPath, newName);



        update(getSelectedNode());
    }

    /**
     * Adds a new section node to the tree
     * @param newName Name for this node
     */
    public void addNewSectionNode(String newName) {


        DefaultMutableTreeNode temp = getSelectedNode();
        Section newNode = null;
        if(temp == null)
        {
            LogRunner.dialogMessage(this.getClass(),"Please select a node to add this section to.");
        }

        else
        {
            newNode = new Section(newName,mainWindow);
            try
            {
                temp.add(newNode);
            }
            catch(java.lang.IllegalStateException e)
            {
                LogRunner.getLogger().warning("You can not add a section to a page");
                temp = (DefaultMutableTreeNode) temp.getParent();
                temp.add(newNode);
            }
        }
        //set the selected node to the previously selected node

        update(temp);

        if(newNode != null)
        {
            mainWindow.getStructureTree().setSelectionPath(new TreePath(newNode.getPath()));

        }
    }

    /**
     * Adds a new page to this tree
     * @param newName Name for the node
     * @return The newly created page
     */
    public Page addNewPageNode(String newName)
    {

        TreePath oldPath = mainWindow.getStructureTree().getSelectionPath();
        //Section newSection = new Section(newSectionName);
        DefaultMutableTreeNode temp = getSelectedNode();
        Page newPage = null;
        if(temp == null)
        {
            LogRunner.dialogMessage(this.getClass(),"Please select a module or section to add this section to.");
        }

        else
        {
            newPage = new Page(newName,mainWindow);
            try
            {
                temp.add(newPage);

            }
            catch(java.lang.IllegalStateException e)
            {
                LogRunner.getLogger().warning("You can not add any more nodes to a page.");
                temp = (DefaultMutableTreeNode) temp.getParent();
                temp.add(newPage);

            }
        }
        update(temp);
        mainWindow.getStructureTree().setSelectionPath(oldPath);
        return newPage;
    }


    /**
     * Propmpts the user to entere a new name for a node that is selected
     */
    private void rename()
    {
        String newname = JOptionPane.showInputDialog("New name?");
        changeNodeName(newname);
    }
    /**
     * Deletes the selected node from the tree
     */
    private void delete()
    {
        DefaultMutableTreeNode node = (DefaultMutableTreeNode)mainWindow.getStructureTree().getLastSelectedPathComponent();
        if(node == null) return;

        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)(node.getParent());
        if(parentNode == null) return;

        //remove node
        parentNode.remove(node);
        ((DefaultTreeModel)mainWindow.getStructureTree().getModel()).reload(parentNode);
    }

    /**
     * Deletes a specific node from the tree
     * @param node The node to delete
     */
    protected void delete(DefaultMutableTreeNode node)
    {
         if(node == null) return;

        DefaultMutableTreeNode parentNode = (DefaultMutableTreeNode)(node.getParent());
        if(parentNode == null) return;

        //remove node
        parentNode.remove(node);
        ((DefaultTreeModel)mainWindow.getStructureTree().getModel()).reload(parentNode);
    }


    /**
     * Adds the popup menu functionality to the tree
     */
    private void addPopUp()
    {

        Pmenu = new JPopupMenu();
        newSectionMenuItem = new JMenuItem("Add New Section");
        Pmenu.add(newSectionMenuItem);
        newPageMenuItem = new JMenuItem("Add New Page");
        Pmenu.add(newPageMenuItem);
        Pmenu.add(new JSeparator());
        deleteMenuItem = new JMenuItem("Delete");
        Pmenu.add(deleteMenuItem);
        renameMenuItem = new JMenuItem("Rename");
        Pmenu.add(renameMenuItem);


        //add actionlisteners to the menu items
        deleteMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                delete();}
            }
        );

        renameMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                rename();}
            }
        );

        newSectionMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                mainWindow.createNewSectionPublicCall();}
            }
        );

        newPageMenuItem.addActionListener(new ActionListener()
        {
            public void actionPerformed(ActionEvent e)
            {
                mainWindow.createNewPagePublicCall();}
            }
        );

        //add action listener to the tree
        mainWindow.getStructureTree().addMouseListener(new MouseAdapter()
        {
            public void mouseReleased(MouseEvent Me)
            {
                if(Me.isPopupTrigger())
                {
                    Pmenu.show(Me.getComponent(), Me.getX(), Me.getY());
                }
            }
        });

        if(getClass().equals(Page.class))
        {
            newSectionMenuItem.setEnabled(false);
        }
    }


    /**
     * Returns all the nodes in this tree from doing a left heavy recursive
     * traversal of the tree from the given root
     * @param root The root from which to start the search
     * @return A list of the nodes
     */
    public ArrayList getAllNodesInOrder(BCStructure root)
    {
        ArrayList nodes = new ArrayList();
        getAllNodesInOrderRec(root, nodes);
        return nodes;
    }

    /**
     * Recursive function that gets the nodes in the tree
     * @param currNode
     * @param theNodes
     */
    private void getAllNodesInOrderRec(BCStructure currNode, ArrayList theNodes)
    {
        theNodes.add(currNode);
        for(int i = 0; i lt currNode.getChildCount(); i++)
        {
            currNode.getAllNodesInOrderRec((BCStructure) currNode.getChildAt(i), theNodes);
        }
    }


}


And in the example above, the actual nodes you are seeing are a subclass of BCStructure called Page. This is the actual class that I am renaming.


package Structure;

import Components.BCFrame;
import Components.Basic.BackGroundImage;
import GUI.Window;
import Logging.LogRunner;
import XMLProcessing.XMLWriter;
import java.awt.Color;
import java.awt.Dimension;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.tree.DefaultTreeCellRenderer;

/**
 * This class is responcible for holding the components the make up a page and
 * is accessible through the tree structure. In other words, this class is what
 * actually makes up a page. It holds the componenets in an array, and since it
 * node in a tree, it can be notified when it has been clicked, and load the
 * compoenents it is holding.
 * @author dvargo
 */
public class Page extends BCStructure
{
    /**
     * Holds all the componenets in the content pane so an action can be done on
     * all componenets. Also sets the added component to the current component.
     */
    private ArrayList theComponents = new ArrayList()
    {
        @Override
        public boolean add(BCFrame e)
        {
            e.setPage(selfReference);
            return super.add(e);
        }
    };


    /**
     * Self reference to this page
     */
    private Page selfReference = this;

    /**
     * The dimensions of this page. It defualts to a medium page size
     */
    private Dimension pageSize = Window.NORMAL;

    /**
     * This bages background;
     */
    private BackGroundImage background;

    /**
     * Constructor that sets the node up in the tree and inializes values.
     * @param newName - Name for this node
     * @param inWindow - Reference to the main window
     * @param inRoot - The section or module that is the root for this page.
     */
    public Page(String newName, Window inWindow)
    {
        super(newName, inWindow,false);
        DefaultTreeCellRenderer renderer = new DefaultTreeCellRenderer();
        ImageIcon theImage = new ImageIcon(new JFrame().getToolkit().getImage(getClass().getResource("/GUI/fileIcon.png")));
        renderer.setLeafIcon(theImage);
        //set the background color to white, there will always be a background
        background = new BackGroundImage(0,0,pageSize.width,pageSize.height,mainWindow);
        background.setColor(Color.WHITE);
        theComponents.add(background);
        //you must add this to the content pane to keep indexes correct. it will not display anything though
        mainWindow.getComponentContentPane().add(background,0);
        mainWindow.getContentPanePanel().repaint();
    }


    /**
     * Loads all the componenets held in the arraylist to to the screen.
     */
    public void loadPage()
    {
        //remove the background of the previous page
        mainWindow.getComponentContentPane().removeAll();
        mainWindow.setPageSizeComboSeleted(pageSize);
        background.setSize(pageSize);
        mainWindow.getComponentContentPane().setPreferredSize(pageSize);
        mainWindow.getComponentContentPane().setSize(pageSize);
        for(BCFrame currentComp : theComponents)
        {
            mainWindow.getComponentContentPane().add(currentComp);
            currentComp.setVisible(true);
            currentComp.revalidate();
            currentComp.repaint();
            currentComp.setPage(this);
       }

        mainWindow.getComponentContentPane().repaint();
        mainWindow.getComponentContentPane().revalidate();

    }

    /**
     * Writes the componenets to file in XML.
     * @param filePath - The path and name of the file to write.
     */
    public void save(String filePath)
    {
        XMLWriter theWriter = new XMLWriter();
        for(int i = 0; i  newComponents)
    {
        theComponents = newComponents;
        boolean backgroundExists = false;
        for(BCFrame curr : theComponents)
        {
            if(curr.getClass().equals(BackGroundImage.class))
            {
                background = (BackGroundImage) curr; //make sure background isnt null
                backgroundExists = true;
            }
            curr.setPage(this);
        }
        if(backgroundExists)
        {
            return;
        }
        LogRunner.getLogger().severe("Could not find a background while setting the components, adding a new dfualt white one");
        BackGroundImage bgi= new BackGroundImage();
        bgi.setSize(pageSize);
        bgi.setColor(Color.WHITE);
        theComponents.add(bgi);
        background = bgi;
    }

    public ArrayList getComponents()
    {
        return theComponents;
    }



}



Answer

user489041 picture user489041 · Feb 25, 2011

I figured it out. If you notice in BCStructure, it has a value called name. Whenever I change the name of the node I did not update this value. Then if you notice in the copy() of the Page class, it uses this name variable. Copy gets used in the drag and drop process. If name was not updated, it would be using the old value which is why I saw the behavior I did. Kind of simple to see but hard to explain. Thanks all for your help.