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);
49 public Object getChild(Object parent, int index) {
50 RocketComponent component = (RocketComponent) parent;
53 return component.getChild(index);
54 } catch (IndexOutOfBoundsException e) {
60 public int getChildCount(Object parent) {
61 RocketComponent c = (RocketComponent) parent;
63 return c.getChildCount();
67 public int getIndexOfChild(Object parent, Object child) {
68 if (parent == null || child == null)
71 RocketComponent p = (RocketComponent) parent;
72 RocketComponent c = (RocketComponent) child;
74 return p.getChildPosition(c);
77 public Object getRoot() {
81 public boolean isLeaf(Object node) {
82 return !((RocketComponent) node).allowsChildren();
85 public void addTreeModelListener(TreeModelListener l) {
89 public void removeTreeModelListener(TreeModelListener l) {
93 private void fireTreeNodeChanged(RocketComponent node) {
94 TreeModelEvent e = new TreeModelEvent(this, makeTreePath(node), null, null);
95 Object[] l = listeners.toArray();
96 for (int i = 0; i < l.length; i++)
97 ((TreeModelListener) l[i]).treeNodesChanged(e);
100 private void fireTreeNodesChanged() {
101 Object[] path = { root };
102 TreeModelEvent e = new TreeModelEvent(this, path);
103 Object[] l = listeners.toArray();
104 for (int i = 0; i < l.length; i++)
105 ((TreeModelListener) l[i]).treeNodesChanged(e);
109 @SuppressWarnings("unused")
110 private void printStructure(TreePath p, int level) {
112 for (int i = 0; i < level; i++)
114 System.out.println(indent + p +
115 ": isVisible:" + tree.isVisible(p) +
116 " isCollapsed:" + tree.isCollapsed(p) +
117 " isExpanded:" + tree.isExpanded(p));
118 Object parent = p.getLastPathComponent();
119 for (int i = 0; i < getChildCount(parent); i++) {
120 Object child = getChild(parent, i);
121 TreePath path = makeTreePath((RocketComponent) child);
122 printStructure(path, level + 1);
127 private void fireTreeStructureChanged(RocketComponent source) {
128 Object[] path = { root };
131 // Get currently expanded path IDs
132 Enumeration<TreePath> enumer = tree.getExpandedDescendants(new TreePath(path));
133 ArrayList<String> expanded = new ArrayList<String>();
134 if (enumer != null) {
135 while (enumer.hasMoreElements()) {
136 TreePath p = enumer.nextElement();
137 expanded.add(((RocketComponent) p.getLastPathComponent()).getID());
141 // Send structure change event
142 TreeModelEvent e = new TreeModelEvent(this, path);
143 Object[] l = listeners.toArray();
144 for (int i = 0; i < l.length; i++)
145 ((TreeModelListener) l[i]).treeStructureChanged(e);
147 // Re-expand the paths
148 for (String id : expanded) {
149 RocketComponent c = root.findComponent(id);
152 tree.expandPath(makeTreePath(c));
154 if (source != null) {
155 TreePath p = makeTreePath(source);
161 public void valueForPathChanged(TreePath path, Object newValue) {
162 System.err.println("ERROR: valueForPathChanged called?!");
166 public void componentChanged(ComponentChangeEvent e) {
167 if (e.isTreeChange() || e.isUndoChange()) {
168 // Tree must be fully updated also in case of an undo change
169 fireTreeStructureChanged((RocketComponent) e.getSource());
170 if (e.isTreeChange() && e.isUndoChange()) {
171 // If the undo has changed the tree structure, some elements may be hidden
173 // TODO: LOW: Could this be performed better?
176 } else if (e.isOtherChange()) {
177 fireTreeNodeChanged(e.getSource());
181 public void expandAll() {
182 Iterator<RocketComponent> iterator = root.deepIterator();
183 while (iterator.hasNext()) {
184 tree.makeVisible(makeTreePath(iterator.next()));
190 * Return the rocket component that a TreePath object is referring to.
192 * @param path the TreePath
193 * @return the RocketComponent the path is referring to.
194 * @throws NullPointerException if path is null
195 * @throws BugException if the path does not refer to a RocketComponent
197 public static RocketComponent componentFromPath(TreePath path) {
198 Object last = path.getLastPathComponent();
199 if (!(last instanceof RocketComponent)) {
200 throw new BugException("Tree path does not refer to a RocketComponent: " + path.toString());
202 return (RocketComponent) last;
207 * Return a TreePath corresponding to the specified rocket component.
209 * @param component the rocket component
210 * @return a TreePath corresponding to this RocketComponent
212 public static TreePath makeTreePath(RocketComponent component) {
213 if (component == null) {
214 throw new NullPointerException();
217 RocketComponent c = component;
219 List<RocketComponent> list = new LinkedList<RocketComponent>();
226 return new TreePath(list.toArray());
231 * Return a string describing the path, using component normal names.
233 * @param treePath the tree path
234 * @return a string representation
236 public static String pathToString(TreePath treePath) {
237 StringBuffer sb = new StringBuffer();
239 for (Object o : treePath.getPath()) {
240 if (sb.length() > 1) {
243 if (o instanceof RocketComponent) {
244 sb.append(((RocketComponent) o).getComponentName());
246 sb.append(o.toString());
250 return sb.toString();