KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > SampleTree


1 /*
2  * @(#)SampleTree.java 1.26 05/11/17
3  *
4  * Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * -Redistribution of source code must retain the above copyright notice, this
10  * list of conditions and the following disclaimer.
11  *
12  * -Redistribution in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * Neither the name of Sun Microsystems, Inc. or the names of contributors may
17  * be used to endorse or promote products derived from this software without
18  * specific prior written permission.
19  *
20  * This software is provided "AS IS," without a warranty of any kind. ALL
21  * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
22  * ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23  * OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
24  * AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
25  * AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
26  * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
27  * REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
28  * INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
29  * OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
30  * EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
31  *
32  * You acknowledge that this software is not designed, licensed or intended
33  * for use in the design, construction, operation or maintenance of any
34  * nuclear facility.
35  */

36
37 /*
38  * @(#)SampleTree.java 1.26 05/11/17
39  */

40
41 import javax.swing.*;
42 import javax.swing.event.*;
43 import java.awt.BorderLayout JavaDoc;
44 import java.awt.Color JavaDoc;
45 import java.awt.Dimension JavaDoc;
46 import java.awt.FlowLayout JavaDoc;
47 import java.awt.event.ActionEvent JavaDoc;
48 import java.awt.event.ActionListener JavaDoc;
49 import java.awt.event.WindowAdapter JavaDoc;
50 import java.awt.event.WindowEvent JavaDoc;
51 import java.util.*;
52 import javax.swing.border.*;
53 import javax.swing.tree.*;
54
55 /**
56   * A demo for illustrating how to do different things with JTree.
57   * The data that this displays is rather boring, that is each node will
58   * have 7 children that have random names based on the fonts. Each node
59   * is then drawn with that font and in a different color.
60   * While the data isn't interesting the example illustrates a number
61   * of things:
62   *
63   * For an example of dynamicaly loading children refer to DynamicTreeNode.
64   * For an example of adding/removing/inserting/reloading refer to the inner
65   * classes of this class, AddAction, RemovAction, InsertAction and
66   * ReloadAction.
67   * For an example of creating your own cell renderer refer to
68   * SampleTreeCellRenderer.
69   * For an example of subclassing JTreeModel for editing refer to
70   * SampleTreeModel.
71   *
72   * @version 1.26 11/17/05
73   * @author Scott Violet
74   */

