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 a standard Java print dialog.
50 public class PrintPanel extends JPanel implements TreeSelectionListener {
52 private static final LogHelper log = Application.getLogger();
54 private static final String TAB_TITLE = "Rocket";
55 private static final String SETTINGS_BUTTON_TEXT = "Settings";
56 private static final String PREVIEW_BUTTON_TEXT = "Preview";
57 private static final String SAVE_AS_PDF_BUTTON_TEXT = "Save as PDF";
58 private static final String SHOW_BY_STAGE = "Show By Stage";
60 private final RocketPrintTree stagedTree;
61 private final RocketPrintTree noStagedTree;
62 private OpenRocketDocument rocDoc;
63 private RocketPrintTree currentTree;
64 private boolean bDesktopSupported = false;
65 private Desktop desktop;
66 private PrintDialog printDialog;
68 JButton previewButton;
74 * @param orDocument the OR rocket container
75 * @param theParent the OR parent print dialog
77 public PrintPanel (OpenRocketDocument orDocument, PrintDialog theParent) {
79 super(new MigLayout("fill, gap rel unrel"));
81 // before any Desktop APIs are used, first check whether the API is
82 // supported by this particular VM on this particular host
83 if (Desktop.isDesktopSupported()) {
84 bDesktopSupported = true;
85 desktop = Desktop.getDesktop();
88 printDialog = theParent;
90 Rocket rocket = orDocument.getRocket();
92 noStagedTree = RocketPrintTree.create(rocket.getName());
93 noStagedTree.setShowsRootHandles(false);
94 CheckTreeManager ctm = new net.sf.openrocket.gui.print.components.CheckTreeManager(noStagedTree);
95 ctm.addTreeSelectionListener(this);
97 final int stages = rocket.getStageCount();
100 stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
101 ctm = new CheckTreeManager(stagedTree);
102 stagedTree.setShowsRootHandles(false);
103 ctm.addTreeSelectionListener(this);
106 stagedTree = noStagedTree;
108 currentTree = stagedTree;
110 final JScrollPane scrollPane = new JScrollPane(stagedTree);
111 add(scrollPane, "width 416!, wrap");
113 final JCheckBox sortByStage = new JCheckBox(SHOW_BY_STAGE);
114 sortByStage.setEnabled(stages > 1);
115 sortByStage.setSelected(stages > 1);
116 sortByStage.addActionListener(new ActionListener() {
117 public void actionPerformed (ActionEvent e) {
118 if (sortByStage.isEnabled()) {
119 if (((JCheckBox) e.getSource()).isSelected()) {
120 scrollPane.setViewportView(stagedTree);
121 stagedTree.setExpandsSelectedPaths(true);
122 currentTree = stagedTree;
125 scrollPane.setViewportView(noStagedTree);
126 noStagedTree.setExpandsSelectedPaths(true);
127 currentTree = noStagedTree;
132 add(sortByStage, "wrap");
134 saveAsPDF = new JButton(SAVE_AS_PDF_BUTTON_TEXT);
135 saveAsPDF.addActionListener(new ActionListener() {
137 public void actionPerformed (ActionEvent e) {
138 onSavePDF(PrintPanel.this);
141 add(saveAsPDF, "span 2, tag save");
143 previewButton = new JButton(PREVIEW_BUTTON_TEXT);
144 previewButton.addActionListener(new ActionListener() {
146 public void actionPerformed (ActionEvent e) {
150 add(previewButton, "x 150");
152 JButton settingsButton = new JButton(SETTINGS_BUTTON_TEXT);
153 settingsButton.addActionListener(new ActionListener() {
155 public void actionPerformed (ActionEvent e) {
156 PrintSettingsDialog settingsDialog = new PrintSettingsDialog(printDialog.getDialog());
157 settingsDialog.setVisible(true);
160 add(settingsButton, "x 340");
162 expandAll(currentTree, true);
163 if (currentTree != noStagedTree) {
164 expandAll(noStagedTree, true);
170 * The title of the tab that gets displayed for this panel, when placed in the print dialog.
174 public String getTitle () {
179 public String getName() {
184 public void valueChanged (final TreeSelectionEvent e) {
185 final TreePath path = e.getNewLeadSelectionPath();
187 previewButton.setEnabled(true);
188 saveAsPDF.setEnabled(true);
191 previewButton.setEnabled(false);
192 saveAsPDF.setEnabled(false);
197 * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
199 * @param theTree the tree to expand/contract
200 * @param expand expand if true, contract if not
202 public void expandAll (RocketPrintTree theTree, boolean expand) {
203 TreeNode root = (TreeNode) theTree.getModel().getRoot();
204 // Traverse theTree from root
205 expandAll(theTree, new TreePath(root), expand);
209 * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
212 * @param theTree the tree to expand/contract
213 * @param parent the node to iterate/recurse over
214 * @param expand expand if true, contract if not
216 private void expandAll (RocketPrintTree theTree, TreePath parent, boolean expand) {
217 theTree.addSelectionPath(parent);
219 TreeNode node = (TreeNode) parent.getLastPathComponent();
220 if (node.getChildCount() >= 0) {
221 for (Enumeration e = node.children(); e.hasMoreElements();) {
222 TreeNode n = (TreeNode) e.nextElement();
223 TreePath path = parent.pathByAddingChild(n);
224 expandAll(theTree, path, expand);
227 // Expansion or collapse must be done bottom-up
229 theTree.expandPath(parent);
232 theTree.collapsePath(parent);
237 * Get a media size name (the name of a paper size). If no page size is selected, it will default to the locale
238 * specific page size (LETTER in North America, A4 elsewhere).
240 * @return the name of a page size
242 private MediaSizeName getMediaSize () {
243 MediaSizeName paperSize = getMediaSize(printDialog.getAttributes());
244 if (paperSize == null) {
245 paperSize = PrintUtilities.getDefaultMedia().getMediaSizeName();
251 * Get the media size name (the name of a paper size) as selected by the user.
253 * @param atts the set of selected printer attributes
255 * @return a media size name, may be null
257 private MediaSizeName getMediaSize (PrintRequestAttributeSet atts) {
258 return (MediaSizeName) atts.get(javax.print.attribute.standard.Media.class);
262 * Generate a report using a temporary file. The file will be deleted upon JVM exit.
264 * @param paper the name of the paper size
266 * @return a file, populated with the "printed" output (the rocket info)
268 * @throws IOException thrown if the file could not be generated
270 private File generateReport (MediaSizeName paper) throws IOException {
271 final File f = File.createTempFile("oro", ".pdf");
273 return generateReport(f, paper);
277 * Generate a report to a specified file.
279 * @param f the file to which rocket data will be written
280 * @param paper the name of the paper size
282 * @return a file, populated with the "printed" output (the rocket info)
284 * @throws IOException thrown if the file could not be generated
286 private File generateReport (File f, MediaSizeName paper) throws IOException {
287 Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
288 new PrintController().print(rocDoc, toBePrinted, new FileOutputStream(f), paper);
293 * Generate a report to a byte array output stream.
295 * @return a stream populated with the "printed" output (the rocket info)
297 public ByteArrayOutputStream generateReport() {
298 Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
299 ByteArrayOutputStream baos = new ByteArrayOutputStream();
300 new PrintController().print(rocDoc, toBePrinted, baos, getMediaSize ());
305 * Handler for when the Preview button is clicked.
307 private void onPreview () {
308 if (bDesktopSupported) {
310 MediaSizeName paperSize = getMediaSize();
311 File f = generateReport(paperSize);
314 catch (IOException e) {
315 log.error("Could not create temporary file for previewing.", e);
316 JOptionPane.showMessageDialog(this, "Could not create a temporary file for previewing.",
317 "Error creating file", JOptionPane.ERROR_MESSAGE);
321 JOptionPane.showMessageDialog(this,
322 "Your environment does not support automatically opening the default PDF viewer.",
323 "Error creating file", JOptionPane.INFORMATION_MESSAGE);
328 * Handler for when the "Save as PDF" button is clicked.
330 * @param p the component to parent the save dialog to
332 private void onSavePDF (JComponent p) {
334 JFileChooser chooser = new JFileChooser();
335 // Note: source for ExampleFileFilter can be found in FileChooserDemo,
336 // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
337 FileFilter filter = new FileFilter() {
339 //Accept all directories and all pdf files.
340 public boolean accept (File f) {
344 //The description of this filter
345 public String getDescription () {
349 chooser.setFileFilter(filter);
350 int returnVal = chooser.showSaveDialog(p);
351 if (returnVal == JFileChooser.APPROVE_OPTION) {
354 String fname = chooser.getSelectedFile().getCanonicalPath();
355 if (!getExtension(fname).equals("pdf")) {
356 fname = fname + ".pdf";
358 File f = new File(fname);
359 generateReport(f, getMediaSize());
361 catch (IOException e) {
368 * Get the extension of a file.
370 private static String getExtension (String s) {
372 int i = s.lastIndexOf('.');
374 if (i > 0 && i < s.length() - 1) {
375 ext = s.substring(i + 1).toLowerCase();
377 return ext != null ? ext : "";
382 * This class is a dialog for displaying advanced settings for printing rocket related info.
384 class PrintSettingsDialog extends JDialog {
387 * The fill color chooser.
389 private ColorChooser fill;
392 * The line color chooser.
394 private ColorChooser line;
397 * Construct a dialog for setting the advanced rocket print settings.
399 * @param parent the owning dialog
401 public PrintSettingsDialog (JDialog parent) {
402 super(parent, "Advanced Settings", true);
403 setLayout(new MigLayout("fill"));
405 JPanel settingsPanel = new JPanel();
406 settingsPanel.setLayout(new MigLayout("gap rel"));
408 fill = addColorChooser(settingsPanel, "Template Fill", TemplateProperties.getFillColor());
409 line = addColorChooser(settingsPanel, "Template Line", TemplateProperties.getLineColor());
411 settingsPanel.add(fill);
412 settingsPanel.add(line);
414 add(settingsPanel, "wrap");
416 JButton closeButton = new JButton("Close");
417 closeButton.addActionListener(new ActionListener() {
419 public void actionPerformed (ActionEvent e) {
420 UIManager.put(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY, fill.getCurrentColor());
421 UIManager.put(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY, line.getCurrentColor());
425 add(closeButton, "right, gapright para");
431 * Add a color chooser to a panel.
433 * @param panel the parent panel to add the color chooser.
434 * @param label the label that indicates which color property is being changed
435 * @param initialColor the initial, or current, color to display
437 * @return a swing component containing a label, a colorized field, and a button that when clicked opens a color
440 private ColorChooser addColorChooser (JPanel panel, String label, Color initialColor) {
441 final JColorChooser colorChooser = new JColorChooser(initialColor);
442 return new ColorChooser(panel, colorChooser, label);