/*
- * PrintDialog.java
+ * PrintPanel.java
*/
package net.sf.openrocket.gui.dialogs;
+import java.awt.Desktop;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.print.PDFPrintStreamDoc;
-import net.sf.openrocket.gui.print.PrintUtilities;
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.gui.print.PrintController;
+import net.sf.openrocket.gui.print.PrintSettings;
+import net.sf.openrocket.gui.print.PrintableContext;
+import net.sf.openrocket.gui.print.TemplateProperties;
+import net.sf.openrocket.gui.print.components.CheckTreeManager;
+import net.sf.openrocket.gui.print.components.RocketPrintTree;
import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.Rocket;
import net.sf.openrocket.startup.Application;
-import sun.print.ServiceDialog;
-
-import javax.print.DocFlavor;
-import javax.print.DocPrintJob;
-import javax.print.PrintException;
-import javax.print.PrintService;
-import javax.print.PrintServiceLookup;
-import javax.print.attribute.Attribute;
-import javax.print.attribute.AttributeSet;
-import javax.print.attribute.HashPrintRequestAttributeSet;
-import javax.print.attribute.PrintRequestAttributeSet;
-import javax.print.attribute.standard.Destination;
-import javax.print.attribute.standard.Fidelity;
-import javax.swing.JDialog;
-import javax.swing.JMenu;
-import javax.swing.JTabbedPane;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Dialog;
-import java.awt.GraphicsConfiguration;
-import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.awt.HeadlessException;
-import java.awt.Rectangle;
-import java.io.ByteArrayOutputStream;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.Prefs;
/**
- * This class is not a dialog by inheritance, but is by delegation. It front-ends a java print dialog by
- * augmenting it with application specific (rocket) settings.
+ * This class isolates the Swing components used to create a panel that is added to a standard Java print dialog.
*/
-public class PrintDialog {
-
- /**
- * The service UI dialog.
- */
- private ServiceDialog dialog;
-
- /**
- * A javax doc flavor specific for printing PDF documents.
- */
- private static final DocFlavor.INPUT_STREAM PDF = DocFlavor.INPUT_STREAM.PDF;
-
- private static final Translator trans = Application.getTranslator();
-
- /**
- * Construct a print dialog using an Open Rocket document - which contains the rocket data to ultimately be
- * printed.
- *
- * @param orDocument the rocket container
- */
- public PrintDialog (OpenRocketDocument orDocument) {
- PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
- PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
- PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
- attrs.add(PrintUtilities.getDefaultMedia().getMediaSizeName());
-
- final PrintPanel panel = new PrintPanel(orDocument, this);
- PrintService ps = printDialog(null, 100, 100, services, svc, PDF, attrs, panel);
- if (ps != null) {
- DocPrintJob dpj = ps.createPrintJob();
- try {
- System.err.println(attrs.size());
- ByteArrayOutputStream baos = panel.generateReport();
- dpj.print(new PDFPrintStreamDoc(baos, null), attrs);
- }
- catch (PrintException e) {
- e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
- }
- }
- }
-
- /**
- * Get the set of attributes from the service ui print dialog.
- *
- * @return a set of print attributes
- */
- PrintRequestAttributeSet getAttributes () {
- return dialog.getAttributes();
- }
-
- /**
- * Get the service ui dialog. This is the actual dialog that gets displayed - we co-opt it for adding a rocket
- * specific tab.
- *
- * @return the Java service ui print dialog
- */
- JDialog getDialog () {
- return dialog;
- }
-
- /**
- * Mimics the ServiceUI.printDialog method, but with enhancements for our own print settings tab.
- *
- * @param gc used to select screen. null means primary or default screen.
- * @param x location of dialog including border in screen coordinates
- * @param y location of dialog including border in screen coordinates
- * @param services to be browsable, must be non-null.
- * @param defaultService - initial PrintService to display.
- * @param flavor - the flavor to be printed, or null.
- * @param attributes on input is the initial application supplied preferences. This cannot be null but may be
- * empty. On output the attributes reflect changes made by the user.
- * @param addnl a panel to be added, as a tab, to the internal tabbed pane of the resulting print dialog
- *
- * @return print service selected by the user, or null if the user cancelled the dialog.
- *
- * @throws HeadlessException if GraphicsEnvironment.isHeadless() returns true.
- * @throws IllegalArgumentException if services is null or empty, or attributes is null, or the initial PrintService
- * is not in the list of browsable services.
- */
- private PrintService printDialog (GraphicsConfiguration gc,
- int x, int y,
- PrintService[] services,
- PrintService defaultService,
- DocFlavor flavor,
- PrintRequestAttributeSet attributes,
- PrintPanel addnl)
- throws HeadlessException {
- int defaultIndex = -1;
-
- if (GraphicsEnvironment.isHeadless()) {
- throw new HeadlessException();
- }
- else if ((services == null) || (services.length == 0)) {
- throw new IllegalArgumentException("services must be non-null " +
- "and non-empty");
- }
- else if (attributes == null) {
- throw new IllegalArgumentException("attributes must be non-null");
- }
-
- if (defaultService != null) {
- for (int i = 0; i < services.length; i++) {
- if (services[i].equals(defaultService)) {
- defaultIndex = i;
- break;
- }
- }
-
- if (defaultIndex < 0) {
- throw new IllegalArgumentException("services must contain " +
- "defaultService");
- }
- }
- else {
- defaultIndex = 0;
- }
-
- Rectangle gcBounds = (gc == null) ? GraphicsEnvironment.
- getLocalGraphicsEnvironment().getDefaultScreenDevice().
- getDefaultConfiguration().getBounds() : gc.getBounds();
-
- dialog = new ServiceDialog(gc,
- x + gcBounds.x,
- y + gcBounds.y,
- services, defaultIndex,
- flavor, attributes,
- (Dialog) null);
- Rectangle dlgBounds = dialog.getBounds();
-
- // get union of all GC bounds
- GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
- GraphicsDevice[] gs = ge.getScreenDevices();
- for (GraphicsDevice g : gs) {
- gcBounds = gcBounds.union(g.getDefaultConfiguration().getBounds());
- }
-
- // if portion of dialog is not within the gc boundary
- if (!gcBounds.contains(dlgBounds)) {
- // put in the center relative to parent frame/dialog
- dialog.setLocationRelativeTo(null);
- }
- if (addnl != null && addnl.getTitle() != null) {
- JTabbedPane tp = (JTabbedPane) getDescendantOfClass(JTabbedPane.class, dialog);
- tp.add(addnl, addnl.getTitle(), 0);
- tp.setSelectedIndex(0);
- }
+public class PrintDialog extends JDialog implements TreeSelectionListener {
+
+ private static final LogHelper log = Application.getLogger();
+ private static final Translator trans = Application.getTranslator();
+
+ private final Desktop desktop;
+
+ private final RocketPrintTree stagedTree;
+ private final RocketPrintTree noStagedTree;
+ private OpenRocketDocument document;
+ private RocketPrintTree currentTree;
+
+ private JButton previewButton;
+ private JButton saveAsPDF;
+ private JButton cancel;
+
+ /**
+ * Constructor.
+ *
+ * @param orDocument the OR rocket container
+ */
+ public PrintDialog(Window parent, OpenRocketDocument orDocument) {
+ super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
+
- dialog.setVisible(true);
+ JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel"));
+ this.add(panel);
+
- if (dialog.getStatus() == ServiceDialog.APPROVE) {
- PrintRequestAttributeSet newas = dialog.getAttributes();
- Class dstCategory = Destination.class;
- Class fdCategory = Fidelity.class;
+ // before any Desktop APIs are used, first check whether the API is
+ // supported by this particular VM on this particular host
+ if (Desktop.isDesktopSupported()) {
+ desktop = Desktop.getDesktop();
+ } else {
+ desktop = null;
+ }
+
+ document = orDocument;
+ Rocket rocket = orDocument.getRocket();
+
+ noStagedTree = RocketPrintTree.create(rocket.getName());
+ noStagedTree.setShowsRootHandles(false);
+ CheckTreeManager ctm = new net.sf.openrocket.gui.print.components.CheckTreeManager(noStagedTree);
+ ctm.addTreeSelectionListener(this);
+
+ final int stages = rocket.getStageCount();
+
- if (attributes.containsKey(dstCategory) &&
- !newas.containsKey(dstCategory)) {
- attributes.remove(dstCategory);
- }
+ JLabel label = new JLabel(trans.get("lbl.selectElements"));
+ panel.add(label, "wrap unrel");
+
+ // Create the tree
+ if (stages > 1) {
+ stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
+ ctm = new CheckTreeManager(stagedTree);
+ stagedTree.setShowsRootHandles(false);
+ ctm.addTreeSelectionListener(this);
+ } else {
+ stagedTree = noStagedTree;
+ }
+ currentTree = stagedTree;
+
+ // Add the tree to the UI
+ final JScrollPane scrollPane = new JScrollPane(stagedTree);
+ panel.add(scrollPane, "width 400lp, height 200lp, grow, wrap para");
+
- attributes.addAll(newas);
+ // Checkboxes and buttons
+ final JCheckBox sortByStage = new JCheckBox(trans.get("checkbox.showByStage"));
+ sortByStage.setEnabled(stages > 1);
+ sortByStage.setSelected(stages > 1);
+ sortByStage.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (sortByStage.isEnabled()) {
+ if (((JCheckBox) e.getSource()).isSelected()) {
+ scrollPane.setViewportView(stagedTree);
+ stagedTree.setExpandsSelectedPaths(true);
+ currentTree = stagedTree;
+ }
+ else {
+ scrollPane.setViewportView(noStagedTree);
+ noStagedTree.setExpandsSelectedPaths(true);
+ currentTree = noStagedTree;
+ }
+ }
+ }
+ });
+ panel.add(sortByStage, "aligny top, split");
+
- Fidelity fd = (Fidelity) attributes.get(fdCategory);
- if (fd != null) {
- if (fd == Fidelity.FIDELITY_TRUE) {
- removeUnsupportedAttributes(dialog.getPrintService(),
- flavor, attributes);
- }
- }
- return dialog.getPrintService();
- }
- else {
- return null;
- }
- }
+ panel.add(new JPanel(), "growx");
+
- private Component getDescendantOfClass (Class c, Container cont) {
- if (c == null || cont == null) {
- return null;
- }
- Component[] children = (cont instanceof JMenu)
- ? ((JMenu) cont).getMenuComponents()
- : cont.getComponents();
- for (int i = 0, n = children.length; i < n; i++) {
- Component comp = children[i];
- if (c.isInstance(comp)) {
- return comp;
- }
- comp = getDescendantOfClass(c, (Container) comp);
- if (comp != null) {
- return comp;
- }
- }
- return null;
- }
+ JButton settingsButton = new JButton(trans.get("printdlg.but.settings"));
+ settingsButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PrintSettings settings = Prefs.getPrintSettings();
+ log.debug("settings=" + settings);
+ PrintSettingsDialog settingsDialog = new PrintSettingsDialog(PrintDialog.this, settings);
+ settingsDialog.setVisible(true);
+ Prefs.setPrintSettings(settings);
+ }
+ });
+ panel.add(settingsButton, "wrap para");
+
- /**
- * Removes any attributes from the given AttributeSet that are unsupported by the given PrintService/DocFlavor
- * combination.
- *
- * @param ps the print service for which unsupported attributes will be determined
- * @param flavor the document flavor; PDF in our case
- * @param aset the set of attributes requested
- */
- private static void removeUnsupportedAttributes (PrintService ps,
- DocFlavor flavor,
- AttributeSet aset) {
- AttributeSet asUnsupported = ps.getUnsupportedAttributes(flavor,
- aset);
+ previewButton = new JButton(trans.get("but.previewAndPrint"));
+ previewButton.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ onPreview();
+ PrintDialog.this.setVisible(false);
+ }
+ });
+ panel.add(previewButton, "split, right, gap para");
+
- if (asUnsupported != null) {
- Attribute[] usAttrs = asUnsupported.toArray();
+ saveAsPDF = new JButton(trans.get("printdlg.but.saveaspdf"));
+ saveAsPDF.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (onSavePDF()) {
+ PrintDialog.this.setVisible(false);
+ }
+ }
+ });
+ panel.add(saveAsPDF, "right, gap para");
+
- for (Attribute usAttr : usAttrs) {
- Class<? extends Attribute> category = usAttr.getCategory();
+ cancel = new JButton(trans.get("button.cancel"));
+ cancel.addActionListener(new ActionListener() {
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ PrintDialog.this.setVisible(false);
+ }
+ });
+ panel.add(cancel, "right, gap para");
+
- if (ps.isAttributeCategorySupported(category)) {
- Attribute attr =
- (Attribute) ps.getDefaultAttributeValue(category);
+ expandAll(currentTree, true);
+ if (currentTree != noStagedTree) {
+ expandAll(noStagedTree, true);
+ }
+
- if (attr != null) {
- aset.add(attr);
- }
- else {
- aset.remove(category);
- }
- }
- else {
- aset.remove(category);
- }
- }
- }
- }
+ GUIUtil.setDisposableDialogOptions(this, previewButton);
+
+ }
+
+
+ @Override
+ public void valueChanged(final TreeSelectionEvent e) {
+ final TreePath path = e.getNewLeadSelectionPath();
+ if (path != null) {
+ previewButton.setEnabled(true);
+ saveAsPDF.setEnabled(true);
+ } else {
+ previewButton.setEnabled(false);
+ saveAsPDF.setEnabled(false);
+ }
+ }
+
+ /**
+ * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
+ *
+ * @param theTree the tree to expand/contract
+ * @param expand expand if true, contract if not
+ */
+ public void expandAll(RocketPrintTree theTree, boolean expand) {
+ TreeNode root = (TreeNode) theTree.getModel().getRoot();
+ // Traverse theTree from root
+ expandAll(theTree, new TreePath(root), expand);
+ }
+
+ /**
+ * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
+ * the theTree.
+ *
+ * @param theTree the tree to expand/contract
+ * @param parent the node to iterate/recurse over
+ * @param expand expand if true, contract if not
+ */
+ private void expandAll(RocketPrintTree theTree, TreePath parent, boolean expand) {
+ theTree.addSelectionPath(parent);
+ // Traverse children
+ TreeNode node = (TreeNode) parent.getLastPathComponent();
+ if (node.getChildCount() >= 0) {
+ for (Enumeration<?> e = node.children(); e.hasMoreElements();) {
+ TreeNode n = (TreeNode) e.nextElement();
+ TreePath path = parent.pathByAddingChild(n);
+ expandAll(theTree, path, expand);
+ }
+ }
+ // Expansion or collapse must be done bottom-up
+ if (expand) {
+ theTree.expandPath(parent);
+ } else {
+ theTree.collapsePath(parent);
+ }
+ }
+
+
+ /**
+ * Generate a report using a temporary file. The file will be deleted upon JVM exit.
+ *
+ * @param paper the name of the paper size
+ *
+ * @return a file, populated with the "printed" output (the rocket info)
+ *
+ * @throws IOException thrown if the file could not be generated
+ */
+ private File generateReport(PrintSettings settings) throws IOException {
+ final File f = File.createTempFile("openrocket-", ".pdf");
+ f.deleteOnExit();
+ return generateReport(f, settings);
+ }
+
+ /**
+ * Generate a report to a specified file.
+ *
+ * @param f the file to which rocket data will be written
+ * @param paper the name of the paper size
+ *
+ * @return a file, populated with the "printed" output (the rocket info)
+ *
+ * @throws IOException thrown if the file could not be generated
+ */
+ private File generateReport(File f, PrintSettings settings) throws IOException {
+ Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
+ new PrintController().print(document, toBePrinted, new FileOutputStream(f), settings);
+ return f;
+ }
+
+
+ /**
+ * Handler for when the Preview button is clicked.
+ */
+ private void onPreview() {
+ if (desktop != null) {
+ try {
+ PrintSettings settings = Prefs.getPrintSettings();
+ // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
+ TemplateProperties.setColors(settings);
+ File f = generateReport(settings);
+ desktop.open(f);
+ } catch (IOException e) {
+ log.error("Could not open preview.", e);
+ JOptionPane.showMessageDialog(this, new String[] {
+ trans.get("error.preview.desc1"),
+ trans.get("error.preview.desc2") },
+ trans.get("error.preview.title"),
+ JOptionPane.ERROR_MESSAGE);
+ }
+ } else {
+ JOptionPane.showMessageDialog(this, new String[] {
+ trans.get("error.preview.desc1"),
+ trans.get("error.preview.desc2") },
+ trans.get("error.preview.title"),
+ JOptionPane.INFORMATION_MESSAGE);
+ }
+ }
+
+ /**
+ * Handler for when the "Save as PDF" button is clicked.
+ *
+ * @return true if the PDF was saved
+ */
+ private boolean onSavePDF() {
+
+ JFileChooser chooser = new JFileChooser();
+ // Note: source for ExampleFileFilter can be found in FileChooserDemo,
+ // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
+ FileFilter filter = new FileFilter() {
+
+ //Accept all directories and all pdf files.
+ @Override
+ public boolean accept(File f) {
+ if (f.isDirectory())
+ return true;
+ return f.getName().toLowerCase().endsWith(".pdf");
+ }
+
+ //The description of this filter
+ @Override
+ public String getDescription() {
+ return trans.get("filetypes.pdf");
+ }
+ };
+ chooser.setFileFilter(filter);
+ int returnVal = chooser.showSaveDialog(this);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+
+ try {
+ String fname = chooser.getSelectedFile().getCanonicalPath();
+ if (!getExtension(fname).equals("pdf")) {
+ fname = fname + ".pdf";
+ }
+ File f = new File(fname);
+ PrintSettings settings = Prefs.getPrintSettings();
+ // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
+ TemplateProperties.setColors(settings);
+ generateReport(f, settings);
+ } catch (IOException e) {
+ ExceptionHandler.handleErrorCondition(e);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Get the extension of a file.
+ */
+ private static String getExtension(String s) {
+ String ext = null;
+ int i = s.lastIndexOf('.');
+
+ if (i > 0 && i < s.length() - 1) {
+ ext = s.substring(i + 1).toLowerCase();
+ }
+ return ext != null ? ext : "";
+ }
}