75
76 public class SampleTree
77 {
78     /** Window for showing Tree. */
79     protected JFrame frame;
80     /** Tree used for the example. */
81     protected JTree tree;
82     /** Tree model. */
83     protected DefaultTreeModel treeModel;
84
85     /**
86       * Constructs a new instance of SampleTree.
87       */

88     public SampleTree() {
89     // Force SampleTree to come up in the Cross Platform L&F
90
try {
91         UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
92         // If you want the System L&F instead, comment out the above line and
93
// uncomment the following:
94
// UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
95
} catch (Exception JavaDoc exc) {
96         System.err.println("Error loading L&F: " + exc);
97     }
98
99
100     JMenuBar menuBar = constructMenuBar();
101     JPanel panel = new JPanel(true);
102
103     frame = new JFrame("SampleTree");
104     frame.getContentPane().add("Center", panel);
105     frame.setJMenuBar(menuBar);
106     frame.setBackground(Color.lightGray);
107
108     /* Create the JTreeModel. */
109     DefaultMutableTreeNode root = createNewNode("Root");
110     treeModel = new SampleTreeModel(root);
111
112     /* Create the tree. */
113     tree = new JTree(treeModel);
114
115     /* Enable tool tips for the tree, without this tool tips will not
116        be picked up. */

117     ToolTipManager.sharedInstance().registerComponent(tree);
118
119     /* Make the tree use an instance of SampleTreeCellRenderer for
120        drawing. */

121     tree.setCellRenderer(new SampleTreeCellRenderer());
122
123     /* Make tree ask for the height of each row. */
124     tree.setRowHeight(-1);
125
126     /* Put the Tree in a scroller. */
127     JScrollPane sp = new JScrollPane();
128     sp.setPreferredSize(new Dimension JavaDoc(300, 300));
129     sp.getViewport().add(tree);
130
131     /* And show it. */
132     panel.setLayout(new BorderLayout JavaDoc());
133     panel.add("Center", sp);
134     panel.add("South", constructOptionsPanel());
135
136     frame.addWindowListener( new WindowAdapter JavaDoc() {
137         public void windowClosing(WindowEvent JavaDoc e) {System.exit(0);}});
138
139     frame.pack();
140     frame.show();
141     }
142
143     /** Constructs a JPanel containing check boxes for the different
144       * options that tree supports. */

145     private JPanel constructOptionsPanel() {
146     JCheckBox aCheckbox;
147     JPanel retPanel = new JPanel(false);
148     JPanel borderPane = new JPanel(false);
149
150     borderPane.setLayout(new BorderLayout JavaDoc());
151     retPanel.setLayout(new FlowLayout JavaDoc());
152
153     aCheckbox = new JCheckBox("show top level handles");
154     aCheckbox.setSelected(tree.getShowsRootHandles());
155     aCheckbox.addChangeListener(new ShowHandlesChangeListener());
156     retPanel.add(aCheckbox);
157
158     aCheckbox = new JCheckBox("show root");
159     aCheckbox.setSelected(tree.isRootVisible());
160     aCheckbox.addChangeListener(new ShowRootChangeListener());
161     retPanel.add(aCheckbox);
162
163     aCheckbox = new JCheckBox("editable");
164     aCheckbox.setSelected(tree.isEditable());
165     aCheckbox.addChangeListener(new TreeEditableChangeListener());
166     aCheckbox.setToolTipText("Triple click to edit");
167     retPanel.add(aCheckbox);
168
169     borderPane.add(retPanel, BorderLayout.CENTER);
170
171     /* Create a set of radio buttons that dictate what selection should
172        be allowed in the tree. */

173     ButtonGroup group = new ButtonGroup();
174     JPanel buttonPane = new JPanel(false);
175     JRadioButton button;
176
177     buttonPane.setLayout(new FlowLayout JavaDoc());
178     buttonPane.setBorder(new TitledBorder("Selection Mode"));
179     button = new JRadioButton("Single");
180     button.addActionListener(new AbstractAction() {
181         public boolean isEnabled() { return true; }
182         public void actionPerformed(ActionEvent JavaDoc e) {
183         tree.getSelectionModel().setSelectionMode
184             (TreeSelectionModel.SINGLE_TREE_SELECTION);
185         }
186     });
187     group.add(button);
188     buttonPane.add(button);
189     button = new JRadioButton("Contiguous");
190     button.addActionListener(new AbstractAction() {
191         public boolean isEnabled() { return true; }
192         public void actionPerformed(ActionEvent JavaDoc e) {
193         tree.getSelectionModel().setSelectionMode
194             (TreeSelectionModel.CONTIGUOUS_TREE_SELECTION);
195         }
196     });
197     group.add(button);
198     buttonPane.add(button);
199     button = new JRadioButton("Discontiguous");
200     button.addActionListener(new AbstractAction() {
201         public boolean isEnabled() { return true; }
202         public void actionPerformed(ActionEvent JavaDoc e) {
203         tree.getSelectionModel().setSelectionMode
204             (TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
205         }
206     });
207     button.setSelected(true);
208     group.add(button);
209     buttonPane.add(button);
210
211     borderPane.add(buttonPane, BorderLayout.SOUTH);
212
213     // NOTE: This will be enabled in a future release.
214
// Create a label and combobox to determine how many clicks are
215
// needed to expand.
216
/*
217     JPanel clickPanel = new JPanel();
218     Object[] values = { "Never", new Integer(1),
219                     new Integer(2), new Integer(3) };
220     final JComboBox clickCBox = new JComboBox(values);
221
222     clickPanel.setLayout(new FlowLayout());
223     clickPanel.add(new JLabel("Click count to expand:"));
224     clickCBox.setSelectedIndex(2);
225     clickCBox.addActionListener(new ActionListener() {
226         public void actionPerformed(ActionEvent ae) {
227         Object selItem = clickCBox.getSelectedItem();
228
229         if(selItem instanceof Integer)
230             tree.setToggleClickCount(((Integer)selItem).intValue());
231         else // Don't toggle
232             tree.setToggleClickCount(0);
233         }
234     });
235     clickPanel.add(clickCBox);
236     borderPane.add(clickPanel, BorderLayout.NORTH);
237 */

238     return borderPane;
239     }
240
241     /** Construct a menu. */
242     private JMenuBar constructMenuBar() {
243     JMenu menu;
244     JMenuBar menuBar = new JMenuBar();
245     JMenuItem menuItem;
246
247     /* Good ol exit. */
248     menu = new JMenu("File");
249     menuBar.add(menu);
250
251     menuItem = menu.add(new JMenuItem("Exit"));
252     menuItem.addActionListener(new ActionListener JavaDoc() {
253         public void actionPerformed(ActionEvent JavaDoc e) {
254         System.exit(0);
255         }});
256
257     /* Tree related stuff. */
258     menu = new JMenu("Tree");
259     menuBar.add(menu);
260
261     menuItem = menu.add(new JMenuItem("Add"));
262     menuItem.addActionListener(new AddAction());
263
264     menuItem = menu.add(new JMenuItem("Insert"));
265     menuItem.addActionListener(new InsertAction());
266
267     menuItem = menu.add(new JMenuItem("Reload"));
268     menuItem.addActionListener(new ReloadAction());
269
270     menuItem = menu.add(new JMenuItem("Remove"));
271     menuItem.addActionListener(new RemoveAction());
272
273     return menuBar;
274     }
275
276     /**
277       * Returns the TreeNode instance that is selected in the tree.
278       * If nothing is selected, null is returned.
279       */

280     protected DefaultMutableTreeNode getSelectedNode() {
281     TreePath selPath = tree.getSelectionPath();
282
283     if(selPath != null)
284         return (DefaultMutableTreeNode)selPath.getLastPathComponent();
285     return null;
286     }
287
288     /**
289      * Returns the selected TreePaths in the tree, may return null if
290      * nothing is selected.
291      */

292     protected TreePath[] getSelectedPaths() {
293         return tree.getSelectionPaths();
294     }
295
296     protected DefaultMutableTreeNode createNewNode(String JavaDoc name) {
297     return new DynamicTreeNode(new SampleData(null, Color.black, name));
298     }
299
300     /**
301       * AddAction is used to add a new item after the selected item.
302       */

303     class AddAction extends Object JavaDoc implements ActionListener JavaDoc
304     {
305     /** Number of nodes that have been added. */
306     public int addCount;
307
308     /**
309       * Messaged when the user clicks on the Add menu item.
310       * Determines the selection from the Tree and adds an item
311       * after that. If nothing is selected, an item is added to
312       * the root.
313       */

314     public void actionPerformed(ActionEvent JavaDoc e) {
315         DefaultMutableTreeNode lastItem = getSelectedNode();
316         DefaultMutableTreeNode parent;
317
318         /* Determine where to create the new node. */
319         if(lastItem != null) {
320         parent = (DefaultMutableTreeNode)lastItem.getParent();
321         if(parent == null) {
322             parent = (DefaultMutableTreeNode)treeModel.getRoot();
323             lastItem = null;
324         }
325         }
326         else
327         parent = (DefaultMutableTreeNode)treeModel.getRoot();
328             if (parent == null) {
329                 // new root
330
treeModel.setRoot(createNewNode("Added " +
331                                                 Integer.toString(addCount++)));
332             }
333             else {
334                 int newIndex;
335                 if(lastItem == null)
336                     newIndex = treeModel.getChildCount(parent);
337                 else
338                     newIndex = parent.getIndex(lastItem) + 1;
339
340                 /* Let the treemodel know. */
341                 treeModel.insertNodeInto(createNewNode("Added " +
342                                     Integer.toString(addCount++)),
343                     parent, newIndex);
344             }
345     }
346     } // End of SampleTree.AddAction
347

348
349     /**
350       * InsertAction is used to insert a new item before the selected item.
351       */

352     class InsertAction extends Object JavaDoc implements ActionListener JavaDoc
353     {
354     /** Number of nodes that have been added. */
355     public int insertCount;
356
357     /**
358       * Messaged when the user clicks on the Insert menu item.
359       * Determines the selection from the Tree and inserts an item
360       * after that. If nothing is selected, an item is added to
361       * the root.
362       */

363     public void actionPerformed(ActionEvent JavaDoc e) {
364         DefaultMutableTreeNode lastItem = getSelectedNode();
365         DefaultMutableTreeNode parent;
366
367         /* Determine where to create the new node. */
368         if(lastItem != null) {
369         parent = (DefaultMutableTreeNode)lastItem.getParent();
370         if(parent == null) {
371             parent = (DefaultMutableTreeNode)treeModel.getRoot();
372             lastItem = null;
373         }
374         }
375         else
376         parent = (DefaultMutableTreeNode)treeModel.getRoot();
377             if (parent == null) {
378                 // new root
379
treeModel.setRoot(createNewNode("Inserted " +
380                                              Integer.toString(insertCount++)));
381             }
382             else {
383                 int newIndex;
384
385                 if(lastItem == null)
386                     newIndex = treeModel.getChildCount(parent);
387                 else
388                     newIndex = parent.getIndex(lastItem);
389
390                 /* Let the treemodel know. */
391                 treeModel.insertNodeInto(createNewNode("Inserted " +
392                      Integer.toString(insertCount++)),
393                                          parent, newIndex);
394             }
395     }
396     } // End of SampleTree.InsertAction
397

398
399     /**
400       * ReloadAction is used to reload from the selected node. If nothing
401       * is selected, reload is not issued.
402       */

403     class ReloadAction extends Object JavaDoc implements ActionListener JavaDoc
404     {
405     /**
406       * Messaged when the user clicks on the Reload menu item.
407       * Determines the selection from the Tree and asks the treemodel
408       * to reload from that node.
409       */

410     public void actionPerformed(ActionEvent JavaDoc e) {
411         DefaultMutableTreeNode lastItem = getSelectedNode();
412
413         if(lastItem != null)
414         treeModel.reload(lastItem);
415     }
416     } // End of SampleTree.ReloadAction
417

418     /**
419       * RemoveAction removes the selected node from the tree. If
420       * The root or nothing is selected nothing is removed.
421       */

422     class RemoveAction extends Object JavaDoc implements ActionListener JavaDoc
423     {
424     /**
425       * Removes the selected item as long as it isn't root.
426       */

427     public void actionPerformed(ActionEvent JavaDoc e) {
428             TreePath[] selected = getSelectedPaths();
429
430             if (selected != null && selected.length > 0) {
431                 TreePath shallowest;
432
433                 // The remove process consists of the following steps:
434
// 1 - find the shallowest selected TreePath, the shallowest
435
// path is the path with the smallest number of path
436
// components.
437
// 2 - Find the siblings of this TreePath
438
// 3 - Remove from selected the TreePaths that are descendants
439
// of the paths that are going to be removed. They will
440
// be removed as a result of their ancestors being
441
// removed.
442
// 4 - continue until selected contains only null paths.
443
while ((shallowest = findShallowestPath(selected)) != null) {
444                     removeSiblings(shallowest, selected);
445                 }
446             }
447     }
448
449         /**
450          * Removes the sibling TreePaths of <code>path</code>, that are
451          * located in <code>paths</code>.
452          */

453         private void removeSiblings(TreePath path, TreePath[] paths) {
454             // Find the siblings
455
if (path.getPathCount() == 1) {
456                 // Special case, set the root to null
457
for (int counter = paths.length - 1; counter >= 0; counter--) {
458                     paths[counter] = null;
459                 }
460                 treeModel.setRoot(null);
461             }
462             else {
463                 // Find the siblings of path.
464
TreePath parent = path.getParentPath();
465                 MutableTreeNode parentNode = (MutableTreeNode)parent.
466                                 getLastPathComponent();
467                 ArrayList toRemove = new ArrayList();
468                 int depth = parent.getPathCount();
469
470                 // First pass, find paths with a parent TreePath of parent
471
for (int counter = paths.length - 1; counter >= 0; counter--) {
472                     if (paths[counter] != null && paths[counter].
473                               getParentPath().equals(parent)) {
474                         toRemove.add(paths[counter]);
475                         paths[counter] = null;
476                     }
477                 }
478
479                 // Second pass, remove any paths that are descendants of the
480
// paths that are going to be removed. These paths are
481
// implicitly removed as a result of removing the paths in
482
// toRemove
483
int rCount = toRemove.size();
484                 for (int counter = paths.length - 1; counter >= 0; counter--) {
485                     if (paths[counter] != null) {
486                         for (int rCounter = rCount - 1; rCounter >= 0;
487                              rCounter--) {
488                             if (((TreePath)toRemove.get(rCounter)).
489                                            isDescendant(paths[counter])) {
490                                 paths[counter] = null;
491                             }
492                         }
493                     }
494                 }
495
496                 // Sort the siblings based on position in the model
497
if (rCount > 1) {
498                     Collections.sort(toRemove, new PositionComparator());
499                 }
500                 int[] indices = new int[rCount];
501                 Object JavaDoc[] removedNodes = new Object JavaDoc[rCount];
502                 for (int counter = rCount - 1; counter >= 0; counter--) {
503                     removedNodes[counter] = ((TreePath)toRemove.get(counter)).
504                                 getLastPathComponent();
505                     indices[counter] = treeModel.getIndexOfChild
506                                         (parentNode, removedNodes[counter]);
507                     parentNode.remove(indices[counter]);
508                 }
509                 treeModel.nodesWereRemoved(parentNode, indices, removedNodes);
510             }
511         }
512
513         /**
514          * Returns the TreePath with the smallest path count in
515          * <code>paths</code>. Will return null if there is no non-null
516          * TreePath is <code>paths</code>.
517          */

518         private TreePath findShallowestPath(TreePath[] paths) {
519             int shallowest = -1;
520             TreePath shallowestPath = null;
521
522             for (int counter = paths.length - 1; counter >= 0; counter--) {
523                 if (paths[counter] != null) {
524                     if (shallowest != -1) {
525                         if (paths[counter].getPathCount() < shallowest) {
526                             shallowest = paths[counter].getPathCount();
527                             shallowestPath = paths[counter];
528                             if (shallowest == 1) {
529                                 return shallowestPath;
530                             }
531                         }
532                     }
533                     else {
534                         shallowestPath = paths[counter];
535                         shallowest = paths[counter].getPathCount();
536                     }
537                 }
538             }
539             return shallowestPath;
540         }
541
542
543         /**
544          * An Comparator that bases the return value on the index of the
545          * passed in objects in the TreeModel.
546          * <p>
547          * This is actually rather expensive, it would be more efficient
548          * to extract the indices and then do the comparision.
549          */

550         private class PositionComparator implements Comparator {
551             public int compare(Object JavaDoc o1, Object JavaDoc o2) {
552                 TreePath p1 = (TreePath)o1;
553                 int o1Index = treeModel.getIndexOfChild(p1.getParentPath().
554                           getLastPathComponent(), p1.getLastPathComponent());
555                 TreePath p2 = (TreePath)o2;
556                 int o2Index = treeModel.getIndexOfChild(p2.getParentPath().
557                           getLastPathComponent(), p2.getLastPathComponent());
558                 return o1Index - o2Index;
559             }
560
561             public boolean equals(Object JavaDoc obj) {
562                 return super.equals(obj);
563             }
564         }
565
566     } // End of SampleTree.RemoveAction
567

568
569     /**
570       * ShowHandlesChangeListener implements the ChangeListener interface
571       * to toggle the state of showing the handles in the tree.
572       */

573     class ShowHandlesChangeListener extends Object JavaDoc implements ChangeListener
574     {
575     public void stateChanged(ChangeEvent e) {
576         tree.setShowsRootHandles(((JCheckBox)e.getSource()).isSelected());
577     }
578
579     } // End of class SampleTree.ShowHandlesChangeListener
580

581
582     /**
583       * ShowRootChangeListener implements the ChangeListener interface
584       * to toggle the state of showing the root node in the tree.
585       */

586     class ShowRootChangeListener extends Object JavaDoc implements ChangeListener
587     {
588     public void stateChanged(ChangeEvent e) {
589         tree.setRootVisible(((JCheckBox)e.getSource()).isSelected());
590     }
591
592     } // End of class SampleTree.ShowRootChangeListener
593

594
595     /**
596       * TreeEditableChangeListener implements the ChangeListener interface
597       * to toggle between allowing editing and now allowing editing in
598       * the tree.
599       */

600     class TreeEditableChangeListener extends Object JavaDoc implements ChangeListener
601     {
602     public void stateChanged(ChangeEvent e) {
603         tree.setEditable(((JCheckBox)e.getSource()).isSelected());
604     }
605
606     } // End of class SampleTree.TreeEditableChangeListener
607

608
609     static public void main(String JavaDoc args[]) {
610     new SampleTree();
611     }
612
613 }
614
Popular Tags