updates for 0.9.4
[debian/openrocket] / src / net / sf / openrocket / gui / main / ComponentTreeModel.java
1 package net.sf.openrocket.gui.main;
2
3 import java.util.ArrayList;
4 import java.util.Enumeration;
5 import java.util.Iterator;
6 import java.util.List;
7
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;
13
14 import net.sf.openrocket.rocketcomponent.ComponentChangeEvent;
15 import net.sf.openrocket.rocketcomponent.ComponentChangeListener;
16 import net.sf.openrocket.rocketcomponent.RocketComponent;
17
18
19 /**
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).
23  * 
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. 
27  * 
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.
30  * 
31  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
32  */
33
34 public class ComponentTreeModel implements TreeModel, ComponentChangeListener {
35         ArrayList<TreeModelListener> listeners = new ArrayList<TreeModelListener>();
36
37         private final RocketComponent root;
38         private final JTree tree;
39
40         public ComponentTreeModel(RocketComponent root, JTree tree) {
41                 this.root = root;
42                 this.tree = tree;
43                 root.addComponentChangeListener(this);
44         }
45         
46         
47         public Object getChild(Object parent, int index) {
48                 RocketComponent component = (RocketComponent)parent;
49                 
50                 try {
51                         return component.getChild(index);
52                 } catch (IndexOutOfBoundsException e) {
53                         return null;
54                 }
55         }
56         
57
58         public int getChildCount(Object parent) {
59                 RocketComponent c = (RocketComponent)parent;
60
61                 return c.getChildCount();
62         }
63
64         
65         public int getIndexOfChild(Object parent, Object child) {
66                 if (parent==null || child==null)
67                         return -1;
68                 
69                 RocketComponent p = (RocketComponent)parent;
70                 RocketComponent c = (RocketComponent)child;
71                 
72                 return p.getChildPosition(c);
73         }
74
75         public Object getRoot() {
76                 return root;
77         }
78
79         public boolean isLeaf(Object node) {
80                 RocketComponent c = (RocketComponent)node;
81
82                 return (c.getChildCount() == 0);
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         public static TreePath makeTreePath(RocketComponent component) {
190                 RocketComponent c = component;
191         
192                 List<RocketComponent> list = new ArrayList<RocketComponent>();
193                 
194                 while (c != null) {
195                         list.add(0,c);
196                         c = c.getParent();
197                 }
198                 
199                 return new TreePath(list.toArray());
200         }
201         
202 }