create changelog entry
[debian/openrocket] / core / src / net / sf / openrocket / gui / print / components / RocketPrintTree.java
1 /*
2  * RocketPrintTree.java
3  */
4 package net.sf.openrocket.gui.print.components;
5
6 import net.sf.openrocket.gui.print.OpenRocketPrintable;
7 import net.sf.openrocket.gui.print.PrintableContext;
8 import net.sf.openrocket.rocketcomponent.RocketComponent;
9 import net.sf.openrocket.rocketcomponent.Stage;
10
11 import javax.swing.*;
12 import javax.swing.event.TreeExpansionEvent;
13 import javax.swing.event.TreeWillExpandListener;
14 import javax.swing.tree.DefaultMutableTreeNode;
15 import javax.swing.tree.ExpandVetoException;
16 import javax.swing.tree.TreePath;
17 import javax.swing.tree.TreeSelectionModel;
18 import java.util.ArrayList;
19 import java.util.Iterator;
20 import java.util.List;
21 import java.util.Vector;
22
23 /**
24  * A specialized JTree for displaying various rocket items that can be printed.
25  */
26 public class RocketPrintTree extends JTree {
27         
28         /**
29          * All check boxes are initially set to true (selected).
30          */
31         public static final boolean INITIAL_CHECKBOX_SELECTED = true;
32         
33         /**
34          * The selection model that tracks the check box state.
35          */
36         private TreeSelectionModel theCheckBoxSelectionModel;
37         
38         /**
39          * Constructor.
40          *
41          * @param root the vector of check box nodes (rows) to place into the tree
42          */
43         private RocketPrintTree(Vector root) {
44                 super(root);
45                 
46                 //Remove the little down and sideways arrows.  These are not needed because the tree expansion is fixed.
47                 ((javax.swing.plaf.basic.BasicTreeUI) this.getUI()).
48                                 setExpandedIcon(null);
49                 ((javax.swing.plaf.basic.BasicTreeUI) this.getUI()).
50                                 setCollapsedIcon(null);
51         }
52         
53         /**
54          * Factory method to create a specialized JTree.  This version is for rocket's that have more than one stage.
55          *
56          * @param rocketName the name of the rocket
57          * @param stages     the array of all stages
58          *
59          * @return an instance of JTree
60          */
61         public static RocketPrintTree create(String rocketName, List<RocketComponent> stages) {
62                 Vector root = new Vector();
63                 Vector toAddTo = root;
64                 
65                 if (stages != null) {
66                         if (stages.size() > 1) {
67                                 final Vector parent = new NamedVector(rocketName != null ? rocketName : "Rocket");
68                                 
69                                 root.add(parent);
70                                 toAddTo = parent;
71                         }
72                         for (RocketComponent stage : stages) {
73                                 if (stage instanceof Stage) {
74                                         toAddTo.add(createNamedVector(stage.getName(), createPrintTreeNode(true), stage.getStageNumber()));
75                                 }
76                         }
77                 }
78
79         List<OpenRocketPrintable> unstaged = OpenRocketPrintable.getUnstaged();
80         for (int i = 0; i < unstaged.size(); i++) {
81                     toAddTo.add(new CheckBoxNode(unstaged.get(i).getDescription(),
82                                                                                 INITIAL_CHECKBOX_SELECTED));
83         }
84
85                 RocketPrintTree tree = new RocketPrintTree(root);
86                 
87                 tree.addTreeWillExpandListener
88                                 (new TreeWillExpandListener() {
89                                         @Override
90                                         public void treeWillExpand(TreeExpansionEvent e) {
91                                         }
92                                         
93                                         @Override
94                                         public void treeWillCollapse(TreeExpansionEvent e)
95                                                         throws ExpandVetoException {
96                                                 throw new ExpandVetoException(e, "you can't collapse this JTree");
97                                         }
98                                 });
99                 
100                 return tree;
101         }
102         
103         /**
104          * Factory method to create a specialized JTree.  This version is for a rocket with only one stage.
105          *
106          * @param rocketName the name of the rocket
107          *
108          * @return an instance of JTree
109          */
110         public static RocketPrintTree create(String rocketName) {
111                 Vector root = new Vector();
112                 root.add(new NamedVector(rocketName != null ? rocketName : "Rocket", createPrintTreeNode(false)));
113                 
114                 RocketPrintTree tree = new RocketPrintTree(root);
115                 
116                 tree.addTreeWillExpandListener
117                                 (new TreeWillExpandListener() {
118                                         @Override
119                                         public void treeWillExpand(TreeExpansionEvent e) {
120                                         }
121                                         
122                                         @Override
123                                         public void treeWillCollapse(TreeExpansionEvent e)
124                                                         throws ExpandVetoException {
125                                                 throw new ExpandVetoException(e, "you can't collapse this JTree");
126                                         }
127                                 });
128                 
129                 return tree;
130         }
131         
132         /**
133          * This tree needs to have access both to the normal selection model (for the textual row) which is managed by the
134          * superclass, as well as the selection model for the check boxes.  This mutator method allows an external class to
135          * set the model back onto this class.  Because of some unfortunate circular dependencies this cannot be set at
136          * construction.
137          * <p/>
138          * TODO: Ensure these circular references get cleaned up properly at dialog disposal so everything can be GC'd.
139          *
140          * @param checkBoxSelectionModel the selection model used to keep track of the check box state
141          */
142         public void setCheckBoxSelectionModel(TreeSelectionModel checkBoxSelectionModel) {
143                 theCheckBoxSelectionModel = checkBoxSelectionModel;
144         }
145         
146         /**
147          * Add a selection path to the internal check box selection model.  The normal JTree selection model is unaffected.
148          * This has the effect of "selecting" the check box, but not highlighting the row.
149          *
150          * @param path the path (row)
151          */
152         @Override
153         public void addSelectionPath(TreePath path) {
154                 theCheckBoxSelectionModel.addSelectionPath(path);
155         }
156         
157         /**
158          * Helper to construct a named vector.
159          *
160          * @param name  the name of the vector
161          * @param nodes the array of nodes to put into the vector
162          * @param stage the stage number
163          *
164          * @return a NamedVector suitable for adding to a JTree
165          */
166         private static Vector createNamedVector(String name, CheckBoxNode[] nodes, int stage) {
167                 return new NamedVector(name, nodes, stage);
168         }
169         
170         /**
171          * Helper to construct the set of check box rows for each stage.
172          *
173          * @param onlyStageSpecific  if true then only stage specific OpenRocketPrintable rows are represented (in this part 
174          * of the tree).
175          * 
176          * @return an array of CheckBoxNode
177          */
178         private static CheckBoxNode[] createPrintTreeNode(boolean onlyStageSpecific) {
179                 List<CheckBoxNode> nodes = new ArrayList<CheckBoxNode>();
180                 OpenRocketPrintable[] printables = OpenRocketPrintable.values();
181                 for (OpenRocketPrintable openRocketPrintable : printables) {
182                         if (!onlyStageSpecific || openRocketPrintable.isStageSpecific()) {
183                                 nodes.add(new CheckBoxNode(openRocketPrintable.getDescription(),
184                                                                                         INITIAL_CHECKBOX_SELECTED));
185                         }
186                 }
187                 return nodes.toArray(new CheckBoxNode[nodes.size()]);
188         }
189         
190         /**
191          * Get the set of items to be printed, as selected by the user.
192          * 
193          * @return the things to be printed, returned as an Iterator<PrintableContext>
194          */
195         public Iterator<PrintableContext> getToBePrinted() {
196                 final DefaultMutableTreeNode mutableTreeNode = (DefaultMutableTreeNode) getModel().getRoot();
197                 PrintableContext pc = new PrintableContext();
198                 add(pc, mutableTreeNode);
199                 return pc.iterator();
200         }
201         
202         /**
203          * Walk a tree, finding everything that has been selected and aggregating it into something that can be iterated upon
204          * This method is recursive.
205          * 
206          * @param pc                  the printable context that aggregates the choices into an iterator
207          * @param theMutableTreeNode  the root node
208          */
209         private void add(final PrintableContext pc, final DefaultMutableTreeNode theMutableTreeNode) {
210                 int children = theMutableTreeNode.getChildCount();
211                 for (int x = 0; x < children; x++) {
212                         
213                         final DefaultMutableTreeNode at = (DefaultMutableTreeNode) theMutableTreeNode.getChildAt(x);
214                         if (at.getUserObject() instanceof CheckBoxNode) {
215                                 CheckBoxNode cbn = (CheckBoxNode) at.getUserObject();
216                                 if (cbn.isSelected()) {
217                                         final OpenRocketPrintable printable = OpenRocketPrintable.findByDescription(cbn.getText());
218                                         pc.add(
219                                                         printable.isStageSpecific() ? ((NamedVector) theMutableTreeNode.getUserObject())
220                                                                         .getStage() : null,
221                                                         printable);
222                                 }
223                         }
224                         add(pc, at);
225                 }
226         }
227         
228 }
229
230 /**
231  * JTree's work off of Vector's (unfortunately).  This class is tailored for use with check boxes in the JTree.
232  */
233 class NamedVector extends Vector<CheckBoxNode> {
234         String name;
235         
236         int stageNumber;
237         
238         public NamedVector(String theName) {
239                 name = theName;
240         }
241         
242         public NamedVector(String theName, CheckBoxNode elements[], int stage) {
243                 this(theName, elements);
244                 stageNumber = stage;
245         }
246         
247         public NamedVector(String theName, CheckBoxNode elements[]) {
248                 name = theName;
249                 for (int i = 0, n = elements.length; i < n; i++) {
250                         add(elements[i]);
251                 }
252         }
253         
254         @Override
255         public String toString() {
256                 return name;
257         }
258         
259         public int getStage() {
260                 return stageNumber;
261         }
262 }