001: /*BEGIN_COPYRIGHT_BLOCK
002: *
003: * Copyright (c) 2001-2007, JavaPLT group at Rice University (javaplt@rice.edu)
004: * All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions are met:
008: * * Redistributions of source code must retain the above copyright
009: * notice, this list of conditions and the following disclaimer.
010: * * Redistributions in binary form must reproduce the above copyright
011: * notice, this list of conditions and the following disclaimer in the
012: * documentation and/or other materials provided with the distribution.
013: * * Neither the names of DrJava, the JavaPLT group, Rice University, nor the
014: * names of its contributors may be used to endorse or promote products
015: * derived from this software without specific prior written permission.
016: *
017: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
018: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
019: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
020: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
021: * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
022: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
023: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
024: * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
025: * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
026: * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
027: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
028: *
029: * This software is Open Source Initiative approved Open Source Software.
030: * Open Source Initative Approved is a trademark of the Open Source Initiative.
031: *
032: * This file is part of DrJava. Download the current version of this project
033: * from http://www.drjava.org/ or http://sourceforge.net/projects/drjava/
034: *
035: * END_COPYRIGHT_BLOCK*/
036:
037: package edu.rice.cs.drjava.ui;
038:
039: import javax.swing.*;
040: import javax.swing.event.*;
041: import javax.swing.border.MatteBorder;
042: import javax.swing.border.EmptyBorder;
043: import java.awt.event.*;
044: import java.awt.*;
045: import java.awt.print.*;
046: import java.awt.image.*;
047: import java.net.*;
048: import java.lang.reflect.Method;
049: import edu.rice.cs.drjava.model.*;
050:
051: /** DrJava's print preview window
052: * @version $Id: PreviewFrame.java 4255 2007-08-28 19:17:37Z mgricken $
053: */
054: public abstract class PreviewFrame extends JFrame {
055:
056: protected final SingleDisplayModel _model;
057: protected final MainFrame _mainFrame;
058: protected final Pageable _print;
059: protected volatile int _pageNumber;
060:
061: // private JTextField _pageTextField = new JTextField("" + (_pageNumber + 1), 2) {
062: // public Dimension getMaximumSize() {
063: // return getPreferredSize();
064: // }
065: // };
066:
067: private final PageChangerUpdater _pageChanger;
068:
069: private static abstract class PageChangerUpdater {
070: abstract void update(int pageNumber) throws Exception;
071:
072: abstract JComponent getComponent();
073: }
074:
075: private class JTextFieldChanger extends PageChangerUpdater {
076: private final JTextField textfield;
077:
078: private JTextFieldChanger(JTextField tf) {
079: textfield = tf;
080: }
081:
082: void update(int pageNumber) throws Exception {
083: textfield.setText(String.valueOf(pageNumber));
084: }
085:
086: JComponent getComponent() {
087: return textfield;
088: }
089: }
090:
091: private class JSpinnerChanger extends PageChangerUpdater {
092: private volatile JComponent spinner;
093: private volatile Method setValueMethod;
094: private final Object[] args = new Object[1];
095:
096: private JSpinnerChanger(Class<?> spinnerClass,
097: JComponent spinnerObj) throws Exception {
098: spinner = spinnerObj;
099: setValueMethod = spinnerClass.getMethod("setValue",
100: Object.class);
101: }
102:
103: void update(int pageNumber) throws Exception {
104: args[0] = new Integer(pageNumber);
105: setValueMethod.invoke(spinner, args);
106: }
107:
108: JComponent getComponent() {
109: return spinner;
110: }
111: }
112:
113: // Print Preview Dimensions
114: private final int PREVIEW_WIDTH;
115: private final int PREVIEW_HEIGHT;
116: private final int PREVIEW_PAGE_WIDTH;
117: private final int PREVIEW_PAGE_HEIGHT;
118:
119: private final double PAGE_ZOOM = 0.7;
120: private final static int PAGE_BORDER = 20;
121: private final int TOOLBAR_HEIGHT = 35;
122: private static final String ICON_PATH = "/edu/rice/cs/drjava/ui/icons/";
123:
124: // Components
125: private JToolBar _toolBar;
126: private PagePreview _pagePreview;
127:
128: // Actions
129: /** Prints the current document. */
130: private final ActionListener _printListener = new ActionListener() {
131: public void actionPerformed(ActionEvent ae) {
132: _print();
133: _close();
134: }
135: };
136:
137: /** Prints the current document. */
138: private final Action _closeAction = new AbstractAction("Close") {
139: public void actionPerformed(ActionEvent ae) {
140: _close();
141: }
142: };
143:
144: /** Displays the next page of the document. */
145: private final Action _nextPageAction = new AbstractAction(
146: "Next Page") {
147: public void actionPerformed(ActionEvent ae) {
148: _nextPage();
149: }
150: };
151:
152: /** Displays the previous page of the document. */
153: private final Action _prevPageAction = new AbstractAction(
154: "Previous Page") {
155: public void actionPerformed(ActionEvent ae) {
156: _previousPage();
157: }
158: };
159:
160: /** How Preview Pane responds to window events. */
161: private final WindowListener _windowCloseListener = new WindowAdapter() {
162: public void windowClosing(WindowEvent ev) {
163: _close();
164: }
165: };
166:
167: /** Contructs a new PreviewFrame using a parent model and a Pageable object print to show. Should only be called in
168: * event thread.
169: */
170: public PreviewFrame(SingleDisplayModel model, MainFrame mainFrame,
171: boolean interactions) throws IllegalStateException {
172: super ("Print Preview");
173: mainFrame.hourglassOn();
174: _model = model;
175: _mainFrame = mainFrame;
176: _toolBar = new JToolBar();
177: _print = setUpDocument(model, interactions);
178: _pageChanger = createPageChanger();
179:
180: /* Initialize constants. */
181: PageFormat first = _print.getPageFormat(0);
182:
183: PREVIEW_PAGE_WIDTH = (int) (PAGE_ZOOM * first.getWidth());
184: PREVIEW_PAGE_HEIGHT = (int) (PAGE_ZOOM * first.getHeight());
185:
186: PREVIEW_WIDTH = PREVIEW_PAGE_WIDTH + (2 * PAGE_BORDER);
187: PREVIEW_HEIGHT = PREVIEW_PAGE_HEIGHT + (2 * PAGE_BORDER)
188: + TOOLBAR_HEIGHT;
189:
190: _setUpActions();
191: _setUpToolBar();
192:
193: _pagePreview = new PagePreview(PREVIEW_PAGE_WIDTH,
194: PREVIEW_PAGE_HEIGHT);
195: _pageNumber = 0;
196:
197: PagePreviewContainer ppc = new PagePreviewContainer();
198: ppc.add(_pagePreview);
199: JPanel tbCont = new JPanel(new BorderLayout());
200: JPanel cp = new JPanel(new BorderLayout());
201: tbCont.add(_toolBar, BorderLayout.NORTH);
202: tbCont.add(Box.createVerticalStrut(10), BorderLayout.SOUTH);
203: tbCont.setBorder(new EmptyBorder(0, 0, 5, 0));
204: setContentPane(cp);
205: cp.setBorder(new EmptyBorder(5, 5, 5, 5));
206: cp.add(tbCont, BorderLayout.NORTH);
207: cp.add(ppc, BorderLayout.SOUTH);
208:
209: addWindowListener(_windowCloseListener);
210:
211: showPage();
212: _updateActions();
213:
214: setSize(PREVIEW_WIDTH, PREVIEW_HEIGHT);
215: setDefaultCloseOperation(DISPOSE_ON_CLOSE);
216: setVisible(true);
217: }
218:
219: /** Prints the document being previewed */
220: abstract protected void _print();
221:
222: /** Sets up the document to be displayed and returns the Pageable object that allows display by pages
223: * @param model the current display model
224: * @param interactions whether the document is an interactions document
225: * @return a Pageable object that allows the document to be displayed by pages
226: */
227: abstract protected Pageable setUpDocument(SingleDisplayModel model,
228: boolean interactions);
229:
230: private void _close() {
231: dispose();
232: _mainFrame.hourglassOff();
233: }
234:
235: private void _nextPage() {
236: _pageNumber++;
237: _goToPage(_pageNumber);
238: }
239:
240: private void _previousPage() {
241: _pageNumber--;
242: _goToPage(_pageNumber);
243: }
244:
245: private void _goToPage(int pi) {
246: _pageNumber = pi;
247: showPage();
248: _updateActions();
249: }
250:
251: protected void _showError(Exception e, String title, String message) {
252: JOptionPane.showMessageDialog(this , message + "\n" + e, title,
253: JOptionPane.ERROR_MESSAGE);
254: }
255:
256: /** Updates all of the buttons on the page to reflect the current state of the PreviewWindows. Enables/Disables the
257: * page buttons, and updates the gotopage field.
258: */
259: private void _updateActions() {
260: _nextPageAction
261: .setEnabled(_print.getNumberOfPages() > (_pageNumber + 1));
262: _prevPageAction.setEnabled(_pageNumber > 0);
263: try {
264: _pageChanger.update(_pageNumber + 1);
265: } catch (Exception e) { /* ignore */
266: }
267: }
268:
269: /** Initializes all action objects. Adds icons and descriptions to several of the actions. */
270: private void _setUpActions() {
271: //_printAction.putValue(Action.SHORT_DESCRIPTION, "Print");
272: _closeAction.putValue(Action.SHORT_DESCRIPTION, "Close");
273: // _printAction.putValue(Action.SMALL_ICON, _getIcon("Print16.gif"));
274: _nextPageAction.putValue(Action.SMALL_ICON,
275: _getIcon("Forward16.gif"));
276: _nextPageAction.putValue(Action.SHORT_DESCRIPTION, "Next Page");
277: _prevPageAction.putValue(Action.SMALL_ICON,
278: _getIcon("Back16.gif"));
279: _prevPageAction.putValue(Action.SHORT_DESCRIPTION,
280: "Previous Page");
281: }
282:
283: private PageChangerUpdater createPageChanger() {
284: //_pageTextField.setAction(_goToPageAction);
285: // _goToPageAction.putValue(Action.SHORT_DESCRIPTION, "Goto Page");
286: try {
287: Class<?> spinnerClass = Class
288: .forName("javax.swing.JSpinner");
289: final JComponent spinner = (JComponent) spinnerClass
290: .newInstance();
291: final Method getter = spinnerClass.getMethod("getValue",
292: new Class[0]);
293: Object model = callMethod(spinner, spinnerClass,
294: "getModel", null, null);
295: Class<?> modelClass = model.getClass();
296: Class<?>[] ca = new Class<?>[] { Comparable.class };
297: Object[] aa = new Object[] { new Integer(1) };
298: callMethod(model, modelClass, "setMinimum", ca, aa);
299: aa[0] = new Integer(_print.getNumberOfPages());
300: callMethod(model, modelClass, "setMaximum", ca, aa);
301: ca[0] = ChangeListener.class;
302: aa[0] = new ChangeListener() {
303: public void stateChanged(ChangeEvent ev) {
304: int num = _pageNumber;
305: try {
306: num = ((Number) getter.invoke(spinner,
307: new Object[0])).intValue() - 1;
308: if ((num >= 0)
309: && (num < _print.getNumberOfPages()))
310: _goToPage(num);
311: else
312: _updateActions();
313: } catch (Exception ex) {
314: _updateActions();
315: }
316: }
317: };
318: callMethod(spinner, spinnerClass, "addChangeListener", ca,
319: aa);
320: return new JSpinnerChanger(spinnerClass, spinner);
321: } catch (Exception e) {
322: /** Displays the previous page of the document. */
323: final JTextField tf = new JTextField();
324: tf.addActionListener(new ActionListener() {
325: public void actionPerformed(ActionEvent ae) {
326: try {
327: int pageToGoTo = Integer.parseInt(tf.getText()) - 1;
328: if ((pageToGoTo < 0)
329: || (pageToGoTo >= _print
330: .getNumberOfPages())) {
331: _updateActions();
332: } else
333: _goToPage(pageToGoTo);
334: } catch (NumberFormatException e) {
335: _updateActions();
336: }
337: }
338: });
339: return new JTextFieldChanger(tf);
340: }
341: }
342:
343: private static Object callMethod(Object rec, Class<?> c,
344: String name, Class<?>[] ca, Object[] args) throws Exception {
345: Method m = c.getMethod(name, ca);
346: return m.invoke(rec, args);
347: }
348:
349: /** Mirrored from MainFrame, will later use the same Icon access code. */
350: private ImageIcon _getIcon(String name) {
351: URL url = PreviewFrame.class.getResource(ICON_PATH + name);
352: if (url != null)
353: return new ImageIcon(url);
354: return null;
355: }
356:
357: /** Sets up the toolbar with all of the necessary buttons. */
358: private void _setUpToolBar() {
359: _toolBar.setFloatable(false);
360:
361: // Print and Close buttons
362: JButton printButton = new JButton("Print...",
363: _getIcon("Print16.gif"));
364: printButton.setToolTipText("Print this document");
365: printButton.addActionListener(_printListener);
366: _toolBar.add(printButton);
367: _toolBar.addSeparator();
368: _toolBar.add(_closeAction);
369:
370: // Horizontal Gap
371: _toolBar.add(Box.createHorizontalGlue());
372:
373: // Navigation components
374: _toolBar.add(_prevPageAction);
375: _toolBar.add(_nextPageAction);
376: _toolBar.addSeparator();
377:
378: JLabel gotop = new JLabel("Page");
379:
380: JLabel of = new JLabel(" of " + _print.getNumberOfPages());
381:
382: _toolBar.add(gotop);
383: _toolBar.addSeparator();
384: JComponent c = _pageChanger.getComponent();
385: Dimension d = c.getPreferredSize();
386: d = new Dimension(100, d.height);
387: c.setMaximumSize(d);
388: c.setPreferredSize(d);
389: c.setMinimumSize(d);
390: c.setToolTipText("Goto Page");
391: _toolBar.add(c);
392: _toolBar.add(of);
393: }
394:
395: /** Generates an Image, prints to it, and then displays the image on the page. */
396: private void showPage() {
397: BufferedImage img = new BufferedImage((int) _model
398: .getPageFormat().getWidth(), (int) _model
399: .getPageFormat().getHeight(),
400: BufferedImage.TYPE_INT_RGB);
401: Graphics g = img.getGraphics();
402: g.setColor(Color.white);
403: g.fillRect(0, 0, (int) _model.getPageFormat().getWidth(),
404: (int) _model.getPageFormat().getHeight());
405:
406: try {
407: _print.getPrintable(_pageNumber).print(g,
408: _model.getPageFormat(), _pageNumber);
409: _pagePreview.setImage(img);
410: } catch (PrinterException e) { /* ignore */
411: }
412: }
413:
414: /** Internal class which holds (and places) the PagePreview object. */
415: class PagePreviewContainer extends JPanel {
416: public Dimension getPreferredSize() {
417: return getParent().getSize();
418: }
419:
420: /** Places the PagePreview component into the center of this object */
421: public void doLayout() {
422: Component cp = getComponent(0);
423:
424: Dimension dm = cp.getPreferredSize();
425: int Hindent = (int) (getPreferredSize().getWidth() - dm
426: .getWidth()) / 2;
427: int Vindent = TOOLBAR_HEIGHT
428: + (int) ((getPreferredSize().getHeight()
429: - dm.getHeight() - TOOLBAR_HEIGHT) / 2);
430: _pagePreview.setBounds(Hindent, Vindent, (int) dm
431: .getWidth(), (int) dm.getHeight());
432: }
433: }
434:
435: /** Static inner class which displays the image on the screen, and holds the Image object. */
436: static class PagePreview extends JPanel {
437: protected final int _width;
438: protected final int _height;
439: protected volatile Image _source;
440: protected volatile Image _image;
441:
442: /** Constructs a PagePreview object with given width and height. */
443: public PagePreview(int width, int height) {
444: super ();
445: _width = width;
446: _height = height;
447: setBorder(new MatteBorder(1, 1, 2, 2, Color.black));
448: setBackground(Color.white);
449: }
450:
451: /** Scales the interal image to the appropriate size. */
452: protected void updateScaled() {
453: _image = _source.getScaledInstance(_width, _height,
454: Image.SCALE_SMOOTH);
455: _image.flush();
456: }
457:
458: /** Updates the image of this PagePreview.
459: * @param i The Image to place and show.
460: */
461: public void setImage(Image i) {
462: _source = i;
463: updateScaled();
464: repaint();
465: }
466:
467: public Dimension getPreferredSize() {
468: return new Dimension(_width, _height);
469: }
470:
471: public Dimension getMaximumSize() {
472: return getPreferredSize();
473: }
474:
475: public Dimension getMinimumSize() {
476: return getPreferredSize();
477: }
478:
479: public void paint(Graphics g) {
480: g.setColor(getBackground());
481: g.fillRect(0, 0, _width, _height);
482: g.drawImage(_image, 0, 0, this);
483: paintBorder(g);
484: }
485: }
486: }
|