001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /* $Id: PreviewDialog.java 554240 2007-07-07 18:42:56Z adelmelle $ */
019:
020: // Originally contributed by:
021: // Juergen Verwohlt: Juergen.Verwohlt@jCatalog.com,
022: // Rainer Steinkuhle: Rainer.Steinkuhle@jCatalog.com,
023: // Stanislav Gorkhover: Stanislav.Gorkhover@jCatalog.com
024: package org.apache.fop.render.awt.viewer;
025:
026: // Java
027: import java.awt.BorderLayout;
028: import java.awt.Dimension;
029: import java.awt.GridBagConstraints;
030: import java.awt.GridBagLayout;
031: import java.awt.Insets;
032: import java.awt.Point;
033: import java.awt.Toolkit;
034: import java.awt.event.ActionEvent;
035: import java.awt.event.ActionListener;
036: import java.awt.event.KeyEvent;
037: import java.awt.event.WindowAdapter;
038: import java.awt.event.WindowEvent;
039: import java.awt.print.PrinterException;
040: import java.awt.print.PrinterJob;
041:
042: import java.text.DecimalFormat;
043: import java.text.DecimalFormatSymbols;
044: import java.util.Locale;
045:
046: import javax.swing.ActionMap;
047: import javax.swing.BorderFactory;
048: import javax.swing.ButtonGroup;
049: import javax.swing.InputMap;
050: import javax.swing.JComboBox;
051: import javax.swing.JComponent;
052: import javax.swing.JFrame;
053: import javax.swing.JLabel;
054: import javax.swing.JMenu;
055: import javax.swing.JMenuBar;
056: import javax.swing.JOptionPane;
057: import javax.swing.JPanel;
058: import javax.swing.JRadioButtonMenuItem;
059: import javax.swing.JToolBar;
060: import javax.swing.KeyStroke;
061: import javax.swing.SwingUtilities;
062:
063: import org.apache.fop.apps.FOUserAgent;
064: import org.apache.fop.apps.FOPException;
065: import org.apache.fop.render.awt.AWTRenderer;
066:
067: /**
068: * AWT Viewer main window.
069: * Surrounds a PreviewPanel with a bunch of pretty buttons and controls.
070: */
071: public class PreviewDialog extends JFrame implements StatusListener {
072:
073: /** The Translator for localization */
074: protected Translator translator;
075: /** The AWT renderer */
076: protected AWTRenderer renderer;
077: /** The FOUserAgent associated with this window */
078: protected FOUserAgent foUserAgent;
079: /**
080: * Renderable instance that can be used to reload and re-render a document after
081: * modifications.
082: */
083: protected Renderable renderable;
084:
085: /** The JCombobox to rescale the rendered page view */
086: private JComboBox scale;
087:
088: /** The JLabel for the process status bar */
089: private JLabel processStatus;
090:
091: /** The JLabel information status bar */
092: private JLabel infoStatus;
093:
094: /** The main display area */
095: private PreviewPanel previewPanel;
096:
097: /** Formats the text in the scale combobox. */
098: private DecimalFormat percentFormat = new DecimalFormat("###0.0#",
099: new DecimalFormatSymbols(Locale.ENGLISH));
100:
101: /**
102: * Creates a new PreviewDialog that uses the given renderer.
103: * @param foUserAgent the user agent
104: * @param renderable the Renderable instance that is used to reload/re-render a document
105: * after modifications.
106: */
107: public PreviewDialog(FOUserAgent foUserAgent, Renderable renderable) {
108: renderer = (AWTRenderer) foUserAgent.getRendererOverride();
109: this .foUserAgent = foUserAgent;
110: this .renderable = renderable;
111: translator = new Translator();
112:
113: //Commands aka Actions
114: Command printAction = new Command(translator
115: .getString("Menu.Print"), "Print") {
116: public void doit() {
117: startPrinterJob(true);
118: }
119: };
120: Command firstPageAction = new Command(translator
121: .getString("Menu.First.page"), "firstpg") {
122: public void doit() {
123: goToFirstPage();
124: }
125: };
126: Command previousPageAction = new Command(translator
127: .getString("Menu.Prev.page"), "prevpg") {
128: public void doit() {
129: goToPreviousPage();
130: }
131: };
132: Command nextPageAction = new Command(translator
133: .getString("Menu.Next.page"), "nextpg") {
134: public void doit() {
135: goToNextPage();
136: }
137:
138: };
139: Command lastPageAction = new Command(translator
140: .getString("Menu.Last.page"), "lastpg") {
141: public void doit() {
142: goToLastPage();
143: }
144: };
145: Command reloadAction = new Command(translator
146: .getString("Menu.Reload"), "reload") {
147: public void doit() {
148: previewPanel.reload();
149: }
150: };
151: Command debugAction = new Command(translator
152: .getString("Menu.Debug"), "debug") {
153: // TODO use Translator
154: public void doit() {
155: previewPanel.debug();
156: }
157: };
158: Command aboutAction = new Command(translator
159: .getString("Menu.About"), "fopLogo") {
160: public void doit() {
161: startHelpAbout();
162: }
163: };
164:
165: setTitle("FOP: AWT-" + translator.getString("Title.Preview"));
166: setDefaultCloseOperation(DISPOSE_ON_CLOSE);
167:
168: //Sets size to be 61%x90% of the screen size
169: Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
170: // Needed due to bug in Sun's JVM 1.5 (6429775)
171: pack();
172: //Rather frivolous size - fits A4 page width in 1024x768 screen on my desktop
173: setSize(screen.width * 61 / 100, screen.height * 9 / 10);
174:
175: //Page view stuff
176: previewPanel = new PreviewPanel(foUserAgent, renderable,
177: renderer);
178: getContentPane().add(previewPanel, BorderLayout.CENTER);
179:
180: // Keyboard shortcuts - pgup/pgdn
181: InputMap im = previewPanel
182: .getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
183: ActionMap am = previewPanel.getActionMap();
184: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_DOWN, 0),
185: "nextPage");
186: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_PAGE_UP, 0),
187: "prevPage");
188: im
189: .put(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0),
190: "firstPage");
191: im.put(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0), "lastPage");
192: previewPanel.getActionMap().put("nextPage", nextPageAction);
193: previewPanel.getActionMap().put("prevPage", previousPageAction);
194: previewPanel.getActionMap().put("firstPage", firstPageAction);
195: previewPanel.getActionMap().put("lastPage", lastPageAction);
196:
197: //Scaling combobox
198: scale = new JComboBox();
199: scale.addItem(translator.getString("Menu.Fit.Window"));
200: scale.addItem(translator.getString("Menu.Fit.Width"));
201: scale.addItem("25%");
202: scale.addItem("50%");
203: scale.addItem("75%");
204: scale.addItem("100%");
205: scale.addItem("150%");
206: scale.addItem("200%");
207: scale.setMaximumSize(new Dimension(80, 24));
208: scale.setPreferredSize(new Dimension(80, 24));
209: scale.setSelectedItem("100%");
210: scale.setEditable(true);
211: scale.addActionListener(new ActionListener() {
212: public void actionPerformed(ActionEvent e) {
213: scaleActionPerformed(e);
214: }
215: });
216:
217: //Menu
218: setJMenuBar(setupMenu());
219:
220: // Toolbar
221: JToolBar toolBar = new JToolBar();
222: toolBar.add(printAction);
223: toolBar.add(reloadAction);
224: toolBar.addSeparator();
225: toolBar.add(firstPageAction);
226: toolBar.add(previousPageAction);
227: toolBar.add(nextPageAction);
228: toolBar.add(lastPageAction);
229: toolBar.addSeparator(new Dimension(20, 0));
230: toolBar
231: .add(new JLabel(translator.getString("Menu.Zoom") + " "));
232: toolBar.add(scale);
233: toolBar.addSeparator();
234: toolBar.add(debugAction);
235: toolBar.addSeparator();
236: toolBar.add(aboutAction);
237: getContentPane().add(toolBar, BorderLayout.NORTH);
238:
239: // Status bar
240: JPanel statusBar = new JPanel();
241: processStatus = new JLabel();
242: processStatus.setBorder(BorderFactory.createCompoundBorder(
243: BorderFactory.createEtchedBorder(), BorderFactory
244: .createEmptyBorder(0, 3, 0, 0)));
245: infoStatus = new JLabel();
246: infoStatus.setBorder(BorderFactory.createCompoundBorder(
247: BorderFactory.createEtchedBorder(), BorderFactory
248: .createEmptyBorder(0, 3, 0, 0)));
249:
250: statusBar.setLayout(new GridBagLayout());
251:
252: processStatus.setPreferredSize(new Dimension(200, 21));
253: processStatus.setMinimumSize(new Dimension(200, 21));
254:
255: infoStatus.setPreferredSize(new Dimension(100, 21));
256: infoStatus.setMinimumSize(new Dimension(100, 21));
257: statusBar.add(processStatus, new GridBagConstraints(0, 0, 1, 0,
258: 2.0, 0.0, GridBagConstraints.CENTER,
259: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 3),
260: 0, 0));
261: statusBar.add(infoStatus, new GridBagConstraints(1, 0, 1, 0,
262: 1.0, 0.0, GridBagConstraints.CENTER,
263: GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0),
264: 0, 0));
265: getContentPane().add(statusBar, BorderLayout.SOUTH);
266: }
267:
268: /**
269: * Creates and initialize the AWT Viewer main window.
270: * @param foUserAgent the FO user agent
271: * @param renderable the target for the rendering
272: * @return the newly initialized preview dialog
273: */
274: public static PreviewDialog createPreviewDialog(
275: FOUserAgent foUserAgent, Renderable renderable,
276: boolean asMainWindow) {
277: PreviewDialog frame = new PreviewDialog(foUserAgent, renderable);
278:
279: if (asMainWindow) {
280: frame.addWindowListener(new WindowAdapter() {
281: public void windowClosed(WindowEvent we) {
282: System.exit(0);
283: }
284: });
285: }
286:
287: // Centers the window
288: Dimension screenSize = Toolkit.getDefaultToolkit()
289: .getScreenSize();
290: Dimension frameSize = frame.getSize();
291: if (frameSize.height > screenSize.height) {
292: frameSize.height = screenSize.height;
293: }
294: if (frameSize.width > screenSize.width) {
295: frameSize.width = screenSize.width;
296: }
297: frame.setLocation((screenSize.width - frameSize.width) / 2,
298: (screenSize.height - frameSize.height) / 2);
299: frame.setStatus(frame.translator
300: .getString("Status.Build.FO.tree"));
301: frame.setVisible(true);
302: return frame;
303: }
304:
305: /**
306: * Creates a new PreviewDialog that uses the given renderer.
307: * @param foUserAgent the user agent
308: */
309: public PreviewDialog(FOUserAgent foUserAgent) {
310: this (foUserAgent, null);
311: }
312:
313: /**
314: * Creates a new menubar to be shown in this window.
315: * @return the newly created menubar
316: */
317: private JMenuBar setupMenu() {
318: JMenuBar menuBar = new JMenuBar();
319: JMenu menu = new JMenu(translator.getString("Menu.File"));
320: menu.setMnemonic(KeyEvent.VK_F);
321: //Adds mostly the same actions, but without icons
322: menu.add(new Command(translator.getString("Menu.Print"),
323: KeyEvent.VK_P) {
324: public void doit() {
325: startPrinterJob(true);
326: }
327: });
328: // inputHandler must be set to allow reloading
329: if (renderable != null) {
330: menu.add(new Command(translator.getString("Menu.Reload"),
331: KeyEvent.VK_R) {
332: public void doit() {
333: reload();
334: }
335: });
336: }
337: menu.addSeparator();
338: menu.add(new Command(translator.getString("Menu.Exit"),
339: KeyEvent.VK_X) {
340: public void doit() {
341: dispose();
342: }
343: });
344: menuBar.add(menu);
345:
346: menu = new JMenu(translator.getString("Menu.View"));
347: menu.setMnemonic(KeyEvent.VK_V);
348: menu.add(new Command(translator.getString("Menu.First.page"),
349: KeyEvent.VK_F) {
350: public void doit() {
351: goToFirstPage();
352: }
353: });
354: menu.add(new Command(translator.getString("Menu.Prev.page"),
355: KeyEvent.VK_P) {
356: public void doit() {
357: goToPreviousPage();
358: }
359: });
360: menu.add(new Command(translator.getString("Menu.Next.page"),
361: KeyEvent.VK_N) {
362: public void doit() {
363: goToNextPage();
364: }
365: });
366: menu.add(new Command(translator.getString("Menu.Last.page"),
367: KeyEvent.VK_L) {
368: public void doit() {
369: goToLastPage();
370: }
371: });
372: menu.add(new Command(translator.getString("Menu.Go.to.Page"),
373: KeyEvent.VK_G) {
374: public void doit() {
375: showGoToPageDialog();
376: }
377: });
378: menu.addSeparator();
379: JMenu subMenu = new JMenu(translator.getString("Menu.Zoom"));
380: subMenu.setMnemonic(KeyEvent.VK_Z);
381: subMenu.add(new Command("25%", 0) {
382: public void doit() {
383: setScale(25.0);
384: }
385: });
386: subMenu.add(new Command("50%", 0) {
387: public void doit() {
388: setScale(50.0);
389: }
390: });
391: subMenu.add(new Command("75%", 0) {
392: public void doit() {
393: setScale(75.0);
394: }
395: });
396: subMenu.add(new Command("100%", 0) {
397: public void doit() {
398: setScale(100.0);
399: }
400: });
401: subMenu.add(new Command("150%", 0) {
402: public void doit() {
403: setScale(150.0);
404: }
405: });
406: subMenu.add(new Command("200%", 0) {
407: public void doit() {
408: setScale(200.0);
409: }
410: });
411: menu.add(subMenu);
412: menu.addSeparator();
413: menu.add(new Command(translator.getString("Menu.Default.zoom"),
414: KeyEvent.VK_D) {
415: public void doit() {
416: setScale(100.0);
417: }
418: });
419: menu.add(new Command(translator.getString("Menu.Fit.Window"),
420: KeyEvent.VK_F) {
421: public void doit() {
422: setScaleToFitWindow();
423: }
424: });
425: menu.add(new Command(translator.getString("Menu.Fit.Width"),
426: KeyEvent.VK_W) {
427: public void doit() {
428: setScaleToFitWidth();
429: }
430: });
431: menu.addSeparator();
432:
433: ButtonGroup group = new ButtonGroup();
434: JRadioButtonMenuItem single = new JRadioButtonMenuItem(
435: new Command(translator.getString("Menu.Single"),
436: KeyEvent.VK_S) {
437: public void doit() {
438: previewPanel
439: .setDisplayMode(PreviewPanel.SINGLE);
440: }
441: });
442: JRadioButtonMenuItem cont = new JRadioButtonMenuItem(
443: new Command(translator.getString("Menu.Continuous"),
444: KeyEvent.VK_C) {
445: public void doit() {
446: previewPanel
447: .setDisplayMode(PreviewPanel.CONTINUOUS);
448: }
449: });
450: JRadioButtonMenuItem facing = new JRadioButtonMenuItem(
451: new Command(translator.getString("Menu.Facing"), 0) {
452: public void doit() {
453: previewPanel
454: .setDisplayMode(PreviewPanel.CONT_FACING);
455: }
456: });
457: single.setSelected(true);
458: group.add(single);
459: group.add(cont);
460: group.add(facing);
461: menu.add(single);
462: menu.add(cont);
463: menu.add(facing);
464:
465: menuBar.add(menu);
466:
467: menu = new JMenu(translator.getString("Menu.Help"));
468: menu.setMnemonic(KeyEvent.VK_H);
469: menu.add(new Command(translator.getString("Menu.About"),
470: KeyEvent.VK_A) {
471: public void doit() {
472: startHelpAbout();
473: }
474: });
475: menuBar.add(menu);
476: return menuBar;
477: }
478:
479: /** @see org.apache.fop.render.awt.viewer.StatusListener#notifyRendererStopped() */
480: public void notifyRendererStopped() {
481: reload();
482: }
483:
484: private void reload() {
485: setStatus(translator.getString("Status.Show"));
486: previewPanel.reload();
487: }
488:
489: /**
490: * Changes the current visible page
491: * @param number the page number to go to
492: */
493: public void goToPage(int number) {
494: if (number != previewPanel.getPage()) {
495: previewPanel.setPage(number);
496: notifyPageRendered();
497: }
498: }
499:
500: /**
501: * Shows the previous page.
502: */
503: public void goToPreviousPage() {
504: int page = previewPanel.getPage();
505: if (page > 0) {
506: goToPage(page - 1);
507: }
508: }
509:
510: /**
511: * Shows the next page.
512: */
513: public void goToNextPage() {
514: int page = previewPanel.getPage();
515: if (page < renderer.getNumberOfPages() - 1) {
516: goToPage(page + 1);
517: }
518: }
519:
520: /** Shows the first page. */
521: public void goToFirstPage() {
522: goToPage(0);
523: }
524:
525: /**
526: * Shows the last page.
527: */
528: public void goToLastPage() {
529: goToPage(renderer.getNumberOfPages() - 1);
530: }
531:
532: /** Shows the About box */
533: private void startHelpAbout() {
534: PreviewDialogAboutBox dlg = new PreviewDialogAboutBox(this ,
535: translator);
536: //Centers the box
537: Dimension dlgSize = dlg.getPreferredSize();
538: Dimension frmSize = getSize();
539: Point loc = getLocation();
540: dlg.setLocation((frmSize.width - dlgSize.width) / 2 + loc.x,
541: (frmSize.height - dlgSize.height) / 2 + loc.y);
542: dlg.setVisible(true);
543: }
544:
545: /**
546: * Shows "go to page" dialog and then goes to the selected page
547: */
548: private void showGoToPageDialog() {
549: int currentPage = previewPanel.getPage();
550: GoToPageDialog d = new GoToPageDialog(this , translator
551: .getString("Menu.Go.to.Page"), translator);
552: d.setLocation((int) getLocation().getX() + 50,
553: (int) getLocation().getY() + 50);
554: d.setVisible(true);
555: currentPage = d.getPageNumber();
556: if (currentPage < 1
557: || currentPage > renderer.getNumberOfPages()) {
558: return;
559: }
560: currentPage--;
561: goToPage(currentPage);
562: }
563:
564: /** Scales page image */
565: public void setScale(double scaleFactor) {
566: scale.setSelectedItem(percentFormat.format(scaleFactor) + "%");
567: previewPanel.setScaleFactor(scaleFactor / 100d);
568: }
569:
570: public void setScaleToFitWindow() {
571: try {
572: setScale(previewPanel.getScaleToFitWindow() * 100);
573: } catch (FOPException fopEx) {
574: fopEx.printStackTrace();
575: }
576: }
577:
578: public void setScaleToFitWidth() {
579: try {
580: setScale(previewPanel.getScaleToFitWidth() * 100);
581: } catch (FOPException fopEx) {
582: fopEx.printStackTrace();
583: }
584: }
585:
586: private void scaleActionPerformed(ActionEvent e) {
587: try {
588: int index = scale.getSelectedIndex();
589: if (index == 0) {
590: setScale(previewPanel.getScaleToFitWindow() * 100);
591: } else if (index == 1) {
592: setScale(previewPanel.getScaleToFitWidth() * 100);
593: } else {
594: String item = (String) scale.getSelectedItem();
595: setScale(Double.parseDouble(item.substring(0, item
596: .indexOf('%'))));
597: }
598: } catch (FOPException fopEx) {
599: fopEx.printStackTrace();
600: }
601: }
602:
603: /** Prints the document */
604: public void startPrinterJob(boolean showDialog) {
605: PrinterJob pj = PrinterJob.getPrinterJob();
606: pj.setPageable(renderer);
607: if (!showDialog || pj.printDialog()) {
608: try {
609: pj.print();
610: } catch (PrinterException e) {
611: e.printStackTrace();
612: }
613: }
614: }
615:
616: /**
617: * Sets message to be shown in the status bar in a thread safe way.
618: * @param message the message
619: */
620: public void setStatus(String message) {
621: SwingUtilities.invokeLater(new ShowStatus(message));
622: }
623:
624: /** This class is used to show status in a thread safe way. */
625: private class ShowStatus implements Runnable {
626:
627: /** The message to display */
628: private String message;
629:
630: /**
631: * Constructs ShowStatus thread
632: * @param message message to display
633: */
634: public ShowStatus(String message) {
635: this .message = message;
636: }
637:
638: public void run() {
639: processStatus.setText(message.toString());
640: }
641: }
642:
643: /**
644: * Updates the message to be shown in the info bar in a thread safe way.
645: */
646: public void notifyPageRendered() {
647: SwingUtilities.invokeLater(new ShowInfo());
648: }
649:
650: /** This class is used to show info in a thread safe way. */
651: private class ShowInfo implements Runnable {
652:
653: public void run() {
654: String message = translator.getString("Status.Page") + " "
655: + (previewPanel.getPage() + 1) + " "
656: + translator.getString("Status.of") + " "
657: + (renderer.getNumberOfPages());
658: infoStatus.setText(message);
659: }
660: }
661:
662: /**
663: * Opens standard Swing error dialog box and reports given exception details.
664: * @param e the Exception
665: */
666: public void reportException(Exception e) {
667: String msg = translator.getString("Exception.Occured");
668: setStatus(msg);
669: JOptionPane.showMessageDialog(getContentPane(), "<html><b>"
670: + msg + ":</b><br>" + e.getClass().getName() + "<br>"
671: + e.getMessage() + "</html>", translator
672: .getString("Exception.Error"),
673: JOptionPane.ERROR_MESSAGE);
674: }
675: }
|