4 package net.sf.openrocket.gui.dialogs;
6 import net.miginfocom.swing.MigLayout;
7 import net.sf.openrocket.document.OpenRocketDocument;
8 import net.sf.openrocket.gui.components.ColorChooser;
9 import net.sf.openrocket.gui.print.PrintController;
10 import net.sf.openrocket.gui.print.PrintUtilities;
11 import net.sf.openrocket.gui.print.PrintableContext;
12 import net.sf.openrocket.gui.print.TemplateProperties;
13 import net.sf.openrocket.gui.print.components.CheckTreeManager;
14 import net.sf.openrocket.gui.print.components.RocketPrintTree;
15 import net.sf.openrocket.logging.LogHelper;
16 import net.sf.openrocket.rocketcomponent.Rocket;
17 import net.sf.openrocket.startup.Application;
19 import javax.print.attribute.PrintRequestAttributeSet;
20 import javax.print.attribute.standard.MediaSizeName;
21 import javax.swing.JButton;
22 import javax.swing.JCheckBox;
23 import javax.swing.JColorChooser;
24 import javax.swing.JComponent;
25 import javax.swing.JDialog;
26 import javax.swing.JFileChooser;
27 import javax.swing.JOptionPane;
28 import javax.swing.JPanel;
29 import javax.swing.JScrollPane;
30 import javax.swing.UIManager;
31 import javax.swing.event.TreeSelectionEvent;
32 import javax.swing.event.TreeSelectionListener;
33 import javax.swing.filechooser.FileFilter;
34 import javax.swing.tree.TreeNode;
35 import javax.swing.tree.TreePath;
36 import java.awt.Color;
37 import java.awt.Desktop;
38 import java.awt.event.ActionEvent;
39 import java.awt.event.ActionListener;
40 import java.io.ByteArrayOutputStream;
42 import java.io.FileOutputStream;
43 import java.io.IOException;
44 import java.util.Enumeration;
45 import java.util.Iterator;
48 * This class isolates the Swing components used to create a panel that is added to the standard Java print dialog.
50 public class PrintPanel extends JPanel implements TreeSelectionListener {
52 private static final LogHelper log = Application.getLogger();
54 private final RocketPrintTree stagedTree;
55 private final RocketPrintTree noStagedTree;
56 private OpenRocketDocument rocDoc;
57 private RocketPrintTree currentTree;
58 private boolean bDesktopSupported = false;
59 private Desktop desktop;
60 private PrintDialog printDialog;
62 JButton previewButton;
68 * @param orDocument the OR rocket container
69 * @param theParent the OR parent print dialog
71 public PrintPanel (OpenRocketDocument orDocument, PrintDialog theParent) {
73 super(new MigLayout("fill, gap rel unrel"));
75 // before any Desktop APIs are used, first check whether the API is
76 // supported by this particular VM on this particular host
77 if (Desktop.isDesktopSupported()) {
78 bDesktopSupported = true;
79 desktop = Desktop.getDesktop();
82 printDialog = theParent;
84 Rocket rocket = orDocument.getRocket();
86 noStagedTree = RocketPrintTree.create(rocket.getName());
87 noStagedTree.setShowsRootHandles(false);
88 CheckTreeManager ctm = new net.sf.openrocket.gui.print.components.CheckTreeManager(noStagedTree);
89 ctm.addTreeSelectionListener(this);
91 final int stages = rocket.getStageCount();
94 stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
95 ctm = new CheckTreeManager(stagedTree);
96 stagedTree.setShowsRootHandles(false);
97 ctm.addTreeSelectionListener(this);
100 stagedTree = noStagedTree;
102 currentTree = stagedTree;
104 final JScrollPane scrollPane = new JScrollPane(stagedTree);
105 add(scrollPane, "width 475!, wrap");
107 final JCheckBox sortByStage = new JCheckBox("Show By Stage");
108 sortByStage.setEnabled(stages > 1);
109 sortByStage.setSelected(stages > 1);
110 sortByStage.addActionListener(new ActionListener() {
111 public void actionPerformed (ActionEvent e) {
112 if (sortByStage.isEnabled()) {
113 if (((JCheckBox) e.getSource()).isSelected()) {
114 scrollPane.setViewportView(stagedTree);
115 stagedTree.setExpandsSelectedPaths(true);
116 currentTree = stagedTree;
119 scrollPane.setViewportView(noStagedTree);
120 noStagedTree.setExpandsSelectedPaths(true);
121 currentTree = noStagedTree;
126 add(sortByStage, "wrap");
128 saveAsPDF = new JButton("Save as PDF");
129 saveAsPDF.addActionListener(new ActionListener() {
131 public void actionPerformed (ActionEvent e) {
132 onSavePDF(PrintPanel.this);
135 add(saveAsPDF, "span 2, tag save");
137 previewButton = new JButton("Preview");
138 previewButton.addActionListener(new ActionListener() {
140 public void actionPerformed (ActionEvent e) {
144 add(previewButton, "x 150");
146 JButton settingsButton = new JButton("Settings");
147 settingsButton.addActionListener(new ActionListener() {
149 public void actionPerformed (ActionEvent e) {
150 PrintSettingsDialog settingsDialog = new PrintSettingsDialog(printDialog.getDialog());
151 settingsDialog.setVisible(true);
154 add(settingsButton, "x 400");
156 expandAll(currentTree, true);
157 if (currentTree != noStagedTree) {
158 expandAll(noStagedTree, true);
164 * The title of the tab that gets displayed for this panel, when placed in the print dialog.
168 public String getTitle () {
173 public void valueChanged (final TreeSelectionEvent e) {
174 final TreePath path = e.getNewLeadSelectionPath();
176 previewButton.setEnabled(true);
177 saveAsPDF.setEnabled(true);
180 previewButton.setEnabled(false);
181 saveAsPDF.setEnabled(false);
186 * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
188 * @param theTree the tree to expand/contract
189 * @param expand expand if true, contract if not
191 public void expandAll (RocketPrintTree theTree, boolean expand) {
192 TreeNode root = (TreeNode) theTree.getModel().getRoot();
193 // Traverse theTree from root
194 expandAll(theTree, new TreePath(root), expand);
198 * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
201 * @param theTree the tree to expand/contract
202 * @param parent the node to iterate/recurse over
203 * @param expand expand if true, contract if not
205 private void expandAll (RocketPrintTree theTree, TreePath parent, boolean expand) {
206 theTree.addSelectionPath(parent);
208 TreeNode node = (TreeNode) parent.getLastPathComponent();
209 if (node.getChildCount() >= 0) {
210 for (Enumeration e = node.children(); e.hasMoreElements();) {
211 TreeNode n = (TreeNode) e.nextElement();
212 TreePath path = parent.pathByAddingChild(n);
213 expandAll(theTree, path, expand);
216 // Expansion or collapse must be done bottom-up
218 theTree.expandPath(parent);
221 theTree.collapsePath(parent);
226 * Get a media size name (the name of a paper size). If no page size is selected, it will default to the locale
227 * specific page size (LETTER in North America, A4 elsewhere).
229 * @return the name of a page size
231 private MediaSizeName getMediaSize () {
232 MediaSizeName paperSize = getMediaSize(printDialog.getAttributes());
233 if (paperSize == null) {
234 paperSize = PrintUtilities.getDefaultMedia().getMediaSizeName();
240 * Get the media size name (the name of a paper size) as selected by the user.
242 * @param atts the set of selected printer attributes
244 * @return a media size name, may be null
246 private MediaSizeName getMediaSize (PrintRequestAttributeSet atts) {
247 return (MediaSizeName) atts.get(javax.print.attribute.standard.Media.class);
251 * Generate a report using a temporary file. The file will be deleted upon JVM exit.
253 * @param paper the name of the paper size
255 * @return a file, populated with the "printed" output (the rocket info)
257 * @throws IOException thrown if the file could not be generated
259 private File generateReport (MediaSizeName paper) throws IOException {
260 final File f = File.createTempFile("oro", ".pdf");
262 return generateReport(f, paper);
266 * Generate a report to a specified file.
268 * @param f the file to which rocket data will be written
269 * @param paper the name of the paper size
271 * @return a file, populated with the "printed" output (the rocket info)
273 * @throws IOException thrown if the file could not be generated
275 private File generateReport (File f, MediaSizeName paper) throws IOException {
276 Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
277 new PrintController().print(rocDoc, toBePrinted, new FileOutputStream(f), paper);
282 * Generate a report to a byte array output stream.
284 * @return a stream populated with the "printed" output (the rocket info)
286 public ByteArrayOutputStream generateReport() {
287 Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
288 ByteArrayOutputStream baos = new ByteArrayOutputStream();
289 new PrintController().print(rocDoc, toBePrinted, baos, getMediaSize ());
294 * Handler for when the Preview button is clicked.
296 private void onPreview () {
297 if (bDesktopSupported) {
299 MediaSizeName paperSize = getMediaSize();
300 File f = generateReport(paperSize);
303 catch (IOException e) {
304 log.error("Could not create temporary file for previewing.", e);
305 JOptionPane.showMessageDialog(this, "Could not create a temporary file for previewing.",
306 "Error creating file", JOptionPane.ERROR_MESSAGE);
310 JOptionPane.showMessageDialog(this,
311 "Your environment does not support automatically opening the default PDF viewer.",
312 "Error creating file", JOptionPane.INFORMATION_MESSAGE);
317 * Handler for when the "Save as PDF" button is clicked.
319 * @param p the component to parent the save dialog to
321 private void onSavePDF (JComponent p) {
323 JFileChooser chooser = new JFileChooser();
324 // Note: source for ExampleFileFilter can be found in FileChooserDemo,
325 // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
326 FileFilter filter = new FileFilter() {
328 //Accept all directories and all pdf files.
329 public boolean accept (File f) {
333 //The description of this filter
334 public String getDescription () {
338 chooser.setFileFilter(filter);
339 int returnVal = chooser.showSaveDialog(p);
340 if (returnVal == JFileChooser.APPROVE_OPTION) {
343 String fname = chooser.getSelectedFile().getCanonicalPath();
344 if (!getExtension(fname).equals("pdf")) {
345 fname = fname + ".pdf";
347 File f = new File(fname);
348 generateReport(f, getMediaSize());
350 catch (IOException e) {
357 * Get the extension of a file.
359 private static String getExtension (String s) {
361 int i = s.lastIndexOf('.');
363 if (i > 0 && i < s.length() - 1) {
364 ext = s.substring(i + 1).toLowerCase();
366 return ext != null ? ext : "";
371 * This class is a dialog for displaying advanced settings for printing rocket related info.
373 class PrintSettingsDialog extends JDialog {
376 * The fill color chooser.
378 private ColorChooser fill;
381 * The line color chooser.
383 private ColorChooser line;
386 * Construct a dialog for setting the advanced rocket print settings.
388 * @param parent the owning dialog
390 public PrintSettingsDialog (JDialog parent) {
391 super(parent, "Advanced Settings", true);
392 setLayout(new MigLayout("fill"));
394 JPanel settingsPanel = new JPanel();
395 settingsPanel.setLayout(new MigLayout("gap rel"));
397 fill = addColorChooser(settingsPanel, "Template Fill", TemplateProperties.getFillColor());
398 line = addColorChooser(settingsPanel, "Template Line", TemplateProperties.getLineColor());
400 settingsPanel.add(fill);
401 settingsPanel.add(line);
403 add(settingsPanel, "wrap");
405 JButton closeButton = new JButton("Close");
406 closeButton.addActionListener(new ActionListener() {
408 public void actionPerformed (ActionEvent e) {
409 UIManager.put(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY, fill.getCurrentColor());
410 UIManager.put(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY, line.getCurrentColor());
414 add(closeButton, "right, gapright para");
420 * Add a color chooser to a panel.
422 * @param panel the parent panel to add the color chooser.
423 * @param label the label that indicates which color property is being changed
424 * @param initialColor the initial, or current, color to display
426 * @return a swing component containing a label, a colorized field, and a button that when clicked opens a color
429 private ColorChooser addColorChooser (JPanel panel, String label, Color initialColor) {
430 final JColorChooser colorChooser = new JColorChooser(initialColor);
431 return new ColorChooser(panel, colorChooser, label);