home *** CD-ROM | disk | FTP | other *** search
/ Programming Languages Suite / JBuilder8.iso / Solaris / resource / jre / demo / jfc / SampleTree / src / SampleTree.java < prev    next >
Encoding:
Java Source  |  2002-09-06  |  21.1 KB  |  611 lines

  1. /*
  2.  * Copyright (c) 2002 Sun Microsystems, Inc. All  Rights Reserved.
  3.  * 
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  * 
  8.  * -Redistributions of source code must retain the above copyright
  9.  *  notice, this list of conditions and the following disclaimer.
  10.  * 
  11.  * -Redistribution in binary form must reproduct the above copyright
  12.  *  notice, this list of conditions and the following disclaimer in
  13.  *  the documentation and/or other materials provided with the distribution.
  14.  * 
  15.  * Neither the name of Sun Microsystems, Inc. or the names of contributors
  16.  * may be used to endorse or promote products derived from this software
  17.  * without specific prior written permission.
  18.  * 
  19.  * This software is provided "AS IS," without a warranty of any kind. ALL
  20.  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
  21.  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
  22.  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT
  23.  * BE LIABLE FOR ANY DAMAGES OR LIABILITIES SUFFERED BY LICENSEE AS A RESULT
  24.  * OF OR RELATING TO USE, MODIFICATION OR DISTRIBUTION OF THE SOFTWARE OR ITS
  25.  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
  26.  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
  27.  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
  28.  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE SOFTWARE, EVEN
  29.  * IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
  30.  * 
  31.  * You acknowledge that Software is not designed, licensed or intended for
  32.  * use in the design, construction, operation or maintenance of any nuclear
  33.  * facility.
  34.  */
  35.  
  36. /*
  37.  * @(#)SampleTree.java    1.21 02/06/13
  38.  */
  39.  
  40. import javax.swing.*;
  41. import javax.swing.event.*;
  42. import java.awt.BorderLayout;
  43. import java.awt.Color;
  44. import java.awt.Dimension;
  45. import java.awt.FlowLayout;
  46. import java.awt.event.ActionEvent;
  47. import java.awt.event.ActionListener;
  48. import java.awt.event.WindowAdapter;
  49. import java.awt.event.WindowEvent;
  50. import java.util.*;
  51. import javax.swing.tree.*;
  52.  
  53. /**
  54.   * A demo for illustrating how to do different things with JTree.
  55.   * The data that this displays is rather boring, that is each node will
  56.   * have 7 children that have random names based on the fonts.  Each node
  57.   * is then drawn with that font and in a different color.
  58.   * While the data isn't interesting the example illustrates a number
  59.   * of things:
  60.   *
  61.   * For an example of dynamicaly loading children refer to DynamicTreeNode.
  62.   * For an example of adding/removing/inserting/reloading refer to the inner
  63.   *     classes of this class, AddAction, RemovAction, InsertAction and
  64.   *     ReloadAction.
  65.   * For an example of creating your own cell renderer refer to
  66.   *     SampleTreeCellRenderer.
  67.   * For an example of subclassing JTreeModel for editing refer to
  68.   *     SampleTreeModel.
  69.   *
  70.   * @version 1.21 06/13/02
  71.   * @author Scott Violet
  72.   */
  73.  
  74. public class SampleTree
  75. {
  76.     /** Window for showing Tree. */
  77.     protected JFrame            frame;
  78.     /** Tree used for the example. */
  79.     protected JTree             tree;
  80.     /** Tree model. */
  81.     protected DefaultTreeModel        treeModel;
  82.  
  83.     /**
  84.       * Constructs a new instance of SampleTree.
  85.       */
  86.     public SampleTree() {
  87.     // Force SampleTree to come up in the Cross Platform L&F
  88.     try {
  89.         UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
  90.         // If you want the System L&F instead, comment out the above line and
  91.         // uncomment the following:
  92.         // UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  93.     } catch (Exception exc) {
  94.         System.err.println("Error loading L&F: " + exc);
  95.     }
  96.  
  97.  
  98.     JMenuBar         menuBar = constructMenuBar();
  99.     JPanel           panel = new JPanel(true);
  100.  
  101.     frame = new JFrame("SampleTree");
  102.     frame.getContentPane().add("Center", panel);
  103.     frame.setJMenuBar(menuBar);
  104.     frame.setBackground(Color.lightGray);
  105.  
  106.     /* Create the JTreeModel. */
  107.     DefaultMutableTreeNode root = createNewNode("Root");
  108.     treeModel = new SampleTreeModel(root);
  109.  
  110.     /* Create the tree. */
  111.     tree = new JTree(treeModel);
  112.  
  113.     /* Enable tool tips for the tree, without this tool tips will not
  114.        be picked up. */
  115.     ToolTipManager.sharedInstance().registerComponent(tree);
  116.  
  117.     /* Make the tree use an instance of SampleTreeCellRenderer for
  118.        drawing. */
  119.     tree.setCellRenderer(new SampleTreeCellRenderer());
  120.  
  121.     /* Make tree ask for the height of each row. */
  122.     tree.setRowHeight(-1);
  123.  
  124.     /* Put the Tree in a scroller. */
  125.     JScrollPane        sp = new JScrollPane();
  126.     sp.setPreferredSize(new Dimension(300, 300));
  127.     sp.getViewport().add(tree);
  128.  
  129.     /* And show it. */
  130.     panel.setLayout(new BorderLayout());
  131.     panel.add("Center", sp);
  132.     panel.add("South", constructOptionsPanel());
  133.  
  134.     frame.addWindowListener( new WindowAdapter() {
  135.         public void windowClosing(WindowEvent e) {System.exit(0);}});
  136.  
  137.     frame.pack();
  138.     frame.show();
  139.     }
  140.  
  141.     /** Constructs a JPanel containing check boxes for the different
  142.       * options that tree supports. */
  143.     private JPanel constructOptionsPanel() {
  144.     JCheckBox               aCheckbox;
  145.     JPanel           retPanel = new JPanel(false);
  146.     JPanel           borderPane = new JPanel(false);
  147.  
  148.     borderPane.setLayout(new BorderLayout());
  149.     retPanel.setLayout(new FlowLayout());
  150.  
  151.     aCheckbox = new JCheckBox("show handles");
  152.     aCheckbox.setSelected(tree.getShowsRootHandles());
  153.     aCheckbox.addChangeListener(new ShowHandlesChangeListener());
  154.     retPanel.add(aCheckbox);
  155.  
  156.     aCheckbox = new JCheckBox("show root");
  157.     aCheckbox.setSelected(tree.isRootVisible());
  158.     aCheckbox.addChangeListener(new ShowRootChangeListener());
  159.     retPanel.add(aCheckbox);
  160.  
  161.     aCheckbox = new JCheckBox("editable");
  162.     aCheckbox.setSelected(tree.isEditable());
  163.     aCheckbox.addChangeListener(new TreeEditableChangeListener());
  164.     aCheckbox.setToolTipText("Triple click to edit");
  165.     retPanel.add(aCheckbox);
  166.  
  167.     borderPane.add(retPanel, BorderLayout.CENTER);
  168.  
  169.     /* Create a set of radio buttons that dictate what selection should
  170.        be allowed in the tree. */
  171.     ButtonGroup           group = new ButtonGroup();
  172.     JPanel         buttonPane = new JPanel(false);
  173.     JRadioButton          button;
  174.  
  175.     buttonPane.setLayout(new FlowLayout());
  176.     button = new JRadioButton("Single");
  177.     button.addActionListener(new AbstractAction() {
  178.         public boolean isEnabled() { return true; }
  179.         public void actionPerformed(ActionEvent e) {
  180.         tree.getSelectionModel().setSelectionMode
  181.             (TreeSelectionModel.SINGLE_TREE_SELECTION);
  182.         }
  183.     });
  184.     group.add(button);
  185.     buttonPane.add(button);
  186.     button = new JRadioButton("Contiguous");
  187.     button.addActionListener(new AbstractAction() {
  188.         public boolean isEnabled() { return true; }
  189.         public void actionPerformed(ActionEvent e) {
  190.         tree.getSelectionModel().setSelectionMode
  191.             (TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
  192.         }
  193.     });
  194.     group.add(button);
  195.     buttonPane.add(button);
  196.     button = new JRadioButton("Discontiguous");
  197.     button.addActionListener(new AbstractAction() {
  198.         public boolean isEnabled() { return true; }
  199.         public void actionPerformed(ActionEvent e) {
  200.         tree.getSelectionModel().setSelectionMode
  201.             (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
  202.         }
  203.     });
  204.     button.setSelected(true);
  205.     group.add(button);
  206.     buttonPane.add(button);
  207.  
  208.     borderPane.add(buttonPane, BorderLayout.SOUTH);
  209.  
  210.     // NOTE: This will be enabled in a future release.
  211.     // Create a label and combobox to determine how many clicks are
  212.     // needed to expand.
  213. /*
  214.     JPanel               clickPanel = new JPanel();
  215.     Object[]             values = { "Never", new Integer(1),
  216.                     new Integer(2), new Integer(3) };
  217.     final JComboBox      clickCBox = new JComboBox(values);
  218.  
  219.     clickPanel.setLayout(new FlowLayout());
  220.     clickPanel.add(new JLabel("Click count to expand:"));
  221.     clickCBox.setSelectedIndex(2);
  222.     clickCBox.addActionListener(new ActionListener() {
  223.         public void actionPerformed(ActionEvent ae) {
  224.         Object       selItem = clickCBox.getSelectedItem();
  225.  
  226.         if(selItem instanceof Integer)
  227.             tree.setToggleClickCount(((Integer)selItem).intValue());
  228.         else // Don't toggle
  229.             tree.setToggleClickCount(0);
  230.         }
  231.     });
  232.     clickPanel.add(clickCBox);
  233.     borderPane.add(clickPanel, BorderLayout.NORTH);
  234. */
  235.     return borderPane;
  236.     }
  237.  
  238.     /** Construct a menu. */
  239.     private JMenuBar constructMenuBar() {
  240.     JMenu            menu;
  241.     JMenuBar         menuBar = new JMenuBar();
  242.     JMenuItem        menuItem;
  243.  
  244.     /* Good ol exit. */
  245.     menu = new JMenu("File");
  246.     menuBar.add(menu);
  247.  
  248.     menuItem = menu.add(new JMenuItem("Exit"));
  249.     menuItem.addActionListener(new ActionListener() {
  250.         public void actionPerformed(ActionEvent e) {
  251.         System.exit(0);
  252.         }});
  253.  
  254.     /* Tree related stuff. */
  255.     menu = new JMenu("Tree");
  256.     menuBar.add(menu);
  257.  
  258.     menuItem = menu.add(new JMenuItem("Add"));
  259.     menuItem.addActionListener(new AddAction());
  260.  
  261.     menuItem = menu.add(new JMenuItem("Insert"));
  262.     menuItem.addActionListener(new InsertAction());
  263.  
  264.     menuItem = menu.add(new JMenuItem("Reload"));
  265.     menuItem.addActionListener(new ReloadAction());
  266.  
  267.     menuItem = menu.add(new JMenuItem("Remove"));
  268.     menuItem.addActionListener(new RemoveAction());
  269.  
  270.     return menuBar;
  271.     }
  272.  
  273.     /**
  274.       * Returns the TreeNode instance that is selected in the tree.
  275.       * If nothing is selected, null is returned.
  276.       */
  277.     protected DefaultMutableTreeNode getSelectedNode() {
  278.     TreePath   selPath = tree.getSelectionPath();
  279.  
  280.     if(selPath != null)
  281.         return (DefaultMutableTreeNode)selPath.getLastPathComponent();
  282.     return null;
  283.     }
  284.  
  285.     /**
  286.      * Returns the selected TreePaths in the tree, may return null if
  287.      * nothing is selected.
  288.      */
  289.     protected TreePath[] getSelectedPaths() {
  290.         return tree.getSelectionPaths();
  291.     }
  292.  
  293.     protected DefaultMutableTreeNode createNewNode(String name) {
  294.     return new DynamicTreeNode(new SampleData(null, Color.black, name));
  295.     }
  296.  
  297.     /**
  298.       * AddAction is used to add a new item after the selected item.
  299.       */
  300.     class AddAction extends Object implements ActionListener
  301.     {
  302.     /** Number of nodes that have been added. */
  303.     public int               addCount;
  304.  
  305.     /**
  306.       * Messaged when the user clicks on the Add menu item.
  307.       * Determines the selection from the Tree and adds an item
  308.       * after that.  If nothing is selected, an item is added to
  309.       * the root.
  310.       */
  311.     public void actionPerformed(ActionEvent e) {
  312.         DefaultMutableTreeNode          lastItem = getSelectedNode();
  313.         DefaultMutableTreeNode          parent;
  314.  
  315.         /* Determine where to create the new node. */
  316.         if(lastItem != null) {
  317.         parent = (DefaultMutableTreeNode)lastItem.getParent();
  318.         if(parent == null) {
  319.             parent = (DefaultMutableTreeNode)treeModel.getRoot();
  320.             lastItem = null;
  321.         }
  322.         }
  323.         else
  324.         parent = (DefaultMutableTreeNode)treeModel.getRoot();
  325.             if (parent == null) {
  326.                 // new root
  327.                 treeModel.setRoot(createNewNode("Added " +
  328.                                                 Integer.toString(addCount++)));
  329.             }
  330.             else {
  331.                 int               newIndex;
  332.                 if(lastItem == null)
  333.                     newIndex = treeModel.getChildCount(parent);
  334.                 else
  335.                     newIndex = parent.getIndex(lastItem) + 1;
  336.  
  337.                 /* Let the treemodel know. */
  338.                 treeModel.insertNodeInto(createNewNode("Added " +
  339.                                     Integer.toString(addCount++)),
  340.                     parent, newIndex);
  341.             }
  342.     }
  343.     } // End of SampleTree.AddAction
  344.  
  345.  
  346.     /**
  347.       * InsertAction is used to insert a new item before the selected item.
  348.       */
  349.     class InsertAction extends Object implements ActionListener
  350.     {
  351.     /** Number of nodes that have been added. */
  352.     public int               insertCount;
  353.  
  354.     /**
  355.       * Messaged when the user clicks on the Insert menu item.
  356.       * Determines the selection from the Tree and inserts an item
  357.       * after that.  If nothing is selected, an item is added to
  358.       * the root.
  359.       */
  360.     public void actionPerformed(ActionEvent e) {
  361.         DefaultMutableTreeNode          lastItem = getSelectedNode();
  362.         DefaultMutableTreeNode          parent;
  363.  
  364.         /* Determine where to create the new node. */
  365.         if(lastItem != null) {
  366.         parent = (DefaultMutableTreeNode)lastItem.getParent();
  367.         if(parent == null) {
  368.             parent = (DefaultMutableTreeNode)treeModel.getRoot();
  369.             lastItem = null;
  370.         }
  371.         }
  372.         else
  373.         parent = (DefaultMutableTreeNode)treeModel.getRoot();
  374.             if (parent == null) {
  375.                 // new root
  376.                 treeModel.setRoot(createNewNode("Inserted " +
  377.                                              Integer.toString(insertCount++)));
  378.             }
  379.             else {
  380.                 int               newIndex;
  381.  
  382.                 if(lastItem == null)
  383.                     newIndex = treeModel.getChildCount(parent);
  384.                 else
  385.                     newIndex = parent.getIndex(lastItem);
  386.  
  387.                 /* Let the treemodel know. */
  388.                 treeModel.insertNodeInto(createNewNode("Inserted " +
  389.                      Integer.toString(insertCount++)),
  390.                                          parent, newIndex);
  391.             }
  392.     }
  393.     } // End of SampleTree.InsertAction
  394.  
  395.  
  396.     /**
  397.       * ReloadAction is used to reload from the selected node.  If nothing
  398.       * is selected, reload is not issued.
  399.       */
  400.     class ReloadAction extends Object implements ActionListener
  401.     {
  402.     /**
  403.       * Messaged when the user clicks on the Reload menu item.
  404.       * Determines the selection from the Tree and asks the treemodel
  405.       * to reload from that node.
  406.       */
  407.     public void actionPerformed(ActionEvent e) {
  408.         DefaultMutableTreeNode          lastItem = getSelectedNode();
  409.  
  410.         if(lastItem != null)
  411.         treeModel.reload(lastItem);
  412.     }
  413.     } // End of SampleTree.ReloadAction
  414.  
  415.     /**
  416.       * RemoveAction removes the selected node from the tree.  If
  417.       * The root or nothing is selected nothing is removed.
  418.       */
  419.     class RemoveAction extends Object implements ActionListener
  420.     {
  421.     /**
  422.       * Removes the selected item as long as it isn't root.
  423.       */
  424.     public void actionPerformed(ActionEvent e) {
  425.             TreePath[] selected = getSelectedPaths();
  426.  
  427.             if (selected != null && selected.length > 0) {
  428.                 TreePath shallowest;
  429.  
  430.                 // The remove process consists of the following steps:
  431.                 // 1 - find the shallowest selected TreePath, the shallowest
  432.                 //     path is the path with the smallest number of path
  433.                 //     components.
  434.                 // 2 - Find the siblings of this TreePath
  435.                 // 3 - Remove from selected the TreePaths that are descendants
  436.                 //     of the paths that are going to be removed. They will
  437.                 //     be removed as a result of their ancestors being
  438.                 //     removed.
  439.                 // 4 - continue until selected contains only null paths.
  440.                 while ((shallowest = findShallowestPath(selected)) != null) {
  441.                     removeSiblings(shallowest, selected);
  442.                 }
  443.             }
  444.     }
  445.  
  446.         /**
  447.          * Removes the sibling TreePaths of <code>path</code>, that are
  448.          * located in <code>paths</code>.
  449.          */
  450.         private void removeSiblings(TreePath path, TreePath[] paths) {
  451.             // Find the siblings
  452.             if (path.getPathCount() == 1) {
  453.                 // Special case, set the root to null
  454.                 for (int counter = paths.length - 1; counter >= 0; counter--) {
  455.                     paths[counter] = null;
  456.                 }
  457.                 treeModel.setRoot(null);
  458.             }
  459.             else {
  460.                 // Find the siblings of path.
  461.                 TreePath parent = path.getParentPath();
  462.                 MutableTreeNode parentNode = (MutableTreeNode)parent.
  463.                                 getLastPathComponent();
  464.                 ArrayList toRemove = new ArrayList();
  465.                 int depth = parent.getPathCount();
  466.  
  467.                 // First pass, find paths with a parent TreePath of parent
  468.                 for (int counter = paths.length - 1; counter >= 0; counter--) {
  469.                     if (paths[counter] != null && paths[counter].
  470.                               getParentPath().equals(parent)) {
  471.                         toRemove.add(paths[counter]);
  472.                         paths[counter] = null;
  473.                     }
  474.                 }
  475.  
  476.                 // Second pass, remove any paths that are descendants of the
  477.                 // paths that are going to be removed. These paths are
  478.                 // implicitly removed as a result of removing the paths in
  479.                 // toRemove
  480.                 int rCount = toRemove.size();
  481.                 for (int counter = paths.length - 1; counter >= 0; counter--) {
  482.                     if (paths[counter] != null) {
  483.                         for (int rCounter = rCount - 1; rCounter >= 0;
  484.                              rCounter--) {
  485.                             if (((TreePath)toRemove.get(rCounter)).
  486.                                            isDescendant(paths[counter])) {
  487.                                 paths[counter] = null;
  488.                             }
  489.                         }
  490.                     }
  491.                 }
  492.  
  493.                 // Sort the siblings based on position in the model
  494.                 if (rCount > 1) {
  495.                     Collections.sort(toRemove, new PositionComparator());
  496.                 }
  497.                 int[] indices = new int[rCount];
  498.                 Object[] removedNodes = new Object[rCount];
  499.                 for (int counter = rCount - 1; counter >= 0; counter--) {
  500.                     removedNodes[counter] = ((TreePath)toRemove.get(counter)).
  501.                                 getLastPathComponent();
  502.                     indices[counter] = treeModel.getIndexOfChild
  503.                                         (parentNode, removedNodes[counter]);
  504.                     parentNode.remove(indices[counter]);
  505.                 }
  506.                 treeModel.nodesWereRemoved(parentNode, indices, removedNodes);
  507.             }
  508.         }
  509.  
  510.         /**
  511.          * Returns the TreePath with the smallest path count in
  512.          * <code>paths</code>. Will return null if there is no non-null
  513.          * TreePath is <code>paths</code>.
  514.          */
  515.         private TreePath findShallowestPath(TreePath[] paths) {
  516.             int shallowest = -1;
  517.             TreePath shallowestPath = null;
  518.  
  519.             for (int counter = paths.length - 1; counter >= 0; counter--) {
  520.                 if (paths[counter] != null) {
  521.                     if (shallowest != -1) {
  522.                         if (paths[counter].getPathCount() < shallowest) {
  523.                             shallowest = paths[counter].getPathCount();
  524.                             shallowestPath = paths[counter];
  525.                             if (shallowest == 1) {
  526.                                 return shallowestPath;
  527.                             }
  528.                         }
  529.                     }
  530.                     else {
  531.                         shallowestPath = paths[counter];
  532.                         shallowest = paths[counter].getPathCount();
  533.                     }
  534.                 }
  535.             }
  536.             return shallowestPath;
  537.         }
  538.  
  539.  
  540.         /**
  541.          * An Comparator that bases the return value on the index of the
  542.          * passed in objects in the TreeModel.
  543.          * <p>
  544.          * This is actually rather expensive, it would be more efficient
  545.          * to extract the indices and then do the comparision.
  546.          */
  547.         private class PositionComparator implements Comparator {
  548.             public int compare(Object o1, Object o2) {
  549.                 TreePath p1 = (TreePath)o1;
  550.                 int o1Index = treeModel.getIndexOfChild(p1.getParentPath().
  551.                           getLastPathComponent(), p1.getLastPathComponent());
  552.                 TreePath p2 = (TreePath)o2;
  553.                 int o2Index = treeModel.getIndexOfChild(p2.getParentPath().
  554.                           getLastPathComponent(), p2.getLastPathComponent());
  555.                 return o1Index - o2Index;
  556.             }
  557.  
  558.             public boolean equals(Object obj) {
  559.                 return super.equals(obj);
  560.             }
  561.         }
  562.  
  563.     } // End of SampleTree.RemoveAction
  564.  
  565.  
  566.     /**
  567.       * ShowHandlesChangeListener implements the ChangeListener interface
  568.       * to toggle the state of showing the handles in the tree.
  569.       */
  570.     class ShowHandlesChangeListener extends Object implements ChangeListener
  571.     {
  572.     public void stateChanged(ChangeEvent e) {
  573.         tree.setShowsRootHandles(((JCheckBox)e.getSource()).isSelected());
  574.     }
  575.  
  576.     } // End of class SampleTree.ShowHandlesChangeListener
  577.  
  578.  
  579.     /**
  580.       * ShowRootChangeListener implements the ChangeListener interface
  581.       * to toggle the state of showing the root node in the tree.
  582.       */
  583.     class ShowRootChangeListener extends Object implements ChangeListener
  584.     {
  585.     public void stateChanged(ChangeEvent e) {
  586.         tree.setRootVisible(((JCheckBox)e.getSource()).isSelected());
  587.     }
  588.  
  589.     } // End of class SampleTree.ShowRootChangeListener
  590.  
  591.  
  592.     /**
  593.       * TreeEditableChangeListener implements the ChangeListener interface
  594.       * to toggle between allowing editing and now allowing editing in
  595.       * the tree.
  596.       */
  597.     class TreeEditableChangeListener extends Object implements ChangeListener
  598.     {
  599.     public void stateChanged(ChangeEvent e) {
  600.         tree.setEditable(((JCheckBox)e.getSource()).isSelected());
  601.     }
  602.  
  603.     } // End of class SampleTree.TreeEditableChangeListener
  604.  
  605.  
  606.     static public void main(String args[]) {
  607.     new SampleTree();
  608.     }
  609.  
  610. }
  611.