]> git.gag.com Git - debian/openrocket/blob - src/net/sf/openrocket/gui/main/componenttree/ComponentTreeModel.java
f35222c132b26567f52e0e160c592964fc97d6a6
[debian/openrocket] / src / net / sf / openrocket / gui / main / componenttree / ComponentTreeModel.java
1 package net.sf.openrocket.gui.main.componenttree;
2
3 import java.util.ArrayList;
4 import java.util.Enumeration;
5 import java.util.Iterator;
6 import java.util.LinkedList;
7 import java.util.List;
8
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;
14
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;
19
20
21 /**
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).
25  * 
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. 
29  * 
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.
32  * 
33  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
34  */
35
36 public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
37         ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
38         
39         private final RocketComponent root;
40         private final JTree tree;
41         
42         public ComponentTreeModel(RocketComponent root, JTree tree) {
43                 this.root = root;
44                 this.tree = tree;
45                 root.addComponentChangeListener(this);
46         }
47         
48         
49         public Object getChild(Object parent, int index) {
50                 RocketComponent component = (RocketComponent) parent;
51                 
52                 try {
53                         return component.getChild(index);
54                 } catch (IndexOutOfBoundsException e) {
55                         return null;
56                 }
57         }
58         
59         
60         public int getChildCount(Object parent) {
61                 RocketComponent c = (RocketComponent) parent;
62                 
63                 return c.getChildCount();
64         }
65         
66         
67         public int getIndexOfChild(Object parent, Object child) {
68                 if (parent == null || child == null)
69                         return -1;
70                 
71                 RocketComponent p = (RocketComponent) parent;
72                 RocketComponent c = (RocketComponent) child;
73                 
74                 return p.getChildPosition(c);
75         }
76         
77         public Object getRoot() {
78                 return root;
79         }
80         
81         public boolean isLeaf(Object node) {
82                 return !((RocketComponent) node).allowsChildren();
83         }
84         
85         public void addTreeModelListener(TreeModelListener l) {
86                 listeners.add(l);
87         }
88         
89         public void removeTreeModelListener(TreeModelListener l) {
90                 listeners.remove(l);
91         }
92         
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);
98         }
99         
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);
106         }
107         
108         
109         @SuppressWarnings("unused")
110         private void printStructure(TreePath p, int level) {
111                 String indent = "";
112                 for (int i = 0; i < level; i++)
113                         indent += "  ";
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);
123                 }
124         }
125         
126         
127         private void fireTreeStructureChanged(RocketComponent source) {
128                 Object[] path = { root };
129                 
130
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());
138                         }
139                 }
140                 
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);
146                 
147                 // Re-expand the paths
148                 for (String id : expanded) {
149                         RocketComponent c = root.findComponent(id);
150                         if (c == null)
151                                 continue;
152                         tree.expandPath(makeTreePath(c));
153                 }
154                 if (source != null) {
155                         TreePath p = makeTreePath(source);
156                         tree.makeVisible(p);
157                         tree.expandPath(p);
158                 }
159         }
160         
161         public void valueForPathChanged(TreePath path, Object newValue) {
162                 System.err.println("ERROR: valueForPathChanged called?!");
163         }
164         
165         
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
172                                 // unnecessarily
173                                 // TODO: LOW: Could this be performed better?
174                                 expandAll();
175                         }
176                 } else if (e.isOtherChange()) {
177                         fireTreeNodeChanged(e.getSource());
178                 }
179         }
180         
181         public void expandAll() {
182                 Iterator<RocketComponent> iterator = root.deepIterator();
183                 while (iterator.hasNext()) {
184                         tree.makeVisible(makeTreePath(iterator.next()));
185                 }
186         }
187         
188         
189         /**
190          * Return the rocket component that a TreePath object is referring to.
191          * 
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
196          */
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());
201                 }
202                 return (RocketComponent) last;
203         }
204         
205         
206         /**
207          * Return a TreePath corresponding to the specified rocket component.
208          * 
209          * @param component             the rocket component
210          * @return                              a TreePath corresponding to this RocketComponent
211          */
212         public static TreePath makeTreePath(RocketComponent component) {
213                 if (component == null) {
214                         throw new NullPointerException();
215                 }
216                 
217                 RocketComponent c = component;
218                 
219                 List<RocketComponent> list = new LinkedList<RocketComponent>();
220                 
221                 while (c != null) {
222                         list.add(0, c);
223                         c = c.getParent();
224                 }
225                 
226                 return new TreePath(list.toArray());
227         }
228         
229         
230         /**
231          * Return a string describing the path, using component normal names.
232          * 
233          * @param treePath      the tree path
234          * @return                      a string representation
235          */
236         public static String pathToString(TreePath treePath) {
237                 StringBuffer sb = new StringBuffer();
238                 sb.append("[");
239                 for (Object o : treePath.getPath()) {
240                         if (sb.length() > 1) {
241                                 sb.append("; ");
242                         }
243                         if (o instanceof RocketComponent) {
244                                 sb.append(((RocketComponent) o).getComponentName());
245                         } else {
246                                 sb.append(o.toString());
247                         }
248                 }
249                 sb.append("]");
250                 return sb.toString();
251         }
252 }