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.
24 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
27 public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
28 ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
30 private final RocketComponent root;
31 private final JTree tree;
33 public ComponentTreeModel(RocketComponent root, JTree tree) {
36 root.addComponentChangeListener(this);
41 public Object getChild(Object parent, int index) {
42 RocketComponent component = (RocketComponent) parent;
45 return component.getChild(index);
46 } catch (IndexOutOfBoundsException e) {
53 public int getChildCount(Object parent) {
54 RocketComponent c = (RocketComponent) parent;
56 return c.getChildCount();
61 public int getIndexOfChild(Object parent, Object child) {
62 if (parent == null || child == null)
65 RocketComponent p = (RocketComponent) parent;
66 RocketComponent c = (RocketComponent) child;
68 return p.getChildPosition(c);
72 public Object getRoot() {
77 public boolean isLeaf(Object node) {
78 return !((RocketComponent) node).allowsChildren();
82 public void addTreeModelListener(TreeModelListener l) {
87 public void removeTreeModelListener(TreeModelListener l) {
91 private void fireTreeNodeChanged(RocketComponent node) {
92 TreeModelEvent e = new TreeModelEvent(this, makeTreePath(node), null, null);
93 Object[] l = listeners.toArray();
94 for (int i = 0; i < l.length; i++)
95 ((TreeModelListener) l[i]).treeNodesChanged(e);
99 private void fireTreeStructureChanged(RocketComponent source) {
100 Object[] path = { root };
103 // Get currently expanded path IDs
104 Enumeration<TreePath> enumer = tree.getExpandedDescendants(new TreePath(path));
105 ArrayList<String> expanded = new ArrayList<String>();
106 if (enumer != null) {
107 while (enumer.hasMoreElements()) {
108 TreePath p = enumer.nextElement();
109 expanded.add(((RocketComponent) p.getLastPathComponent()).getID());
113 // Send structure change event
114 TreeModelEvent e = new TreeModelEvent(this, path);
115 Object[] l = listeners.toArray();
116 for (int i = 0; i < l.length; i++)
117 ((TreeModelListener) l[i]).treeStructureChanged(e);
119 // Re-expand the paths
120 for (String id : expanded) {
121 RocketComponent c = root.findComponent(id);
124 tree.expandPath(makeTreePath(c));
126 if (source != null) {
127 TreePath p = makeTreePath(source);
134 public void valueForPathChanged(TreePath path, Object newValue) {
135 System.err.println("ERROR: valueForPathChanged called?!");
140 public void componentChanged(ComponentChangeEvent e) {
141 if (e.isTreeChange() || e.isUndoChange()) {
142 // Tree must be fully updated also in case of an undo change
143 fireTreeStructureChanged(e.getSource());
144 if (e.isTreeChange() && e.isUndoChange()) {
145 // If the undo has changed the tree structure, some elements may be hidden
147 // TODO: LOW: Could this be performed better?
150 } else if (e.isOtherChange()) {
151 fireTreeNodeChanged(e.getSource());
155 public void expandAll() {
156 Iterator<RocketComponent> iterator = root.iterator(false);
157 while (iterator.hasNext()) {
158 tree.makeVisible(makeTreePath(iterator.next()));
164 * Return the rocket component that a TreePath object is referring to.
166 * @param path the TreePath
167 * @return the RocketComponent the path is referring to.
168 * @throws NullPointerException if path is null
169 * @throws BugException if the path does not refer to a RocketComponent
171 public static RocketComponent componentFromPath(TreePath path) {
172 Object last = path.getLastPathComponent();
173 if (!(last instanceof RocketComponent)) {
174 throw new BugException("Tree path does not refer to a RocketComponent: " + path.toString());
176 return (RocketComponent) last;
181 * Return a TreePath corresponding to the specified rocket component.
183 * @param component the rocket component
184 * @return a TreePath corresponding to this RocketComponent
186 public static TreePath makeTreePath(RocketComponent component) {
187 if (component == null) {
188 throw new NullPointerException();
191 RocketComponent c = component;
193 List<RocketComponent> list = new LinkedList<RocketComponent>();
200 return new TreePath(list.toArray());
205 * Return a string describing the path, using component normal names.
207 * @param treePath the tree path
208 * @return a string representation
210 public static String pathToString(TreePath treePath) {
211 StringBuffer sb = new StringBuffer();
213 for (Object o : treePath.getPath()) {
214 if (sb.length() > 1) {
217 if (o instanceof RocketComponent) {
218 sb.append(((RocketComponent) o).getComponentName());
220 sb.append(o.toString());
224 return sb.toString();