Merged l10n branch to trunk
[debian/openrocket] / src / net / sf / openrocket / gui / dialogs / PrintDialog.java
index 06119318337be94e42fc64a8938024d6d62b23b5..c820c071e59e7358628c0a2f635b0d3d557eae8d 100644 (file)
 /*
- * 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 : "";
+       }
 }