2 * CheckTreeSelectionModel.java
4 package net.sf.openrocket.gui.print.components;
6 import javax.swing.tree.DefaultTreeSelectionModel;
7 import javax.swing.tree.TreeModel;
8 import javax.swing.tree.TreePath;
9 import javax.swing.tree.TreeSelectionModel;
10 import java.util.ArrayList;
11 import java.util.Stack;
14 * This class implements the selection model for the checkbox tree. This specifically is used to keep
15 * track of the TreePaths that have a selected CheckBox.
17 public class CheckTreeSelectionModel extends DefaultTreeSelectionModel {
22 private TreeModel model;
27 * @param theModel the model in use for the tree
29 public CheckTreeSelectionModel (TreeModel theModel) {
31 setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION);
35 * Tests whether there is any selected node in the subtree of given path.
37 * @param path the path to walk
39 * @return true if any item in the path or its descendants are selected
41 public boolean isPartiallySelected (TreePath path) {
42 if (isPathSelected(path, true)) {
45 TreePath[] selectionPaths = getSelectionPaths();
46 if (selectionPaths == null) {
49 for (TreePath selectionPath : selectionPaths) {
50 if (isDescendant(selectionPath, path)) {
58 * Tells whether given path is selected. If dig is true, then a path is assumed to be selected, if one of its
59 * ancestor is selected.
61 * @param path the path to interrogate
62 * @param dig if true then check if an ancestor is selected
64 * @return true if the path is selected
66 public boolean isPathSelected (TreePath path, boolean dig) {
68 return super.isPathSelected(path);
70 while (path != null && !super.isPathSelected(path)) {
71 path = path.getParentPath();
77 * Determines if path1 is a descendant of path2.
79 * @param path1 descendant?
80 * @param path2 ancestor?
82 * @return true if path1 is a descendant of path2
84 private boolean isDescendant (TreePath path1, TreePath path2) {
85 Object obj1[] = path1.getPath();
86 Object obj2[] = path2.getPath();
87 for (int i = 0; i < obj2.length; i++) {
88 if (i < obj1.length) {
89 if (obj1[i] != obj2[i]) {
102 * Unsupported exception.
104 * @param pPaths an array of paths
106 public void setSelectionPaths (TreePath[] pPaths) {
107 TreePath selected[] = getSelectionPaths();
108 for (TreePath aSelected : selected) {
109 removeSelectionPath(aSelected);
111 for (TreePath pPath : pPaths) {
112 addSelectionPath(pPath);
117 * Add a set of TreePath nodes to the selection model.
119 * @param paths an array of tree path nodes
121 public void addSelectionPaths (TreePath[] paths) {
122 // deselect all descendants of paths[]
123 for (TreePath path : paths) {
124 TreePath[] selectionPaths = getSelectionPaths();
125 if (selectionPaths == null) {
128 ArrayList<TreePath> toBeRemoved = new ArrayList<TreePath>();
129 for (TreePath selectionPath : selectionPaths) {
130 if (isDescendant(selectionPath, path)) {
131 toBeRemoved.add(selectionPath);
134 super.removeSelectionPaths(toBeRemoved.toArray(new TreePath[toBeRemoved.size()]));
137 // if all siblings are selected then deselect them and select parent recursively
138 // otherwise just select that path.
139 for (TreePath path : paths) {
140 TreePath temp = null;
141 while (areSiblingsSelected(path)) {
143 if (path.getParentPath() == null) {
146 path = path.getParentPath();
149 if (temp.getParentPath() != null) {
150 addSelectionPath(temp.getParentPath());
153 if (!isSelectionEmpty()) {
154 removeSelectionPaths(getSelectionPaths());
156 super.addSelectionPaths(new TreePath[]{temp});
160 super.addSelectionPaths(new TreePath[]{path});
166 * Tells whether all siblings of given path are selected.
168 * @param path the tree path node
170 * @return true if all sibling nodes are selected
172 private boolean areSiblingsSelected (TreePath path) {
173 TreePath parent = path.getParentPath();
174 if (parent == null) {
177 Object node = path.getLastPathComponent();
178 Object parentNode = parent.getLastPathComponent();
180 int childCount = model.getChildCount(parentNode);
181 for (int i = 0; i < childCount; i++) {
182 Object childNode = model.getChild(parentNode, i);
183 if (childNode == node) {
186 if (!isPathSelected(parent.pathByAddingChild(childNode))) {
194 * Remove paths from the selection model.
196 * @param paths the array of path nodes
198 public void removeSelectionPaths (TreePath[] paths) {
199 for (TreePath path : paths) {
200 if (path.getPathCount() == 1) {
201 super.removeSelectionPaths(new TreePath[]{path});
204 toggleRemoveSelection(path);
210 * If any ancestor node of given path is selected then deselect it and selection all its descendants except given
211 * path and descendants. otherwise just deselect the given path.
213 * @param path the tree path node
215 private void toggleRemoveSelection (TreePath path) {
216 Stack<TreePath> stack = new Stack<TreePath>();
217 TreePath parent = path.getParentPath();
218 while (parent != null && !isPathSelected(parent)) {
220 parent = parent.getParentPath();
222 if (parent != null) {
226 super.removeSelectionPaths(new TreePath[]{path});
230 while (!stack.isEmpty()) {
231 TreePath temp = stack.pop();
232 TreePath peekPath = stack.isEmpty() ? path : stack.peek();
233 Object node = temp.getLastPathComponent();
234 Object peekNode = peekPath.getLastPathComponent();
235 int childCount = model.getChildCount(node);
236 for (int i = 0; i < childCount; i++) {
237 Object childNode = model.getChild(node, i);
238 if (childNode != peekNode) {
239 super.addSelectionPaths(new TreePath[]{temp.pathByAddingChild(childNode)});
243 super.removeSelectionPaths(new TreePath[]{parent});