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