/*
* RocketPrintTree.java
*/
package net.sf.openrocket.gui.print.components;
import net.sf.openrocket.gui.print.OpenRocketPrintable;
import net.sf.openrocket.gui.print.PrintableContext;
import net.sf.openrocket.rocketcomponent.RocketComponent;
import net.sf.openrocket.rocketcomponent.Stage;
import javax.swing.*;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeWillExpandListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.ExpandVetoException;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
/**
* A specialized JTree for displaying various rocket items that can be printed.
*/
public class RocketPrintTree extends JTree {
/**
* All check boxes are initially set to true (selected).
*/
public static final boolean INITIAL_CHECKBOX_SELECTED = true;
/**
* The selection model that tracks the check box state.
*/
private TreeSelectionModel theCheckBoxSelectionModel;
/**
* Constructor.
*
* @param root the vector of check box nodes (rows) to place into the tree
*/
private RocketPrintTree(Vector root) {
super(root);
//Remove the little down and sideways arrows. These are not needed because the tree expansion is fixed.
((javax.swing.plaf.basic.BasicTreeUI) this.getUI()).
setExpandedIcon(null);
((javax.swing.plaf.basic.BasicTreeUI) this.getUI()).
setCollapsedIcon(null);
}
/**
* Factory method to create a specialized JTree. This version is for rocket's that have more than one stage.
*
* @param rocketName the name of the rocket
* @param stages the array of all stages
*
* @return an instance of JTree
*/
public static RocketPrintTree create(String rocketName, List<RocketComponent> stages) {
Vector root = new Vector();
Vector toAddTo = root;
if (stages != null) {
if (stages.size() > 1) {
final Vector parent = new NamedVector(rocketName != null ? rocketName : "Rocket");
root.add(parent);
toAddTo = parent;
}
for (RocketComponent stage : stages) {
if (stage instanceof Stage) {
toAddTo.add(createNamedVector(stage.getName(), createPrintTreeNode(true), stage.getStageNumber()));
}
}
}
List<OpenRocketPrintable> unstaged = OpenRocketPrintable.getUnstaged();
for (int i = 0; i < unstaged.size(); i++) {
toAddTo.add(new CheckBoxNode(unstaged.get(i).getDescription(),
INITIAL_CHECKBOX_SELECTED));
}
RocketPrintTree tree = new RocketPrintTree(root);
tree.addTreeWillExpandListener
(new TreeWillExpandListener() {
@Override
public void treeWillExpand(TreeExpansionEvent e) {
}
@Override
public void treeWillCollapse(TreeExpansionEvent e)
throws ExpandVetoException {
throw new ExpandVetoException(e, "you can't collapse this JTree");
}
});
return tree;
}
/**
* Factory method to create a specialized JTree. This version is for a rocket with only one stage.
*
* @param rocketName the name of the rocket
*
* @return an instance of JTree
*/
public static RocketPrintTree create(String rocketName) {
Vector root = new Vector();
root.add(new NamedVector(rocketName != null ? rocketName : "Rocket", createPrintTreeNode(false)));
RocketPrintTree tree = new RocketPrintTree(root);
tree.addTreeWillExpandListener
(new TreeWillExpandListener() {
@Override
public void treeWillExpand(TreeExpansionEvent e) {
}
@Override
public void treeWillCollapse(TreeExpansionEvent e)
throws ExpandVetoException {
throw new ExpandVetoException(e, "you can't collapse this JTree");
}
});
return tree;
}
/**
* This tree needs to have access both to the normal selection model (for the textual row) which is managed by the
* superclass, as well as the selection model for the check boxes. This mutator method allows an external class to
* set the model back onto this class. Because of some unfortunate circular dependencies this cannot be set at
* construction.
* <p/>
* TODO: Ensure these circular references get cleaned up properly at dialog disposal so everything can be GC'd.
*
* @param checkBoxSelectionModel the selection model used to keep track of the check box state
*/
public void setCheckBoxSelectionModel(TreeSelectionModel checkBoxSelectionModel) {
theCheckBoxSelectionModel = checkBoxSelectionModel;
}
/**
* Add a selection path to the internal check box selection model. The normal JTree selection model is unaffected.
* This has the effect of "selecting" the check box, but not highlighting the row.
*
* @param path the path (row)
*/
@Override
public void addSelectionPath(TreePath path) {
theCheckBoxSelectionModel.addSelectionPath(path);
}
/**
* Helper to construct a named vector.
*
* @param name the name of the vector
* @param nodes the array of nodes to put into the vector
* @param stage the stage number
*
* @return a NamedVector suitable for adding to a JTree
*/
private static Vector createNamedVector(String name, CheckBoxNode[] nodes, int stage) {
return new NamedVector(name, nodes, stage);
}
/**
* Helper to construct the set of check box rows for each stage.
*
* @param onlyStageSpecific if true then only stage specific OpenRocketPrintable rows are represented (in this part
* of the tree).
*
* @return an array of CheckBoxNode
*/
private static CheckBoxNode[] createPrintTreeNode(boolean onlyStageSpecific) {
List<CheckBoxNode> nodes = new ArrayList<CheckBoxNode>();
OpenRocketPrintable[] printables = OpenRocketPrintable.values();
for (OpenRocketPrintable openRocketPrintable : printables) {
if (!onlyStageSpecific || openRocketPrintable.isStageSpecific()) {
nodes.add(new CheckBoxNode(openRocketPrintable.getDescription(),
INITIAL_CHECKBOX_SELECTED));
}
}
return nodes.toArray(new CheckBoxNode[nodes.size()]);
}
/**
* Get the set of items to be printed, as selected by the user.
*
* @return the things to be printed, returned as an Iterator<PrintableContext>
*/
public Iterator<PrintableContext> getToBePrinted() {
final DefaultMutableTreeNode mutableTreeNode = (DefaultMutableTreeNode) getModel().getRoot();
PrintableContext pc = new PrintableContext();
add(pc, mutableTreeNode);
return pc.iterator();
}
/**
* Walk a tree, finding everything that has been selected and aggregating it into something that can be iterated upon
* This method is recursive.
*
* @param pc the printable context that aggregates the choices into an iterator
* @param theMutableTreeNode the root node
*/
private void add(final PrintableContext pc, final DefaultMutableTreeNode theMutableTreeNode) {
int children = theMutableTreeNode.getChildCount();
for (int x = 0; x < children; x++) {
final DefaultMutableTreeNode at = (DefaultMutableTreeNode) theMutableTreeNode.getChildAt(x);
if (at.getUserObject() instanceof CheckBoxNode) {
CheckBoxNode cbn = (CheckBoxNode) at.getUserObject();
if (cbn.isSelected()) {
final OpenRocketPrintable printable = OpenRocketPrintable.findByDescription(cbn.getText());
pc.add(
printable.isStageSpecific() ? ((NamedVector) theMutableTreeNode.getUserObject())
.getStage() : null,
printable);
}
}
add(pc, at);
}
}
}
/**
* JTree's work off of Vector's (unfortunately). This class is tailored for use with check boxes in the JTree.
*/
class NamedVector extends Vector<CheckBoxNode> {
String name;
int stageNumber;
public NamedVector(String theName) {
name = theName;
}
public NamedVector(String theName, CheckBoxNode elements[], int stage) {
this(theName, elements);
stageNumber = stage;
}
public NamedVector(String theName, CheckBoxNode elements[]) {
name = theName;
for (int i = 0, n = elements.length; i < n; i++) {
add(elements[i]);
}
}
@Override
public String toString() {
return name;
}
public int getStage() {
return stageNumber;
}
}