Source code: com/virtuosotechnologies/asaph/standardgui/PrintHandler.java
1 /*
2 ================================================================================
3
4 FILE: PrintHandler.java
5
6 PROJECT:
7
8 Asaph
9
10 CONTENTS:
11
12 An object that manages printing
13
14 PROGRAMMERS:
15
16 Daniel Azuma (DA) <dazuma@kagi.com>
17
18 COPYRIGHT:
19
20 Copyright (C) 2003 Daniel Azuma (dazuma@kagi.com)
21
22 This program is free software; you can redistribute it and/or
23 modify it under the terms of the GNU General Public License as
24 published by the Free Software Foundation; either version 2
25 of the License, or (at your option) any later version.
26
27 This program is distributed in the hope that it will be useful,
28 but WITHOUT ANY WARRANTY; without even the implied warranty of
29 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 GNU General Public License for more details.
31
32 You should have received a copy of the GNU General Public
33 License along with this program; if not, write to
34 Free Software Foundation, Inc.
35 59 Temple Place, Suite 330
36 Boston, MA 02111-1307 USA
37
38 ================================================================================
39 */
40
41
42 package com.virtuosotechnologies.asaph.standardgui;
43
44
45 import java.awt.Font;
46 import java.awt.Color;
47 import java.awt.FlowLayout;
48 import java.awt.GridBagLayout;
49 import java.awt.GridBagConstraints;
50 import java.awt.Frame;
51 import java.awt.Insets;
52 import java.awt.Graphics;
53 import java.awt.Graphics2D;
54 import java.awt.event.ActionListener;
55 import java.awt.event.ActionEvent;
56 import java.awt.print.Printable;
57 import java.awt.print.Pageable;
58 import java.awt.print.PrinterJob;
59 import java.awt.print.PageFormat;
60 import java.awt.print.PrinterException;
61 import java.util.Hashtable;
62 import javax.swing.JComponent;
63 import javax.swing.JDialog;
64 import javax.swing.JPanel;
65 import javax.swing.JLabel;
66 import javax.swing.JRadioButton;
67 import javax.swing.JComboBox;
68 import javax.swing.JSlider;
69 import javax.swing.JButton;
70 import javax.swing.ButtonGroup;
71 import javax.swing.event.ChangeListener;
72 import javax.swing.event.ChangeEvent;
73 import javax.print.attribute.PrintRequestAttributeSet;
74
75 import com.virtuosotechnologies.lib.asyncjob.AsyncJobRunner;
76 import com.virtuosotechnologies.lib.asyncjob.AbstractAsyncJob;
77 import com.virtuosotechnologies.lib.asyncjob.AsyncJob;
78 import com.virtuosotechnologies.lib.asyncjob.AsyncJobProgressReporter;
79 import com.virtuosotechnologies.lib.asyncjob.AsyncJobException;
80
81
82 /**
83 * An object that manages printing.
84 */
85 /*package*/ class PrintHandler
86 implements Printable, Pageable
87 {
88 private static final String STR_PrintHandler_FormatDialog_Title =
89 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_Title");
90 private static final String STR_PrintHandler_FormatDialog_PromptLabel =
91 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_PromptLabel");
92 private static final String STR_PrintHandler_FormatDialog_ScaleRadio =
93 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_ScaleRadio");
94 private static final String STR_PrintHandler_FormatDialog_FlowRadio =
95 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_FlowRadio");
96 private static final String STR_PrintHandler_FormatDialog_ColumnsLabel =
97 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_ColumnsLabel");
98 private static final String STR_PrintHandler_FormatDialog_GutterLabel =
99 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_GutterLabel");
100 private static final String STR_PrintHandler_FormatDialog_0InchLabel =
101 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_0InchLabel");
102 private static final String STR_PrintHandler_FormatDialog_HalfInchLabel =
103 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_HalfInchLabel");
104 private static final String STR_PrintHandler_FormatDialog_1InchLabel =
105 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_1InchLabel");
106 private static final String STR_PrintHandler_FormatDialog_PrintButton =
107 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_PrintButton");
108 private static final String STR_PrintHandler_FormatDialog_CancelButton =
109 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_CancelButton");
110 private static final String STR_PrintHandler_FormatDialog_PrintJobName =
111 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_PrintJobName");
112
113 private static final Font SMALL_FONT = new Font("Dialog", Font.PLAIN, 9);
114
115 private RenderHelper helper_;
116
117 private JComponent dialogParent_;
118 private AsyncJobRunner jobRunner_;
119
120 private PrinterJob printJob_;
121 private PageFormat pageFormat_;
122 private PrintRequestAttributeSet attrs_;
123
124 // Settings for formatting
125 private boolean fitOnOnePage_;
126 private boolean okayed_;
127 private PrintRenderHelper flowingRender_;
128 private float scaleRequired_;
129
130 // Page format analysis
131 private float pageWidth_;
132 private float pageHeight_;
133 private float imageableLeft_;
134 private float imageableTop_;
135 private float imageableRight_;
136 private float imageableBottom_;
137 private float imageableWidth_;
138 private float imageableHeight_;
139 private float imageableHCenter_;
140 private float imageableVCenter_;
141
142
143 /**
144 * Pseudo-Constructor
145 */
146 /*package*/ static PrintHandler create(
147 PrinterJob job,
148 PrintRequestAttributeSet attrs,
149 RenderHelper renderHelper,
150 JComponent dialogParent,
151 AsyncJobRunner jobRunner)
152 {
153 PrintHandler handler = new PrintHandler(job, attrs, renderHelper, dialogParent, jobRunner);
154 if (handler.displayPrintDialogs())
155 {
156 if (handler.validateFormat())
157 {
158 return handler;
159 }
160 }
161 return null;
162 }
163
164
165 /**
166 * Constructor
167 */
168 private PrintHandler(
169 PrinterJob job,
170 PrintRequestAttributeSet attrs,
171 RenderHelper renderHelper,
172 JComponent dialogParent,
173 AsyncJobRunner jobRunner)
174 {
175 printJob_ = job;
176 attrs_ = attrs;
177 helper_ = renderHelper;
178 dialogParent_ = dialogParent;
179 scaleRequired_ = 1;
180 fitOnOnePage_ = true;
181 jobRunner_ = jobRunner;
182 }
183
184
185 /**
186 * Display print dialogs
187 */
188 private boolean displayPrintDialogs()
189 {
190 if (printJob_.printDialog(attrs_))
191 {
192 PageFormat format = printJob_.pageDialog(attrs_);
193 if (format != null)
194 {
195 setPageFormat(format);
196 return true;
197 }
198 }
199 return false;
200 }
201
202
203 /**
204 * Check formatting
205 */
206 private boolean validateFormat()
207 {
208 if (helper_.getWidth() <= imageableWidth_ && helper_.getHeight() <= imageableHeight_)
209 {
210 // It all fits in one page
211 return true;
212 }
213
214 scaleRequired_ = Math.min(imageableWidth_/helper_.getWidth(),
215 imageableHeight_/helper_.getHeight());
216
217 // Start building dialog
218 final JDialog dialog = new JDialog((Frame)dialogParent_.getTopLevelAncestor(),
219 STR_PrintHandler_FormatDialog_Title, true);
220 JPanel content = new JPanel(new GridBagLayout());
221 dialog.setContentPane(content);
222 GridBagConstraints gbc = new GridBagConstraints();
223 gbc.gridx = 0;
224 gbc.anchor = GridBagConstraints.WEST;
225 gbc.gridwidth = 2;
226
227 JLabel label = new JLabel(STR_PrintHandler_FormatDialog_PromptLabel);
228 gbc.insets = new Insets(10, 10, 10, 10);
229 content.add(label, gbc);
230
231 ButtonGroup radioGroup = new ButtonGroup();
232
233 // Scale-style radio button
234 JRadioButton scaleRadio = new JRadioButton(STR_PrintHandler_FormatDialog_ScaleRadio, true);
235 gbc.insets = new Insets(0, 10, 3, 10);
236 content.add(scaleRadio, gbc);
237 radioGroup.add(scaleRadio);
238
239 // Scale-style requirements
240 JLabel scaleReqLabel = new JLabel(
241 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_ScaleRequires",
242 Integer.toString((int)(scaleRequired_*100))));
243 gbc.insets = new Insets(0, 40, 10, 10);
244 content.add(scaleReqLabel, gbc);
245
246 // Flow-style radio button
247 JRadioButton flowRadio = new JRadioButton(STR_PrintHandler_FormatDialog_FlowRadio);
248 gbc.insets = new Insets(0, 10, 3, 10);
249 content.add(flowRadio, gbc);
250 radioGroup.add(flowRadio);
251
252 // Columns combo and label
253 label = new JLabel(STR_PrintHandler_FormatDialog_ColumnsLabel);
254 gbc.insets = new Insets(0, 40, 5, 5);
255 gbc.gridwidth = 1;
256 content.add(label, gbc);
257
258 final JComboBox columnsCombo = new JComboBox(new Integer[]{
259 new Integer(1), new Integer(2), new Integer(3), new Integer(4)});
260 gbc.insets = new Insets(0, 0, 5, 10);
261 gbc.gridx = 1;
262 gbc.weightx = 1;
263 content.add(columnsCombo, gbc);
264
265 // Gutter slider and label
266 label = new JLabel(STR_PrintHandler_FormatDialog_GutterLabel);
267 gbc.insets = new Insets(0, 40, 0, 5);
268 gbc.gridx = 0;
269 gbc.weightx = 0;
270 gbc.anchor = GridBagConstraints.SOUTHWEST;
271 content.add(label, gbc);
272
273 final JLabel gutterWidthLabel = new JLabel(
274 ResourceAccess.Strings.buildString("PrintHandler_FormatDialog_GutterPoints", "24"));
275 gbc.insets = new Insets(0, 40, 5, 5);
276 gbc.anchor = GridBagConstraints.NORTHWEST;
277 content.add(gutterWidthLabel, gbc);
278
279 final JSlider gutterWidthSlider = new JSlider(0, 72, 24);
280 Hashtable labelTable = new Hashtable();
281 label = new JLabel(STR_PrintHandler_FormatDialog_0InchLabel);
282 label.setFont(SMALL_FONT);
283 labelTable.put(new Integer(0), label);
284 label = new JLabel(STR_PrintHandler_FormatDialog_HalfInchLabel);
285 label.setFont(SMALL_FONT);
286 labelTable.put(new Integer(36), label);
287 label = new JLabel(STR_PrintHandler_FormatDialog_1InchLabel);
288 label.setFont(SMALL_FONT);
289 labelTable.put(new Integer(72), label);
290 gutterWidthSlider.setLabelTable(labelTable);
291 gutterWidthSlider.setPaintLabels(true);
292 gutterWidthSlider.setPaintTicks(true);
293 gutterWidthSlider.setMajorTickSpacing(36);
294 gutterWidthSlider.setMinorTickSpacing(12);
295 gbc.insets = new Insets(0, 0, 5, 10);
296 gbc.gridx = 1;
297 gbc.gridheight = 2;
298 gbc.weightx = 1;
299 gbc.anchor = GridBagConstraints.WEST;
300 content.add(gutterWidthSlider, gbc);
301
302 // Flowed-style requirements
303 final JLabel flowReqLabel = new JLabel();
304 gbc.insets = new Insets(0, 40, 5, 10);
305 gbc.gridx = 0;
306 gbc.gridheight = 1;
307 gbc.gridwidth = 2;
308 gbc.weightx = 0;
309 content.add(flowReqLabel, gbc);
310
311 // Updaters
312 columnsCombo.addActionListener(
313 new ActionListener()
314 {
315 public void actionPerformed(
316 ActionEvent ev)
317 {
318 recalcPrintHelper(((Integer)columnsCombo.getSelectedItem()).intValue(),
319 gutterWidthSlider.getValue(), gutterWidthLabel, flowReqLabel);
320 }
321 });
322 gutterWidthSlider.addChangeListener(
323 new ChangeListener()
324 {
325 public void stateChanged(
326 ChangeEvent ev)
327 {
328 gutterWidthLabel.setText(ResourceAccess.Strings.buildString(
329 "PrintHandler_FormatDialog_GutterPoints",
330 Integer.toString(gutterWidthSlider.getValue())));
331 if (!gutterWidthSlider.getValueIsAdjusting())
332 {
333 recalcPrintHelper(((Integer)columnsCombo.getSelectedItem()).intValue(),
334 gutterWidthSlider.getValue(), gutterWidthLabel, flowReqLabel);
335 }
336 }
337 });
338 recalcPrintHelper(1, 24, gutterWidthLabel, flowReqLabel);
339
340 // Build buttons
341 JPanel buttonPanel = new JPanel(new FlowLayout());
342 JButton okButton = new JButton(STR_PrintHandler_FormatDialog_PrintButton);
343 buttonPanel.add(okButton);
344 JButton cancelButton = new JButton(STR_PrintHandler_FormatDialog_CancelButton);
345 buttonPanel.add(cancelButton);
346 gbc.insets = new Insets(5, 10, 5, 10);
347 gbc.fill = GridBagConstraints.HORIZONTAL;
348 content.add(buttonPanel, gbc);
349
350 okButton.addActionListener(
351 new ActionListener()
352 {
353 public void actionPerformed(
354 ActionEvent ev)
355 {
356 okayed_ = true;
357 dialog.dispose();
358 }
359 });
360 cancelButton.addActionListener(
361 new ActionListener()
362 {
363 public void actionPerformed(
364 ActionEvent ev)
365 {
366 okayed_ = false;
367 dialog.dispose();
368 }
369 });
370 okayed_ = false;
371
372 // Show dialog
373 dialog.pack();
374 dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
375 dialog.setResizable(false);
376 dialog.setLocationRelativeTo(dialogParent_);
377 dialog.show();
378 fitOnOnePage_ = scaleRadio.isSelected();
379
380 return okayed_;
381 }
382
383
384 private void recalcPrintHelper(
385 int columns,
386 int gutterWidth,
387 JLabel gutterWidthLabel,
388 JLabel flowReqLabel)
389 {
390 flowingRender_ = new PrintRenderHelper(helper_,
391 imageableWidth_, imageableHeight_, columns, gutterWidth);
392 flowReqLabel.setText(ResourceAccess.Strings.buildString(
393 "PrintHandler_FormatDialog_FlowRequires",
394 Integer.toString((int)(flowingRender_.getRequiredScale()*100))));
395 }
396
397
398 /**
399 * Do the actual printing
400 */
401 /*package*/ void doPrint()
402 {
403 // Do the print
404 printJob_.setPageable(this);
405 jobRunner_.startJob(
406 new AbstractAsyncJob(STR_PrintHandler_FormatDialog_PrintJobName,
407 false, AsyncJob.INDETERMINATE_PROGRESS, null)
408 {
409 public Object run(
410 AsyncJobProgressReporter reporter)
411 throws
412 AsyncJobException
413 {
414 try
415 {
416 printJob_.print(attrs_);
417 }
418 catch (PrinterException ex)
419 {
420 throw new AsyncJobException(ex);
421 }
422 return null;
423 }
424 });
425 }
426
427
428 /**
429 * Implementation of Pageable
430 */
431 public int getNumberOfPages()
432 {
433 if (fitOnOnePage_)
434 {
435 return 1;
436 }
437 return flowingRender_.getNumPages();
438 }
439
440
441 /**
442 * Implementation of Pageable
443 */
444 public PageFormat getPageFormat(
445 int pageIndex)
446 {
447 return pageFormat_;
448 }
449
450
451 /**
452 * Implementation of Pageable
453 */
454 public Printable getPrintable(
455 int pageIndex)
456 {
457 return this;
458 }
459
460
461 /**
462 * Implementation of Printable
463 */
464 public int print(
465 Graphics g,
466 PageFormat pageFormat,
467 int pageIndex)
468 {
469 if (pageIndex >= getNumberOfPages())
470 {
471 return NO_SUCH_PAGE;
472 }
473 Graphics2D g2d = (Graphics2D)g;
474
475 RenderHelper.mungeGraphics2D(g2d);
476
477 g2d.setColor(Color.white);
478 g2d.fillRect(0, 0, (int)pageWidth_, (int)pageHeight_);
479 g2d.setColor(Color.black);
480
481 if (fitOnOnePage_)
482 {
483 g2d.scale(scaleRequired_, scaleRequired_);
484 float scaledHCenter = imageableHCenter_/scaleRequired_;
485 float scaledVCenter = imageableVCenter_/scaleRequired_;
486 RenderHelper helper = helper_.createUsingGraphics(g2d);
487 helper.paint(g2d, scaledHCenter-(helper.getWidth()*0.5f),
488 scaledVCenter-(helper.getHeight()*0.5f));
489 }
490 else
491 {
492 PrintRenderHelper printHelper = flowingRender_.createUsingGraphics(g2d);
493 float scaleRequired = printHelper.getRequiredScale();
494 g2d.scale(scaleRequired, scaleRequired);
495 float scaledHCenter = imageableHCenter_/scaleRequired;
496 float scaledVCenter = imageableVCenter_/scaleRequired;
497 printHelper.paintPage(pageIndex, g2d,
498 scaledHCenter-(printHelper.getWidth()*0.5f),
499 scaledVCenter-(printHelper.getHeightForPage(pageIndex)*0.5f));
500 }
501
502 return PAGE_EXISTS;
503 }
504
505
506 private void setPageFormat(
507 PageFormat pageFormat)
508 {
509 pageFormat_ = pageFormat;
510 pageWidth_ = (float)pageFormat_.getWidth();
511 pageHeight_ = (float)pageFormat_.getHeight();
512 imageableLeft_ = (float)pageFormat_.getImageableX();
513 imageableTop_ = (float)pageFormat_.getImageableY();
514 imageableRight_ = imageableLeft_+(float)pageFormat_.getImageableWidth();
515 imageableBottom_ = imageableTop_+(float)pageFormat_.getImageableHeight();
516 if (imageableLeft_ < 36) imageableLeft_ = 36;
517 if (imageableTop_ < 36) imageableTop_ = 36;
518 if (imageableRight_ > pageWidth_-36) imageableRight_ = pageWidth_-36;
519 if (imageableBottom_ > pageHeight_-36) imageableBottom_ = pageHeight_-36;
520 imageableWidth_ = imageableRight_-imageableLeft_;
521 imageableHeight_ = imageableBottom_-imageableTop_;
522 imageableHCenter_ = (imageableRight_+imageableLeft_)*0.5f;
523 imageableVCenter_ = (imageableBottom_+imageableTop_)*0.5f;
524 }
525 }