1 package net.sf.openrocket.gui.main.componenttree;
3 import java.util.ArrayList;
4 import java.util.Enumeration;
5 import java.util.Iterator;
6 import java.util.LinkedList;
9 import javax.swing.JTree;
10 import javax.swing.event.TreeModelEvent;
11 import javax.swing.event.TreeModelListener;
12 import javax.swing.tree.TreeModel;
13 import javax.swing.tree.TreePath;
15 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
16 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
17 import net.sf.openrocket.rocketcomponent.RocketComponent;
18 import net.sf.openrocket.util.BugException;
22 * A TreeModel that implements viewing of the rocket tree structure.
23 * This transforms the internal view (which has nested Stages) into the user-view
24 * (which has parallel Stages).
26 * To view with the internal structure, switch to using BareComponentTreeModel in
27 * ComponentTree.java. NOTE: This class's makeTreePath will still be used, which
28 * will create illegal paths, which results in problems with selections.
30 * TODO: MEDIUM: When converting a component to another component this model given
31 * outdated information, since it uses the components themselves as the nodes.
33 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
36 public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
37 ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
39 private final RocketComponent root;
40 private final JTree tree;
42 public ComponentTreeModel(RocketComponent root, JTree tree) {
45 root.addComponentChangeListener(this);
50 public Object getChild(Object parent, int index) {
51 RocketComponent component = (RocketComponent) parent;
54 return component.getChild(index);
55 } catch (IndexOutOfBoundsException e) {
62 public int getChildCount(Object parent) {
63 RocketComponent c = (RocketComponent) parent;
65 return c.getChildCount();
70 public int getIndexOfChild(Object parent, Object child) {
71 if (parent == null || child == null)
74 RocketComponent p = (RocketComponent) parent;
75 RocketComponent c = (RocketComponent) child;
77 return p.getChildPosition(c);
81 public Object getRoot() {
86 public boolean isLeaf(Object node) {
87 return !((RocketComponent) node).allowsChildren();
91 public void addTreeModelListener(TreeModelListener l) {
96 public void removeTreeModelListener(TreeModelListener l) {
100 private void fireTreeNodeChanged(RocketComponent node) {
101 TreeModelEvent e = new TreeModelEvent(this, makeTreePath(node), null, null);
102 Object[] l = listeners.toArray();
103 for (int i = 0; i < l.length; i++)
104 ((TreeModelListener) l[i]).treeNodesChanged(e);
107 private void fireTreeNodesChanged() {
108 Object[] path = { root };
109 TreeModelEvent e = new TreeModelEvent(this, path);
110 Object[] l = listeners.toArray();
111 for (int i = 0; i < l.length; i++)
112 ((TreeModelListener) l[i]).treeNodesChanged(e);
116 @SuppressWarnings("unused")
117 private void printStructure(TreePath p, int level) {
119 for (int i = 0; i < level; i++)
121 System.out.println(indent + p +
122 ": isVisible:" + tree.isVisible(p) +
123 " isCollapsed:" + tree.isCollapsed(p) +
124 " isExpanded:" + tree.isExpanded(p));
125 Object parent = p.getLastPathComponent();
126 for (int i = 0; i < getChildCount(parent); i++) {
127 Object child = getChild(parent, i);
128 TreePath path = makeTreePath((RocketComponent) child);
129 printStructure(path, level + 1);
134 private void fireTreeStructureChanged(RocketComponent source) {
135 Object[] path = { root };
138 // Get currently expanded path IDs
139 Enumeration<TreePath> enumer = tree.getExpandedDescendants(new TreePath(path));
140 ArrayList<String> expanded = new ArrayList<String>();
141 if (enumer != null) {
142 while (enumer.hasMoreElements()) {
143 TreePath p = enumer.nextElement();
144 expanded.add(((RocketComponent) p.getLastPathComponent()).getID());
148 // Send structure change event
149 TreeModelEvent e = new TreeModelEvent(this, path);
150 Object[] l = listeners.toArray();
151 for (int i = 0; i < l.length; i++)
152 ((TreeModelListener) l[i]).treeStructureChanged(e);
154 // Re-expand the paths
155 for (String id : expanded) {
156 RocketComponent c = root.findComponent(id);
159 tree.expandPath(makeTreePath(c));
161 if (source != null) {
162 TreePath p = makeTreePath(source);
169 public void valueForPathChanged(TreePath path, Object newValue) {
170 System.err.println("ERROR: valueForPathChanged called?!");
175 public void componentChanged(ComponentChangeEvent e) {
176 if (e.isTreeChange() || e.isUndoChange()) {
177 // Tree must be fully updated also in case of an undo change
178 fireTreeStructureChanged(e.getSource());
179 if (e.isTreeChange() && e.isUndoChange()) {
180 // If the undo has changed the tree structure, some elements may be hidden
182 // TODO: LOW: Could this be performed better?
185 } else if (e.isOtherChange()) {
186 fireTreeNodeChanged(e.getSource());
190 public void expandAll() {
191 Iterator<RocketComponent> iterator = root.iterator(false);
192 while (iterator.hasNext()) {
193 tree.makeVisible(makeTreePath(iterator.next()));
199 * Return the rocket component that a TreePath object is referring to.
201 * @param path the TreePath
202 * @return the RocketComponent the path is referring to.
203 * @throws NullPointerException if path is null
204 * @throws BugException if the path does not refer to a RocketComponent
206 public static RocketComponent componentFromPath(TreePath path) {
207 Object last = path.getLastPathComponent();
208 if (!(last instanceof RocketComponent)) {
209 throw new BugException("Tree path does not refer to a RocketComponent: " + path.toString());
211 return (RocketComponent) last;
216 * Return a TreePath corresponding to the specified rocket component.
218 * @param component the rocket component
219 * @return a TreePath corresponding to this RocketComponent
221 public static TreePath makeTreePath(RocketComponent component) {
222 if (component == null) {
223 throw new NullPointerException();
226 RocketComponent c = component;
228 List<RocketComponent> list = new LinkedList<RocketComponent>();
235 return new TreePath(list.toArray());
240 * Return a string describing the path, using component normal names.
242 * @param treePath the tree path
243 * @return a string representation
245 public static String pathToString(TreePath treePath) {
246 StringBuffer sb = new StringBuffer();
248 for (Object o : treePath.getPath()) {
249 if (sb.length() > 1) {
252 if (o instanceof RocketComponent) {
253 sb.append(((RocketComponent) o).getComponentName());
255 sb.append(o.toString());
259 return sb.toString();