bug fixes and rocket optimization
[debian/openrocket] / src / net / sf / openrocket / gui / dialogs / PrintDialog.java
1 /*
2  * PrintPanel.java
3  */
4 package net.sf.openrocket.gui.dialogs;
5
6 import java.awt.Desktop;
7 import java.awt.Window;
8 import java.awt.event.ActionEvent;
9 import java.awt.event.ActionListener;
10 import java.io.File;
11 import java.io.FileOutputStream;
12 import java.io.IOException;
13 import java.util.Enumeration;
14 import java.util.Iterator;
15
16 import javax.swing.JButton;
17 import javax.swing.JCheckBox;
18 import javax.swing.JDialog;
19 import javax.swing.JFileChooser;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JScrollPane;
24 import javax.swing.event.TreeSelectionEvent;
25 import javax.swing.event.TreeSelectionListener;
26 import javax.swing.filechooser.FileFilter;
27 import javax.swing.tree.TreeNode;
28 import javax.swing.tree.TreePath;
29
30 import net.miginfocom.swing.MigLayout;
31 import net.sf.openrocket.document.OpenRocketDocument;
32 import net.sf.openrocket.gui.main.ExceptionHandler;
33 import net.sf.openrocket.gui.print.PrintController;
34 import net.sf.openrocket.gui.print.PrintSettings;
35 import net.sf.openrocket.gui.print.PrintableContext;
36 import net.sf.openrocket.gui.print.TemplateProperties;
37 import net.sf.openrocket.gui.print.components.CheckTreeManager;
38 import net.sf.openrocket.gui.print.components.RocketPrintTree;
39 import net.sf.openrocket.l10n.Translator;
40 import net.sf.openrocket.logging.LogHelper;
41 import net.sf.openrocket.rocketcomponent.Rocket;
42 import net.sf.openrocket.startup.Application;
43 import net.sf.openrocket.util.GUIUtil;
44 import net.sf.openrocket.util.Prefs;
45
46 /**
47  * This class isolates the Swing components used to create a panel that is added to a standard Java print dialog.
48  */
49 public class PrintDialog extends JDialog implements TreeSelectionListener {
50         
51         // FIXME:  Printouts use SI units even when imperial are selected
52         // FIXME:  Array out of bounds exception when printing
53         
54         private static final LogHelper log = Application.getLogger();
55         private static final Translator trans = Application.getTranslator();
56         
57         private final Desktop desktop;
58         
59         private final RocketPrintTree stagedTree;
60         private final RocketPrintTree noStagedTree;
61         private OpenRocketDocument document;
62         private RocketPrintTree currentTree;
63         
64         private JButton previewButton;
65         private JButton saveAsPDF;
66         private JButton cancel;
67         
68         /**
69          * Constructor.
70          *
71          * @param orDocument the OR rocket container
72          */
73         public PrintDialog(Window parent, OpenRocketDocument orDocument) {
74                 super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
75                 
76
77                 JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel"));
78                 this.add(panel);
79                 
80
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                         desktop = Desktop.getDesktop();
85                 } else {
86                         desktop = null;
87                 }
88                 
89                 document = orDocument;
90                 Rocket rocket = orDocument.getRocket();
91                 
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);
96                 
97                 final int stages = rocket.getStageCount();
98                 
99
100                 JLabel label = new JLabel(trans.get("lbl.selectElements"));
101                 panel.add(label, "wrap unrel");
102                 
103                 // Create the tree
104                 if (stages > 1) {
105                         stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
106                         ctm = new CheckTreeManager(stagedTree);
107                         stagedTree.setShowsRootHandles(false);
108                         ctm.addTreeSelectionListener(this);
109                 } else {
110                         stagedTree = noStagedTree;
111                 }
112                 currentTree = stagedTree;
113                 
114                 // Add the tree to the UI
115                 final JScrollPane scrollPane = new JScrollPane(stagedTree);
116                 panel.add(scrollPane, "width 400lp, height 200lp, grow, wrap para");
117                 
118
119                 // Checkboxes and buttons
120                 final JCheckBox sortByStage = new JCheckBox(trans.get("checkbox.showByStage"));
121                 sortByStage.setEnabled(stages > 1);
122                 sortByStage.setSelected(stages > 1);
123                 sortByStage.addActionListener(new ActionListener() {
124                         @Override
125                         public void actionPerformed(ActionEvent e) {
126                                 if (sortByStage.isEnabled()) {
127                                         if (((JCheckBox) e.getSource()).isSelected()) {
128                                                 scrollPane.setViewportView(stagedTree);
129                                                 stagedTree.setExpandsSelectedPaths(true);
130                                                 currentTree = stagedTree;
131                                         }
132                                         else {
133                                                 scrollPane.setViewportView(noStagedTree);
134                                                 noStagedTree.setExpandsSelectedPaths(true);
135                                                 currentTree = noStagedTree;
136                                         }
137                                 }
138                         }
139                 });
140                 panel.add(sortByStage, "aligny top, split");
141                 
142
143                 panel.add(new JPanel(), "growx");
144                 
145
146                 JButton settingsButton = new JButton(trans.get("printdlg.but.settings"));
147                 settingsButton.addActionListener(new ActionListener() {
148                         @Override
149                         public void actionPerformed(ActionEvent e) {
150                                 PrintSettings settings = Prefs.getPrintSettings();
151                                 log.debug("settings=" + settings);
152                                 PrintSettingsDialog settingsDialog = new PrintSettingsDialog(PrintDialog.this, settings);
153                                 settingsDialog.setVisible(true);
154                                 Prefs.setPrintSettings(settings);
155                         }
156                 });
157                 panel.add(settingsButton, "wrap para");
158                 
159
160                 previewButton = new JButton(trans.get("but.previewAndPrint"));
161                 previewButton.addActionListener(new ActionListener() {
162                         @Override
163                         public void actionPerformed(ActionEvent e) {
164                                 onPreview();
165                                 PrintDialog.this.setVisible(false);
166                         }
167                 });
168                 panel.add(previewButton, "split, right, gap para");
169                 
170
171                 saveAsPDF = new JButton(trans.get("printdlg.but.saveaspdf"));
172                 saveAsPDF.addActionListener(new ActionListener() {
173                         @Override
174                         public void actionPerformed(ActionEvent e) {
175                                 if (onSavePDF()) {
176                                         PrintDialog.this.setVisible(false);
177                                 }
178                         }
179                 });
180                 panel.add(saveAsPDF, "right, gap para");
181                 
182
183                 cancel = new JButton(trans.get("button.cancel"));
184                 cancel.addActionListener(new ActionListener() {
185                         @Override
186                         public void actionPerformed(ActionEvent e) {
187                                 PrintDialog.this.setVisible(false);
188                         }
189                 });
190                 panel.add(cancel, "right, gap para");
191                 
192
193                 expandAll(currentTree, true);
194                 if (currentTree != noStagedTree) {
195                         expandAll(noStagedTree, true);
196                 }
197                 
198
199                 GUIUtil.setDisposableDialogOptions(this, previewButton);
200                 
201         }
202         
203         
204         @Override
205         public void valueChanged(final TreeSelectionEvent e) {
206                 final TreePath path = e.getNewLeadSelectionPath();
207                 if (path != null) {
208                         previewButton.setEnabled(true);
209                         saveAsPDF.setEnabled(true);
210                 } else {
211                         previewButton.setEnabled(false);
212                         saveAsPDF.setEnabled(false);
213                 }
214         }
215         
216         /**
217          * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
218          *
219          * @param theTree   the tree to expand/contract
220          * @param expand expand if true, contract if not
221          */
222         public void expandAll(RocketPrintTree theTree, boolean expand) {
223                 TreeNode root = (TreeNode) theTree.getModel().getRoot();
224                 // Traverse theTree from root
225                 expandAll(theTree, new TreePath(root), expand);
226         }
227         
228         /**
229          * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
230          * the theTree.
231          *
232          * @param theTree   the tree to expand/contract
233          * @param parent the node to iterate/recurse over
234          * @param expand expand if true, contract if not
235          */
236         private void expandAll(RocketPrintTree theTree, TreePath parent, boolean expand) {
237                 theTree.addSelectionPath(parent);
238                 // Traverse children
239                 TreeNode node = (TreeNode) parent.getLastPathComponent();
240                 if (node.getChildCount() >= 0) {
241                         for (Enumeration<?> e = node.children(); e.hasMoreElements();) {
242                                 TreeNode n = (TreeNode) e.nextElement();
243                                 TreePath path = parent.pathByAddingChild(n);
244                                 expandAll(theTree, path, expand);
245                         }
246                 }
247                 // Expansion or collapse must be done bottom-up
248                 if (expand) {
249                         theTree.expandPath(parent);
250                 } else {
251                         theTree.collapsePath(parent);
252                 }
253         }
254         
255         
256
257         /**
258          * Generate a report using a temporary file.  The file will be deleted upon JVM exit.
259          *
260          * @param paper the name of the paper size
261          *
262          * @return a file, populated with the "printed" output (the rocket info)
263          *
264          * @throws IOException thrown if the file could not be generated
265          */
266         private File generateReport(PrintSettings settings) throws IOException {
267                 final File f = File.createTempFile("openrocket-", ".pdf");
268                 f.deleteOnExit();
269                 return generateReport(f, settings);
270         }
271         
272         /**
273          * Generate a report to a specified file.
274          *
275          * @param f     the file to which rocket data will be written
276          * @param paper the name of the paper size
277          *
278          * @return a file, populated with the "printed" output (the rocket info)
279          *
280          * @throws IOException thrown if the file could not be generated
281          */
282         private File generateReport(File f, PrintSettings settings) throws IOException {
283                 Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
284                 new PrintController().print(document, toBePrinted, new FileOutputStream(f), settings);
285                 return f;
286         }
287         
288         
289         /**
290          * Handler for when the Preview button is clicked.
291          */
292         private void onPreview() {
293                 if (desktop != null) {
294                         try {
295                                 PrintSettings settings = Prefs.getPrintSettings();
296                                 // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
297                                 TemplateProperties.setColors(settings);
298                                 File f = generateReport(settings);
299                                 desktop.open(f);
300                         } catch (IOException e) {
301                                 log.error("Could not open preview.", e);
302                                 JOptionPane.showMessageDialog(this, new String[] {
303                                                 trans.get("error.preview.desc1"),
304                                                 trans.get("error.preview.desc2") },
305                                                 trans.get("error.preview.title"),
306                                                 JOptionPane.ERROR_MESSAGE);
307                         }
308                 } else {
309                         JOptionPane.showMessageDialog(this, new String[] {
310                                         trans.get("error.preview.desc1"),
311                                         trans.get("error.preview.desc2") },
312                                         trans.get("error.preview.title"),
313                                         JOptionPane.INFORMATION_MESSAGE);
314                 }
315         }
316         
317         /**
318          * Handler for when the "Save as PDF" button is clicked.
319          *
320          * @return      true if the PDF was saved
321          */
322         private boolean onSavePDF() {
323                 
324                 JFileChooser chooser = new JFileChooser();
325                 // Note: source for ExampleFileFilter can be found in FileChooserDemo,
326                 // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
327                 FileFilter filter = new FileFilter() {
328                         
329                         //Accept all directories and all pdf files.
330                         @Override
331                         public boolean accept(File f) {
332                                 if (f.isDirectory())
333                                         return true;
334                                 return f.getName().toLowerCase().endsWith(".pdf");
335                         }
336                         
337                         //The description of this filter
338                         @Override
339                         public String getDescription() {
340                                 return trans.get("filetypes.pdf");
341                         }
342                 };
343                 chooser.setFileFilter(filter);
344                 int returnVal = chooser.showSaveDialog(this);
345                 if (returnVal == JFileChooser.APPROVE_OPTION) {
346                         
347                         try {
348                                 String fname = chooser.getSelectedFile().getCanonicalPath();
349                                 if (!getExtension(fname).equals("pdf")) {
350                                         fname = fname + ".pdf";
351                                 }
352                                 File f = new File(fname);
353                                 PrintSettings settings = Prefs.getPrintSettings();
354                                 // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
355                                 TemplateProperties.setColors(settings);
356                                 generateReport(f, settings);
357                         } catch (IOException e) {
358                                 ExceptionHandler.handleErrorCondition(e);
359                         }
360                         return true;
361                 } else {
362                         return false;
363                 }
364         }
365         
366         /**
367          * Get the extension of a file.
368          */
369         private static String getExtension(String s) {
370                 String ext = null;
371                 int i = s.lastIndexOf('.');
372                 
373                 if (i > 0 && i < s.length() - 1) {
374                         ext = s.substring(i + 1).toLowerCase();
375                 }
376                 return ext != null ? ext : "";
377         }
378 }