alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (SampleTree.java)

This example Java source code file (SampleTree.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

abstractaction, actionlistener, awt, changelistener, defaultmutabletreenode, end, event, gui, jcheckbox, jmenubar, jmenuitem, jpanel, jradiobutton, log, logging, object, override, sampletree, swing, treepath

The SampleTree.java Java example source code

/*
 * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *   - Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *
 *   - Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *
 *   - Neither the name of Oracle nor the names of its
 *     contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*
 * This source code is provided to illustrate the usage of a given feature
 * or technique and has been deliberately simplified. Additional steps
 * required for a production-quality application, such as security checks,
 * input validation and proper error handling, might not be present in
 * this sample code.
 */



import java.lang.reflect.InvocationTargetException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.*;
import javax.swing.UIManager.LookAndFeelInfo;
import javax.swing.border.*;
import javax.swing.tree.*;


/**
 * A demo for illustrating how to do different things with JTree.
 * The data that this displays is rather boring, that is each node will
 * have 7 children that have random names based on the fonts.  Each node
 * is then drawn with that font and in a different color.
 * While the data isn't interesting the example illustrates a number
 * of things:
 *
 * For an example of dynamicaly loading children refer to DynamicTreeNode.
 * For an example of adding/removing/inserting/reloading refer to the inner
 *     classes of this class, AddAction, RemovAction, InsertAction and
 *     ReloadAction.
 * For an example of creating your own cell renderer refer to
 *     SampleTreeCellRenderer.
 * For an example of subclassing JTreeModel for editing refer to
 *     SampleTreeModel.
 *
 * @author Scott Violet
 */
public final class SampleTree {

    /** Window for showing Tree. */
    protected JFrame frame;
    /** Tree used for the example. */
    protected JTree tree;
    /** Tree model. */
    protected DefaultTreeModel treeModel;

    /**
     * Constructs a new instance of SampleTree.
     */
    public SampleTree() {
        // Trying to set Nimbus look and feel
        try {
            for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (Exception ignored) {
        }

        JMenuBar menuBar = constructMenuBar();
        JPanel panel = new JPanel(true);

        frame = new JFrame("SampleTree");
        frame.getContentPane().add("Center", panel);
        frame.setJMenuBar(menuBar);
        frame.setBackground(Color.lightGray);

        /* Create the JTreeModel. */
        DefaultMutableTreeNode root = createNewNode("Root");
        treeModel = new SampleTreeModel(root);

        /* Create the tree. */
        tree = new JTree(treeModel);

        /* Enable tool tips for the tree, without this tool tips will not
        be picked up. */
        ToolTipManager.sharedInstance().registerComponent(tree);

        /* Make the tree use an instance of SampleTreeCellRenderer for
        drawing. */
        tree.setCellRenderer(new SampleTreeCellRenderer());

        /* Make tree ask for the height of each row. */
        tree.setRowHeight(-1);

        /* Put the Tree in a scroller. */
        JScrollPane sp = new JScrollPane();
        sp.setPreferredSize(new Dimension(300, 300));
        sp.getViewport().add(tree);

        /* And show it. */
        panel.setLayout(new BorderLayout());
        panel.add("Center", sp);
        panel.add("South", constructOptionsPanel());

        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setVisible(true);
    }

    /** Constructs a JPanel containing check boxes for the different
     * options that tree supports. */
    @SuppressWarnings("serial")
    private JPanel constructOptionsPanel() {
        JCheckBox aCheckbox;
        JPanel retPanel = new JPanel(false);
        JPanel borderPane = new JPanel(false);

        borderPane.setLayout(new BorderLayout());
        retPanel.setLayout(new FlowLayout());

        aCheckbox = new JCheckBox("show top level handles");
        aCheckbox.setSelected(tree.getShowsRootHandles());
        aCheckbox.addChangeListener(new ShowHandlesChangeListener());
        retPanel.add(aCheckbox);

        aCheckbox = new JCheckBox("show root");
        aCheckbox.setSelected(tree.isRootVisible());
        aCheckbox.addChangeListener(new ShowRootChangeListener());
        retPanel.add(aCheckbox);

        aCheckbox = new JCheckBox("editable");
        aCheckbox.setSelected(tree.isEditable());
        aCheckbox.addChangeListener(new TreeEditableChangeListener());
        aCheckbox.setToolTipText("Triple click to edit");
        retPanel.add(aCheckbox);

        borderPane.add(retPanel, BorderLayout.CENTER);

        /* Create a set of radio buttons that dictate what selection should
        be allowed in the tree. */
        ButtonGroup group = new ButtonGroup();
        JPanel buttonPane = new JPanel(false);
        JRadioButton button;

        buttonPane.setLayout(new FlowLayout());
        buttonPane.setBorder(new TitledBorder("Selection Mode"));
        button = new JRadioButton("Single");
        button.addActionListener(new AbstractAction() {

            @Override
            public boolean isEnabled() {
                return true;
            }

            public void actionPerformed(ActionEvent e) {
                tree.getSelectionModel().setSelectionMode(
                        TreeSelectionModel.SINGLE_TREE_SELECTION);
            }
        });
        group.add(button);
        buttonPane.add(button);
        button = new JRadioButton("Contiguous");
        button.addActionListener(new AbstractAction() {

            @Override
            public boolean isEnabled() {
                return true;
            }

            public void actionPerformed(ActionEvent e) {
                tree.getSelectionModel().setSelectionMode(
                        TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
            }
        });
        group.add(button);
        buttonPane.add(button);
        button = new JRadioButton("Discontiguous");
        button.addActionListener(new AbstractAction() {

            @Override
            public boolean isEnabled() {
                return true;
            }

            public void actionPerformed(ActionEvent e) {
                tree.getSelectionModel().setSelectionMode(
                        TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
            }
        });
        button.setSelected(true);
        group.add(button);
        buttonPane.add(button);

        borderPane.add(buttonPane, BorderLayout.SOUTH);

        // NOTE: This will be enabled in a future release.
        // Create a label and combobox to determine how many clicks are
        // needed to expand.
/*
        JPanel               clickPanel = new JPanel();
        Object[]             values = { "Never", new Integer(1),
        new Integer(2), new Integer(3) };
        final JComboBox      clickCBox = new JComboBox(values);

        clickPanel.setLayout(new FlowLayout());
        clickPanel.add(new JLabel("Click count to expand:"));
        clickCBox.setSelectedIndex(2);
        clickCBox.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent ae) {
        Object       selItem = clickCBox.getSelectedItem();

        if(selItem instanceof Integer)
        tree.setToggleClickCount(((Integer)selItem).intValue());
        else // Don't toggle
        tree.setToggleClickCount(0);
        }
        });
        clickPanel.add(clickCBox);
        borderPane.add(clickPanel, BorderLayout.NORTH);
         */
        return borderPane;
    }

    /** Construct a menu. */
    private JMenuBar constructMenuBar() {
        JMenu menu;
        JMenuBar menuBar = new JMenuBar();
        JMenuItem menuItem;

        /* Good ol exit. */
        menu = new JMenu("File");
        menuBar.add(menu);

        menuItem = menu.add(new JMenuItem("Exit"));
        menuItem.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                System.exit(0);
            }
        });

        /* Tree related stuff. */
        menu = new JMenu("Tree");
        menuBar.add(menu);

        menuItem = menu.add(new JMenuItem("Add"));
        menuItem.addActionListener(new AddAction());

        menuItem = menu.add(new JMenuItem("Insert"));
        menuItem.addActionListener(new InsertAction());

        menuItem = menu.add(new JMenuItem("Reload"));
        menuItem.addActionListener(new ReloadAction());

        menuItem = menu.add(new JMenuItem("Remove"));
        menuItem.addActionListener(new RemoveAction());

        return menuBar;
    }

    /**
     * Returns the TreeNode instance that is selected in the tree.
     * If nothing is selected, null is returned.
     */
    protected DefaultMutableTreeNode getSelectedNode() {
        TreePath selPath = tree.getSelectionPath();

        if (selPath != null) {
            return (DefaultMutableTreeNode) selPath.getLastPathComponent();
        }
        return null;
    }

    /**
     * Returns the selected TreePaths in the tree, may return null if
     * nothing is selected.
     */
    protected TreePath[] getSelectedPaths() {
        return tree.getSelectionPaths();
    }

    protected DefaultMutableTreeNode createNewNode(String name) {
        return new DynamicTreeNode(new SampleData(null, Color.black, name));
    }


    /**
     * AddAction is used to add a new item after the selected item.
     */
    class AddAction extends Object implements ActionListener {

        /** Number of nodes that have been added. */
        public int addCount;

        /**
         * Messaged when the user clicks on the Add menu item.
         * Determines the selection from the Tree and adds an item
         * after that.  If nothing is selected, an item is added to
         * the root.
         */
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode lastItem = getSelectedNode();
            DefaultMutableTreeNode parent;

            /* Determine where to create the new node. */
            if (lastItem != null) {
                parent = (DefaultMutableTreeNode) lastItem.getParent();
                if (parent == null) {
                    parent = (DefaultMutableTreeNode) treeModel.getRoot();
                    lastItem = null;
                }
            } else {
                parent = (DefaultMutableTreeNode) treeModel.getRoot();
            }
            if (parent == null) {
                // new root
                treeModel.setRoot(createNewNode("Added " + Integer.toString(
                        addCount++)));
            } else {
                int newIndex;
                if (lastItem == null) {
                    newIndex = treeModel.getChildCount(parent);
                } else {
                    newIndex = parent.getIndex(lastItem) + 1;
                }

                /* Let the treemodel know. */
                treeModel.insertNodeInto(createNewNode("Added " + Integer.
                        toString(addCount++)),
                        parent, newIndex);
            }
        }
    } // End of SampleTree.AddAction


    /**
     * InsertAction is used to insert a new item before the selected item.
     */
    class InsertAction extends Object implements ActionListener {

        /** Number of nodes that have been added. */
        public int insertCount;

        /**
         * Messaged when the user clicks on the Insert menu item.
         * Determines the selection from the Tree and inserts an item
         * after that.  If nothing is selected, an item is added to
         * the root.
         */
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode lastItem = getSelectedNode();
            DefaultMutableTreeNode parent;

            /* Determine where to create the new node. */
            if (lastItem != null) {
                parent = (DefaultMutableTreeNode) lastItem.getParent();
                if (parent == null) {
                    parent = (DefaultMutableTreeNode) treeModel.getRoot();
                    lastItem = null;
                }
            } else {
                parent = (DefaultMutableTreeNode) treeModel.getRoot();
            }
            if (parent == null) {
                // new root
                treeModel.setRoot(createNewNode("Inserted " + Integer.toString(
                        insertCount++)));
            } else {
                int newIndex;

                if (lastItem == null) {
                    newIndex = treeModel.getChildCount(parent);
                } else {
                    newIndex = parent.getIndex(lastItem);
                }

                /* Let the treemodel know. */
                treeModel.insertNodeInto(createNewNode("Inserted " + Integer.
                        toString(insertCount++)),
                        parent, newIndex);
            }
        }
    } // End of SampleTree.InsertAction


    /**
     * ReloadAction is used to reload from the selected node.  If nothing
     * is selected, reload is not issued.
     */
    class ReloadAction extends Object implements ActionListener {

        /**
         * Messaged when the user clicks on the Reload menu item.
         * Determines the selection from the Tree and asks the treemodel
         * to reload from that node.
         */
        public void actionPerformed(ActionEvent e) {
            DefaultMutableTreeNode lastItem = getSelectedNode();

            if (lastItem != null) {
                treeModel.reload(lastItem);
            }
        }
    } // End of SampleTree.ReloadAction


    /**
     * RemoveAction removes the selected node from the tree.  If
     * The root or nothing is selected nothing is removed.
     */
    class RemoveAction extends Object implements ActionListener {

        /**
         * Removes the selected item as long as it isn't root.
         */
        public void actionPerformed(ActionEvent e) {
            TreePath[] selected = getSelectedPaths();

            if (selected != null && selected.length > 0) {
                TreePath shallowest;

                // The remove process consists of the following steps:
                // 1 - find the shallowest selected TreePath, the shallowest
                //     path is the path with the smallest number of path
                //     components.
                // 2 - Find the siblings of this TreePath
                // 3 - Remove from selected the TreePaths that are descendants
                //     of the paths that are going to be removed. They will
                //     be removed as a result of their ancestors being
                //     removed.
                // 4 - continue until selected contains only null paths.
                while ((shallowest = findShallowestPath(selected)) != null) {
                    removeSiblings(shallowest, selected);
                }
            }
        }

        /**
         * Removes the sibling TreePaths of <code>path, that are
         * located in <code>paths.
         */
        private void removeSiblings(TreePath path, TreePath[] paths) {
            // Find the siblings
            if (path.getPathCount() == 1) {
                // Special case, set the root to null
                for (int counter = paths.length - 1; counter >= 0; counter--) {
                    paths[counter] = null;
                }
                treeModel.setRoot(null);
            } else {
                // Find the siblings of path.
                TreePath parent = path.getParentPath();
                MutableTreeNode parentNode = (MutableTreeNode) parent.
                        getLastPathComponent();
                ArrayList<TreePath> toRemove = new ArrayList();

                // First pass, find paths with a parent TreePath of parent
                for (int counter = paths.length - 1; counter >= 0; counter--) {
                    if (paths[counter] != null && paths[counter].getParentPath().
                            equals(parent)) {
                        toRemove.add(paths[counter]);
                        paths[counter] = null;
                    }
                }

                // Second pass, remove any paths that are descendants of the
                // paths that are going to be removed. These paths are
                // implicitly removed as a result of removing the paths in
                // toRemove
                int rCount = toRemove.size();
                for (int counter = paths.length - 1; counter >= 0; counter--) {
                    if (paths[counter] != null) {
                        for (int rCounter = rCount - 1; rCounter >= 0;
                                rCounter--) {
                            if ((toRemove.get(rCounter)).isDescendant(
                                    paths[counter])) {
                                paths[counter] = null;
                            }
                        }
                    }
                }

                // Sort the siblings based on position in the model
                if (rCount > 1) {
                    Collections.sort(toRemove, new PositionComparator());
                }
                int[] indices = new int[rCount];
                Object[] removedNodes = new Object[rCount];
                for (int counter = rCount - 1; counter >= 0; counter--) {
                    removedNodes[counter] = (toRemove.get(counter)).
                            getLastPathComponent();
                    indices[counter] = treeModel.getIndexOfChild(parentNode,
                            removedNodes[counter]);
                    parentNode.remove(indices[counter]);
                }
                treeModel.nodesWereRemoved(parentNode, indices, removedNodes);
            }
        }

        /**
         * Returns the TreePath with the smallest path count in
         * <code>paths. Will return null if there is no non-null
         * TreePath is <code>paths.
         */
        private TreePath findShallowestPath(TreePath[] paths) {
            int shallowest = -1;
            TreePath shallowestPath = null;

            for (int counter = paths.length - 1; counter >= 0; counter--) {
                if (paths[counter] != null) {
                    if (shallowest != -1) {
                        if (paths[counter].getPathCount() < shallowest) {
                            shallowest = paths[counter].getPathCount();
                            shallowestPath = paths[counter];
                            if (shallowest == 1) {
                                return shallowestPath;
                            }
                        }
                    } else {
                        shallowestPath = paths[counter];
                        shallowest = paths[counter].getPathCount();
                    }
                }
            }
            return shallowestPath;
        }


        /**
         * An Comparator that bases the return value on the index of the
         * passed in objects in the TreeModel.
         * <p>
         * This is actually rather expensive, it would be more efficient
         * to extract the indices and then do the comparision.
         */
        private class PositionComparator implements Comparator<TreePath> {

            public int compare(TreePath p1, TreePath p2) {
                int p1Index = treeModel.getIndexOfChild(p1.getParentPath().
                        getLastPathComponent(), p1.getLastPathComponent());
                int p2Index = treeModel.getIndexOfChild(p2.getParentPath().
                        getLastPathComponent(), p2.getLastPathComponent());
                return p1Index - p2Index;
            }
        }
    } // End of SampleTree.RemoveAction


    /**
     * ShowHandlesChangeListener implements the ChangeListener interface
     * to toggle the state of showing the handles in the tree.
     */
    class ShowHandlesChangeListener extends Object implements ChangeListener {

        public void stateChanged(ChangeEvent e) {
            tree.setShowsRootHandles(((JCheckBox) e.getSource()).isSelected());
        }
    } // End of class SampleTree.ShowHandlesChangeListener


    /**
     * ShowRootChangeListener implements the ChangeListener interface
     * to toggle the state of showing the root node in the tree.
     */
    class ShowRootChangeListener extends Object implements ChangeListener {

        public void stateChanged(ChangeEvent e) {
            tree.setRootVisible(((JCheckBox) e.getSource()).isSelected());
        }
    } // End of class SampleTree.ShowRootChangeListener


    /**
     * TreeEditableChangeListener implements the ChangeListener interface
     * to toggle between allowing editing and now allowing editing in
     * the tree.
     */
    class TreeEditableChangeListener extends Object implements ChangeListener {

        public void stateChanged(ChangeEvent e) {
            tree.setEditable(((JCheckBox) e.getSource()).isSelected());
        }
    } // End of class SampleTree.TreeEditableChangeListener

    public static void main(String args[]) {
        try {
            SwingUtilities.invokeAndWait(new Runnable() {

                @SuppressWarnings(value = "ResultOfObjectAllocationIgnored")
                public void run() {
                    new SampleTree();
                }
            });
        } catch (InterruptedException ex) {
            Logger.getLogger(SampleTree.class.getName()).log(Level.SEVERE, null,
                    ex);
        } catch (InvocationTargetException ex) {
            Logger.getLogger(SampleTree.class.getName()).log(Level.SEVERE, null,
                    ex);
        }
    }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java SampleTree.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.