001: /*
002: * PrintPreview.java
003: *
004: * This file is part of SQL Workbench/J, http://www.sql-workbench.net
005: *
006: * Copyright 2002-2008, Thomas Kellerer
007: * No part of this code maybe reused without the permission of the author
008: *
009: * To contact the author please send an email to: support@sql-workbench.net
010: *
011: */
012: package workbench.print;
013:
014: import java.awt.BasicStroke;
015: import java.awt.BorderLayout;
016: import java.awt.Color;
017: import java.awt.Component;
018: import java.awt.Cursor;
019: import java.awt.Dimension;
020: import java.awt.EventQueue;
021: import java.awt.Font;
022: import java.awt.FontMetrics;
023: import java.awt.Graphics;
024: import java.awt.Graphics2D;
025: import java.awt.Image;
026: import java.awt.Insets;
027: import java.awt.Stroke;
028: import java.awt.event.ActionEvent;
029: import java.awt.event.ActionListener;
030: import java.awt.event.WindowEvent;
031: import java.awt.event.WindowListener;
032: import java.awt.image.BufferedImage;
033: import java.awt.print.PageFormat;
034: import java.awt.print.Printable;
035: import java.awt.print.PrinterException;
036: import java.awt.print.PrinterJob;
037:
038: import javax.print.attribute.PrintRequestAttributeSet;
039: import javax.swing.JButton;
040: import javax.swing.JComboBox;
041: import javax.swing.JDialog;
042: import javax.swing.JFrame;
043: import javax.swing.JPanel;
044: import javax.swing.JScrollPane;
045: import javax.swing.border.MatteBorder;
046: import workbench.WbManager;
047:
048: import workbench.gui.WbSwingUtilities;
049: import workbench.gui.components.WbFontChooser;
050: import workbench.gui.components.WbToolbar;
051: import workbench.gui.components.WbToolbarButton;
052: import workbench.log.LogMgr;
053: import workbench.resource.ResourceMgr;
054: import workbench.resource.Settings;
055: import workbench.util.WbThread;
056:
057: public class PrintPreview extends JDialog implements ActionListener,
058: WindowListener {
059: protected int pageWidth;
060: protected int pageHeight;
061: private int scale = 100;
062: protected TablePrinter printTarget;
063: protected JComboBox cbZoom;
064: private JButton pageSetupButton;
065: private JButton printButton;
066: private JButton chooseFontButton;
067: private JButton closeButton;
068: private JButton pageRight;
069: private JButton pageLeft;
070: private JButton pageDown;
071: private JButton pageUp;
072: private boolean hasHorizontalPages;
073: private JScrollPane scroll;
074: protected PreviewContainer preview;
075: private PagePreview pageDisplay;
076: private int currentPage = 0;
077:
078: public PrintPreview(JFrame owner, TablePrinter target) {
079: super (owner, ResourceMgr
080: .getString("TxtPrintPreviewWindowTitle"), true);
081:
082: if (!Settings.getInstance().restoreWindowSize(this )) {
083: setSize(500, 600);
084: }
085: if (!Settings.getInstance().restoreWindowPosition(this )) {
086: WbSwingUtilities.center(this , owner);
087: }
088: getContentPane().setLayout(new BorderLayout());
089: this .printTarget = target;
090:
091: WbToolbar tb = new WbToolbar();
092: tb.addDefaultBorder();
093:
094: this .printButton = new WbToolbarButton(ResourceMgr
095: .getString("LblPrintButton"));
096: this .printButton.addActionListener(this );
097: tb.add(printButton);
098:
099: tb.addSeparator();
100:
101: this .chooseFontButton = new WbToolbarButton(ResourceMgr
102: .getString("LblSelectPrintFont"));
103: this .chooseFontButton.addActionListener(this );
104: tb.add(this .chooseFontButton);
105:
106: tb.addSeparator();
107:
108: this .pageSetupButton = new WbToolbarButton(ResourceMgr
109: .getString("LblPageSetupButton"));
110: this .pageSetupButton.addActionListener(this );
111: tb.add(this .pageSetupButton);
112:
113: tb.addSeparator();
114:
115: this .pageDown = new WbToolbarButton(ResourceMgr
116: .getImage("Down"));
117: this .pageDown.addActionListener(this );
118: this .pageDown.setEnabled(false);
119: tb.add(this .pageDown);
120:
121: this .pageUp = new WbToolbarButton(ResourceMgr.getImage("Up"));
122: this .pageUp.addActionListener(this );
123: this .pageUp.setEnabled(false);
124: tb.add(this .pageUp);
125:
126: if (this .printTarget.getPagesAcross() > 1) {
127: this .hasHorizontalPages = true;
128:
129: this .pageLeft = new WbToolbarButton(ResourceMgr
130: .getImage("Back"));
131: this .pageLeft.addActionListener(this );
132: this .pageLeft.setEnabled(false);
133: tb.add(this .pageLeft);
134:
135: this .pageRight = new WbToolbarButton(ResourceMgr
136: .getImage("Forward"));
137: this .pageRight.addActionListener(this );
138: this .pageRight.setEnabled(false);
139: tb.add(this .pageRight);
140: }
141:
142: tb.addSeparator();
143:
144: String[] scales = { "10%", "25%", "50%", "100%", "150%" };
145: this .cbZoom = new JComboBox(scales);
146:
147: // for some reason the dropdown is extended
148: // to fill all available space in the toolbar
149: // so I'm restricting the max. size to a sensible value
150: Dimension pref = cbZoom.getPreferredSize();
151: Dimension d = new Dimension((int) pref.getWidth() + 10,
152: (int) pref.getHeight());
153: this .cbZoom.setMaximumSize(d);
154: this .cbZoom.setEditable(true);
155: this .cbZoom.setSelectedItem("100%");
156: this .cbZoom.addActionListener(this );
157: tb.add(this .cbZoom);
158: tb.addSeparator();
159:
160: this .closeButton = new WbToolbarButton(ResourceMgr
161: .getString("LblClose"));
162: this .closeButton.addActionListener(this );
163: tb.add(this .closeButton);
164:
165: getContentPane().add(tb, BorderLayout.NORTH);
166:
167: this .addWindowListener(this );
168:
169: this .preview = new PreviewContainer();
170: this .pageDisplay = new PagePreview();
171: this .preview.add(this .pageDisplay);
172: showCurrentPage();
173:
174: this .scroll = new JScrollPane(this .preview);
175: adjustScrollbar();
176:
177: getContentPane().add(scroll, BorderLayout.CENTER);
178:
179: setDefaultCloseOperation(DISPOSE_ON_CLOSE);
180: }
181:
182: private void selectPrintFont() {
183: final Font f = WbFontChooser.chooseFont(this .getRootPane(),
184: this .printTarget.getFont(), false, false);
185: if (f != null) {
186: Settings.getInstance().setPrintFont(f);
187: printTarget.setFont(f);
188: currentPage = 0;
189: Thread t = new WbThread("PrintPreview update font") {
190: public void run() {
191: invalidate();
192: showCurrentPage();
193: repaint();
194: }
195: };
196: t.start();
197: }
198: }
199:
200: private void adjustScrollbar() {
201: this .scroll.getVerticalScrollBar().setBlockIncrement(
202: (int) printTarget.getPageFormat().getImageableHeight());
203: Font f = this .printTarget.getFont();
204: FontMetrics fm = this .getFontMetrics(f);
205: this .scroll.getVerticalScrollBar().setUnitIncrement(
206: fm.getHeight());
207: }
208:
209: private void showCurrentPage() {
210: WbSwingUtilities.showWaitCursorOnWindow(this );
211: try {
212: PageFormat pageFormat = this .printTarget.getPageFormat();
213:
214: this .pageWidth = (int) pageFormat.getWidth();
215: this .pageHeight = (int) pageFormat.getHeight();
216:
217: int w = (this .pageWidth * this .scale / 100);
218: int h = (this .pageHeight * this .scale / 100);
219:
220: try {
221: BufferedImage img = new BufferedImage(pageWidth,
222: pageHeight, BufferedImage.TYPE_INT_RGB);
223: Graphics2D g = img.createGraphics();
224: g.setColor(Color.white);
225: g.fillRect(0, 0, pageWidth, pageHeight);
226: g.setColor(Color.LIGHT_GRAY);
227: Stroke s = g.getStroke();
228: g.setStroke(new BasicStroke(0.2f));
229: g.drawRect((int) pageFormat.getImageableX() - 1,
230: (int) pageFormat.getImageableY() - 1,
231: (int) pageFormat.getImageableWidth() + 1,
232: (int) pageFormat.getImageableHeight() + 1);
233: g.setStroke(s);
234: if (this .printTarget.print(g, pageFormat,
235: this .currentPage) == Printable.PAGE_EXISTS) {
236: this .pageDisplay.setImage(w, h, img);
237: }
238: } catch (PrinterException e) {
239: LogMgr.logError("PrintPreview.updateDisplay()",
240: "Error when creating preview", e);
241: WbSwingUtilities.showErrorMessage(this , ResourceMgr
242: .getString("MsgPrintPreviewError")
243: + "\n" + e.getMessage());
244: }
245: } catch (OutOfMemoryError e) {
246: WbManager.getInstance().showOutOfMemoryError();
247: this .pageDisplay.setImage(0, 0, null);
248: } finally {
249: WbSwingUtilities.repaintLater(this );
250: WbSwingUtilities.showDefaultCursorOnWindow(this );
251: }
252:
253: this .pageUp.setEnabled(this .printTarget
254: .getPreviousVerticalPage(this .currentPage) != -1);
255: this .pageDown.setEnabled(this .printTarget
256: .getNextVerticalPage(this .currentPage) != -1);
257:
258: if (this .hasHorizontalPages) {
259: this .pageLeft.setEnabled(this .printTarget
260: .getPreviousHorizontalPage(this .currentPage) != -1);
261: this .pageRight.setEnabled(this .printTarget
262: .getNextHorizontalPage(this .currentPage) != -1);
263: }
264: }
265:
266: public void doPrint() {
267: try {
268: PrinterJob prnJob = PrinterJob.getPrinterJob();
269: prnJob.setPrintable(this .printTarget, this .printTarget
270: .getPageFormat());
271: prnJob.setPageable(this .printTarget);
272:
273: if (!prnJob.printDialog()) {
274: return;
275: }
276: this .setCursor(Cursor
277: .getPredefinedCursor(Cursor.WAIT_CURSOR));
278: prnJob.print();
279: this .setCursor(Cursor
280: .getPredefinedCursor(Cursor.DEFAULT_CURSOR));
281: this .dispose();
282: } catch (PrinterException ex) {
283: ex.printStackTrace();
284: System.err.println("Printing error: " + ex.toString());
285: }
286: }
287:
288: private boolean pageDialogShowing = false;
289:
290: protected void showCrossPlatformPageSetup() {
291: if (pageDialogShowing) {
292: return;
293: }
294:
295: pageDialogShowing = true;
296: PrinterJob prnJob = PrinterJob.getPrinterJob();
297: PageFormat oldFormat = this .printTarget.getPageFormat();
298: PrintRequestAttributeSet attr = PrintUtil
299: .getPrintAttributes(oldFormat);
300: PageFormat newFormat = prnJob.pageDialog(attr);
301: pageDialogShowing = false;
302: applyNewPage(newFormat, oldFormat);
303: }
304:
305: protected void showNativePageSetup() {
306: if (pageDialogShowing) {
307: return;
308: }
309: pageDialogShowing = true;
310:
311: PrinterJob prnJob = PrinterJob.getPrinterJob();
312: PageFormat oldFormat = this .printTarget.getPageFormat();
313: PageFormat newFormat = prnJob.pageDialog(oldFormat);
314: pageDialogShowing = false;
315: applyNewPage(newFormat, oldFormat);
316: }
317:
318: protected void applyNewPage(final PageFormat newFormat,
319: final PageFormat oldFormat) {
320: if (newFormat == null)
321: return;
322:
323: if (!PrintUtil.pageFormatEquals(newFormat, oldFormat)) {
324: Settings.getInstance().setPageFormat(newFormat);
325: EventQueue.invokeLater(new Runnable() {
326: public void run() {
327: printTarget.setPageFormat(newFormat);
328: showCurrentPage();
329: invalidate();
330: preview.validate();
331: preview.doLayout();
332: }
333: });
334:
335: }
336: }
337:
338: public void doPageSetup() {
339: if (Settings.getInstance().getShowNativePageDialog()) {
340: // the native dialog is shown in it's own thread
341: // because otherwise the repainting of the preview window
342: // does not work properly
343: WbThread t = new WbThread("PageSetup Thread") {
344: public void run() {
345: showNativePageSetup();
346: }
347: };
348: t.start();
349: } else {
350: EventQueue.invokeLater(new Runnable() {
351: public void run() {
352: showCrossPlatformPageSetup();
353: }
354: });
355: }
356: }
357:
358: public void changeZoom() {
359: WbSwingUtilities.showWaitCursor(this );
360: try {
361: String str = cbZoom.getSelectedItem().toString();
362: if (str.endsWith("%")) {
363: str = str.substring(0, str.length() - 1);
364: }
365: str = str.trim();
366: try {
367: scale = Integer.parseInt(str);
368: } catch (NumberFormatException ex) {
369: return;
370: }
371: int w = (pageWidth * scale / 100);
372: int h = (pageHeight * scale / 100);
373:
374: Component[] comps = this .preview.getComponents();
375: for (int k = 0; k < comps.length; k++) {
376: if (!(comps[k] instanceof PagePreview)) {
377: continue;
378: }
379: PagePreview pp = (PagePreview) comps[k];
380: pp.setScaledSize(w, h);
381: }
382: } catch (Throwable th) {
383: LogMgr.logError("PrintPreview.changeZoom()",
384: "Error when changing the zoom factor", th);
385: } finally {
386: this .preview.validate();
387: this .preview.doLayout();
388: WbSwingUtilities.showDefaultCursor(this );
389: }
390: }
391:
392: public void actionPerformed(ActionEvent e) {
393: if (e.getSource() == this .printButton) {
394: this .doPrint();
395: } else if (e.getSource() == this .pageSetupButton) {
396: this .doPageSetup();
397: } else if (e.getSource() == this .chooseFontButton) {
398: this .selectPrintFont();
399: } else if (e.getSource() == this .cbZoom) {
400: Thread runner = new WbThread("PrintPreview Zoom thread") {
401: public void run() {
402: changeZoom();
403: }
404: };
405: runner.start();
406: } else if (e.getSource() == this .pageRight) {
407: int newIndex = this .printTarget
408: .getNextHorizontalPage(this .currentPage);
409: if (newIndex != -1) {
410: this .currentPage = newIndex;
411: }
412: this .showCurrentPage();
413: } else if (e.getSource() == this .pageLeft) {
414: int newIndex = this .printTarget
415: .getPreviousHorizontalPage(this .currentPage);
416: if (newIndex != -1) {
417: this .currentPage = newIndex;
418: }
419: this .showCurrentPage();
420: } else if (e.getSource() == this .pageUp) {
421: int newIndex = this .printTarget
422: .getPreviousVerticalPage(this .currentPage);
423: if (newIndex != -1) {
424: this .currentPage = newIndex;
425: this .showCurrentPage();
426: }
427: } else if (e.getSource() == this .pageDown) {
428: int newIndex = this .printTarget
429: .getNextVerticalPage(this .currentPage);
430: if (newIndex != -1) {
431: this .currentPage = newIndex;
432: this .showCurrentPage();
433: }
434: } else if (e.getSource() == this .closeButton) {
435: this .saveSettings();
436: this .dispose();
437: }
438: }
439:
440: public void saveSettings() {
441: Settings.getInstance().storeWindowSize(this );
442: Settings.getInstance().storeWindowPosition(this );
443: }
444:
445: public void windowActivated(WindowEvent e) {
446: }
447:
448: public void windowClosed(WindowEvent e) {
449: }
450:
451: public void windowClosing(WindowEvent e) {
452: this .saveSettings();
453: }
454:
455: public void windowDeactivated(WindowEvent e) {
456: }
457:
458: public void windowDeiconified(WindowEvent e) {
459: }
460:
461: public void windowIconified(WindowEvent e) {
462: }
463:
464: public void windowOpened(WindowEvent e) {
465: }
466:
467: static class PreviewContainer extends JPanel {
468: protected int H_GAP = 16;
469: protected int V_GAP = 10;
470:
471: public Dimension getPreferredSize() {
472: int n = getComponentCount();
473: if (n == 0) {
474: return new Dimension(H_GAP, V_GAP);
475: }
476: Component comp = getComponent(0);
477: Dimension dc = comp.getPreferredSize();
478: int w = dc.width;
479: int h = dc.height;
480:
481: Dimension dp = getParent().getSize();
482: int nCol = Math.max((dp.width - H_GAP) / (w + H_GAP), 1);
483: int nRow = n / nCol;
484: if (nRow * nCol < n) {
485: nRow++;
486: }
487:
488: int ww = nCol * (w + H_GAP) + H_GAP;
489: int hh = nRow * (h + V_GAP) + V_GAP;
490: Insets ins = getInsets();
491: return new Dimension(ww + ins.left + ins.right, hh
492: + ins.top + ins.bottom);
493: }
494:
495: public Dimension getMaximumSize() {
496: return getPreferredSize();
497: }
498:
499: public Dimension getMinimumSize() {
500: return getPreferredSize();
501: }
502:
503: public void doLayout() {
504: Insets ins = getInsets();
505: int x = ins.left + H_GAP;
506: int y = ins.top + V_GAP;
507:
508: int n = getComponentCount();
509: if (n == 0) {
510: return;
511: }
512: Component comp = getComponent(0);
513: Dimension dc = comp.getPreferredSize();
514: int w = dc.width;
515: int h = dc.height;
516:
517: Dimension dp = getParent().getSize();
518: int nCol = Math.max((dp.width - H_GAP) / (w + H_GAP), 1);
519: int nRow = n / nCol;
520: if (nRow * nCol < n) {
521: nRow++;
522: }
523:
524: int index = 0;
525: for (int k = 0; k < nRow; k++) {
526: for (int m = 0; m < nCol; m++) {
527: if (index >= n) {
528: return;
529: }
530: comp = getComponent(index++);
531: comp.setBounds(x, y, w, h);
532: x += w + H_GAP;
533: }
534: y += h + V_GAP;
535: x = ins.left + H_GAP;
536: }
537: }
538: }
539:
540: static class PagePreview extends JPanel {
541: protected int m_w;
542: protected int m_h;
543: protected Image m_source;
544: protected Image m_img;
545:
546: public PagePreview() {
547: }
548:
549: public PagePreview(int w, int h, Image source) {
550: this .setImage(w, h, source);
551: }
552:
553: public void setImage(int w, int h, Image source) {
554: m_w = w;
555: m_h = h;
556: m_source = source;
557: m_img = m_source.getScaledInstance(m_w, m_h,
558: Image.SCALE_SMOOTH);
559: m_img.flush();
560: setBackground(Color.WHITE);
561: setBorder(new MatteBorder(1, 1, 2, 2, Color.BLACK));
562: }
563:
564: public void setScaledSize(int w, int h) {
565: m_w = w;
566: m_h = h;
567: m_img = m_source.getScaledInstance(m_w, m_h,
568: Image.SCALE_SMOOTH);
569: m_img.flush();
570: repaint();
571: }
572:
573: public Dimension getPreferredSize() {
574: Insets ins = getInsets();
575: return new Dimension(m_w + ins.left + ins.right, m_h
576: + ins.top + ins.bottom);
577: }
578:
579: public Dimension getMaximumSize() {
580: return getPreferredSize();
581: }
582:
583: public Dimension getMinimumSize() {
584: return getPreferredSize();
585: }
586:
587: public void paint(Graphics g) {
588: if (this .m_img != null) {
589: g.setColor(getBackground());
590: g.fillRect(0, 0, getWidth(), getHeight());
591: g.drawImage(m_img, 0, 0, this);
592: paintBorder(g);
593: }
594: }
595: }
596: }
|