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 fireTreeNodesChanged() {
94 Object[] path = { root };
95 TreeModelEvent e = new TreeModelEvent(this,path);
96 Object[] l = listeners.toArray();
97 for (int i=0; i<l.length; i++)
98 ((TreeModelListener)l[i]).treeNodesChanged(e);
102 @SuppressWarnings("unused")
103 private void printStructure(TreePath p, int level) {
105 for (int i=0; i<level; i++)
107 System.out.println(indent+p+
108 ": isVisible:"+tree.isVisible(p)+
109 " isCollapsed:"+tree.isCollapsed(p)+
110 " isExpanded:"+tree.isExpanded(p));
111 Object parent = p.getLastPathComponent();
112 for (int i=0; i<getChildCount(parent); i++) {
113 Object child = getChild(parent,i);
114 TreePath path = makeTreePath((RocketComponent)child);
115 printStructure(path,level+1);
120 private void fireTreeStructureChanged(RocketComponent source) {
121 Object[] path = { root };
124 // Get currently expanded path IDs
125 Enumeration<TreePath> enumer = tree.getExpandedDescendants(new TreePath(path));
126 ArrayList<String> expanded = new ArrayList<String>();
127 if (enumer != null) {
128 while (enumer.hasMoreElements()) {
129 TreePath p = enumer.nextElement();
130 expanded.add(((RocketComponent)p.getLastPathComponent()).getID());
134 // Send structure change event
135 TreeModelEvent e = new TreeModelEvent(this,path);
136 Object[] l = listeners.toArray();
137 for (int i=0; i<l.length; i++)
138 ((TreeModelListener)l[i]).treeStructureChanged(e);
140 // Re-expand the paths
141 for (String id: expanded) {
142 RocketComponent c = root.findComponent(id);
145 tree.expandPath(makeTreePath(c));
147 if (source != null) {
148 TreePath p = makeTreePath(source);
154 public void valueForPathChanged(TreePath path, Object newValue) {
155 System.err.println("ERROR: valueForPathChanged called?!");
159 public void componentChanged(ComponentChangeEvent e) {
160 if (e.isTreeChange() || e.isUndoChange()) {
161 // Tree must be fully updated also in case of an undo change
162 fireTreeStructureChanged((RocketComponent)e.getSource());
163 if (e.isTreeChange() && e.isUndoChange()) {
164 // If the undo has changed the tree structure, some elements may be hidden
166 // TODO: LOW: Could this be performed better?
169 } else if (e.isOtherChange()) {
170 fireTreeNodesChanged();
174 public void expandAll() {
175 Iterator<RocketComponent> iterator = root.deepIterator();
176 while (iterator.hasNext()) {
177 tree.makeVisible(makeTreePath(iterator.next()));
182 public static TreePath makeTreePath(RocketComponent component) {
183 RocketComponent c = component;
185 List<RocketComponent> list = new ArrayList<RocketComponent>();
192 return new TreePath(list.toArray());