1 package net.sf.openrocket.gui.main;
3 import java.util.ArrayList;
4 import java.util.Enumeration;
5 import java.util.Iterator;
8 import javax.swing.JTree;
9 import javax.swing.event.TreeModelEvent;
10 import javax.swing.event.TreeModelListener;
11 import javax.swing.tree.TreeModel;
12 import javax.swing.tree.TreePath;
14 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
15 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
16 import net.sf.openrocket.rocketcomponent.RocketComponent;
20 * A TreeModel that implements viewing of the rocket tree structure.
21 * This transforms the internal view (which has nested Stages) into the user-view
22 * (which has parallel Stages).
24 * To view with the internal structure, switch to using BareComponentTreeModel in
25 * ComponentTree.java. NOTE: This class's makeTreePath will still be used, which
26 * will create illegal paths, which results in problems with selections.
28 * TODO: MEDIUM: When converting a component to another component this model given
29 * outdated information, since it uses the components themselves as the nodes.
31 * @author Sampo Niskanen <sampo.niskanen@iki.fi>
34 public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
35 ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
37 private final RocketComponent root;
38 private final JTree tree;
40 public ComponentTreeModel(RocketComponent root, JTree tree) {
43 root.addComponentChangeListener(this);
47 public Object getChild(Object parent, int index) {
48 RocketComponent component = (RocketComponent)parent;
51 return component.getChild(index);
52 } catch (IndexOutOfBoundsException e) {
58 public int getChildCount(Object parent) {
59 RocketComponent c = (RocketComponent)parent;
61 return c.getChildCount();
65 public int getIndexOfChild(Object parent, Object child) {
66 if (parent==null || child==null)
69 RocketComponent p = (RocketComponent)parent;
70 RocketComponent c = (RocketComponent)child;
72 return p.getChildPosition(c);
75 public Object getRoot() {
79 public boolean isLeaf(Object node) {
80 RocketComponent c = (RocketComponent)node;
82 return (c.getChildCount() == 0);
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()));
189 public static TreePath makeTreePath(RocketComponent component) {
190 RocketComponent c = component;
192 List<RocketComponent> list = new ArrayList<RocketComponent>();
199 return new TreePath(list.toArray());