001: /**
002: * ===========================================
003: * JFreeReport : a free Java reporting library
004: * ===========================================
005: *
006: * Project Info: http://reporting.pentaho.org/
007: *
008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
009: *
010: * This library is free software; you can redistribute it and/or modify it under the terms
011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
012: * either version 2.1 of the License, or (at your option) any later version.
013: *
014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
016: * See the GNU Lesser General Public License for more details.
017: *
018: * You should have received a copy of the GNU Lesser General Public License along with this
019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
020: * Boston, MA 02111-1307, USA.
021: *
022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
023: * in the United States and other countries.]
024: *
025: * ------------
026: * ReportProgressDialog.java
027: * ------------
028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
029: */package org.jfree.report.modules.gui.commonswing;
030:
031: import java.awt.Dialog;
032: import java.awt.Frame;
033: import java.awt.GridBagConstraints;
034: import java.awt.GridBagLayout;
035: import java.awt.Insets;
036: import java.awt.event.WindowAdapter;
037: import java.awt.event.WindowEvent;
038: import java.text.MessageFormat;
039: import javax.swing.JDialog;
040: import javax.swing.JLabel;
041: import javax.swing.JPanel;
042: import javax.swing.JProgressBar;
043: import javax.swing.SwingUtilities;
044: import javax.swing.border.EmptyBorder;
045:
046: import org.jfree.report.event.ReportProgressEvent;
047: import org.jfree.report.event.ReportProgressListener;
048: import org.jfree.report.util.i18n.Messages;
049:
050: /**
051: * A progress monitor dialog component that visualizes the report processing progress. It will receive update events
052: * from the report processors and updates the UI according to the latest event data.
053: * <p/>
054: * The progress will be computed according to the currently processed table row. This approach provides relativly
055: * accurate data, but assumes that processing all bands consumes roughly the same time.
056: *
057: * @author Thomas Morgner
058: */
059: public class ReportProgressDialog extends JDialog implements
060: ReportProgressListener {
061: /**
062: * Handles the update event processing as a separate thread
063: *
064: * @author Thomas Morgner
065: */
066: private class ScreenUpdateRunnable implements Runnable {
067: /**
068: * The event upon which this update event processing will occur
069: */
070: private ReportProgressEvent event;
071:
072: /**
073: * Initializes the update event processing thread with the event information
074: */
075: protected ScreenUpdateRunnable() {
076: }
077:
078: /**
079: * Performs the process of updating all the pieces of the progress dialog with the update event information.
080: */
081: public synchronized void run() {
082: if (event == null) {
083: return;
084: }
085: updatePageMessage(event.getPage());
086: updateRowsMessage(event.getRow(), event.getMaximumRow());
087: updateActivityMessage(event.getActivity());
088: updateProgressBar(event);
089: this .event = null;
090: }
091:
092: public synchronized boolean update(
093: final ReportProgressEvent event) {
094: final boolean retval = (this .event == null);
095: this .event = event;
096: return retval;
097: }
098: }
099:
100: /**
101: * Event handles that will ensure this dialog will remain on top
102: *
103: * @author Thomas Morgner
104: */
105: private static class ToFrontHandler extends WindowAdapter {
106: protected ToFrontHandler() {
107: }
108:
109: /**
110: * Invoked when a window has been opened.
111: */
112: public void windowOpened(final WindowEvent e) {
113: e.getWindow().toFront();
114: }
115: }
116:
117: /**
118: * A label that carries the global message that describes the current task.
119: */
120: private JLabel messageCarrier;
121:
122: /**
123: * A label containing the report processing pass count.
124: */
125: private JLabel passCountMessage;
126:
127: /**
128: * A label containing the current page.
129: */
130: private JLabel pageCountMessage;
131:
132: /**
133: * A label containing the currently processed row.
134: */
135: private JLabel rowCountMessage;
136:
137: /**
138: * The progress bar that is used to visualize the progress.
139: */
140: private JProgressBar progressBar;
141:
142: /**
143: * The reuseable message format for the page label.
144: */
145: private MessageFormat pageMessageFormatter;
146:
147: /**
148: * The reuseable message format for the rows label.
149: */
150: private MessageFormat rowsMessageFormatter;
151:
152: /**
153: * The reuseable message format for the pass label.
154: */
155: private MessageFormat passMessageFormatter;
156:
157: /**
158: * The last page received.
159: */
160: private int lastPage;
161:
162: /**
163: * The last pass values received.
164: */
165: private int lastActivity;
166:
167: /**
168: * The last max-row received.
169: */
170: private int lastMaxRow;
171:
172: /**
173: * the cached value for the max-row value as integer.
174: */
175: private Integer lastMaxRowInteger; // this values doesnt change much, so reduce GC work
176:
177: /**
178: * a text which describes the layouting process.
179: */
180: private String layoutText;
181:
182: /**
183: * a text that describes the export phase of the report processing.
184: */
185: private String outputText;
186:
187: /**
188: * Localised messages.
189: */
190: private Messages messages;
191:
192: private boolean onlyPagination;
193:
194: private ScreenUpdateRunnable updateRunnable;
195:
196: /**
197: * Creates a non-modal dialog without a title and with the specified Dialog owner.
198: *
199: * @param dialog the owner of the dialog
200: */
201: public ReportProgressDialog(final Dialog dialog) {
202: super (dialog);
203: setLocale(dialog.getLocale());
204: initConstructor();
205: }
206:
207: /**
208: * Creates a non-modal dialog without a title and with the specified Frame owner.
209: *
210: * @param frame the owner of the dialog
211: */
212: public ReportProgressDialog(final Frame frame) {
213: super (frame);
214: setLocale(frame.getLocale());
215: initConstructor();
216: }
217:
218: /**
219: * Creates a non-modal dialog without a title and without a specified Frame owner. A shared, hidden frame will be set
220: * as the owner of the Dialog.
221: */
222: public ReportProgressDialog() {
223: initConstructor();
224: }
225:
226: public boolean isOnlyPagination() {
227: return onlyPagination;
228: }
229:
230: public void setOnlyPagination(final boolean onlyPagination) {
231: this .onlyPagination = onlyPagination;
232: }
233:
234: /**
235: * Initializes the dialog (Non-GUI stuff).
236: */
237: private void initConstructor() {
238: updateRunnable = new ScreenUpdateRunnable();
239: messages = new Messages(getLocale(),
240: SwingCommonModule.BUNDLE_NAME);
241: initialize();
242: addWindowListener(new ToFrontHandler());
243:
244: setOutputText(messages
245: .getString("progress-dialog.perform-output")); //$NON-NLS-1$
246: setLayoutText(messages
247: .getString("progress-dialog.prepare-layout")); //$NON-NLS-1$
248:
249: lastActivity = -1;
250: lastMaxRow = -1;
251: lastPage = -1;
252: }
253:
254: /**
255: * Initializes the GUI components of this dialog.
256: */
257: private void initialize() {
258: final JPanel contentPane = new JPanel();
259: contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
260: contentPane.setLayout(new GridBagLayout());
261:
262: pageMessageFormatter = new MessageFormat(messages
263: .getString("progress-dialog.page-label")); //$NON-NLS-1$
264: rowsMessageFormatter = new MessageFormat(messages
265: .getString("progress-dialog.rows-label")); //$NON-NLS-1$
266: passMessageFormatter = new MessageFormat(messages
267: .getString("progress-dialog.pass-label-0")); //$NON-NLS-1$
268:
269: messageCarrier = new JLabel(" "); //$NON-NLS-1$
270: passCountMessage = new JLabel(" "); //$NON-NLS-1$
271: rowCountMessage = new JLabel(" "); //$NON-NLS-1$
272: pageCountMessage = new JLabel(" "); //$NON-NLS-1$
273: progressBar = new JProgressBar(JProgressBar.HORIZONTAL, 0, 100);
274: progressBar.setStringPainted(true);
275:
276: GridBagConstraints gbc = new GridBagConstraints();
277: gbc.gridx = 0;
278: gbc.gridy = 0;
279: gbc.gridwidth = 2;
280: gbc.fill = GridBagConstraints.HORIZONTAL;
281: gbc.weightx = 1;
282: gbc.anchor = GridBagConstraints.WEST;
283: gbc.insets = new Insets(3, 1, 5, 1);
284: gbc.ipadx = 200;
285: contentPane.add(messageCarrier, gbc);
286:
287: gbc = new GridBagConstraints();
288: gbc.gridx = 0;
289: gbc.gridy = 1;
290: gbc.gridwidth = 2;
291: gbc.anchor = GridBagConstraints.SOUTHWEST;
292: gbc.insets = new Insets(3, 1, 1, 1);
293: contentPane.add(passCountMessage, gbc);
294:
295: gbc = new GridBagConstraints();
296: gbc.gridx = 0;
297: gbc.gridy = 2;
298: gbc.gridwidth = 2;
299: gbc.anchor = GridBagConstraints.WEST;
300: gbc.fill = GridBagConstraints.HORIZONTAL;
301: gbc.weightx = 1;
302: gbc.insets = new Insets(3, 1, 1, 1);
303: contentPane.add(progressBar, gbc);
304:
305: gbc = new GridBagConstraints();
306: gbc.gridx = 0;
307: gbc.gridy = 3;
308: gbc.gridwidth = 1;
309: gbc.weighty = 1;
310: gbc.anchor = GridBagConstraints.NORTHWEST;
311: gbc.insets = new Insets(3, 1, 1, 1);
312: contentPane.add(pageCountMessage, gbc);
313:
314: gbc = new GridBagConstraints();
315: gbc.gridx = 1;
316: gbc.gridy = 3;
317: gbc.gridwidth = 1;
318: gbc.anchor = GridBagConstraints.NORTHWEST;
319: gbc.insets = new Insets(3, 10, 1, 1);
320: gbc.weightx = 1;
321: gbc.fill = GridBagConstraints.HORIZONTAL;
322: contentPane.add(rowCountMessage, gbc);
323:
324: setContentPane(contentPane);
325: }
326:
327: /**
328: * Returns the current message.
329: *
330: * @return the current global message.
331: */
332: public String getMessage() {
333: return messageCarrier.getText();
334: }
335:
336: /**
337: * Defines the current message.
338: *
339: * @param message the current global message.
340: */
341: public void setMessage(final String message) {
342: messageCarrier.setText(message);
343: }
344:
345: /**
346: * Updates the page message label if the current page has changed.
347: *
348: * @param page the new page parameter.
349: */
350: protected void updatePageMessage(final int page) {
351: if (lastPage != page) {
352: final Object[] parameters = new Object[] { new Integer(page) };
353: pageCountMessage.setText(pageMessageFormatter
354: .format(parameters));
355: lastPage = page;
356: }
357: }
358:
359: /**
360: * Updates the rows message label if either the rows or maxrows changed.
361: *
362: * @param rows the currently processed rows.
363: * @param maxRows the maximum number of rows in the report.
364: */
365: protected void updateRowsMessage(final int rows, final int maxRows) {
366: if (maxRows != lastMaxRow) {
367: lastMaxRowInteger = new Integer(maxRows);
368: lastMaxRow = maxRows;
369: }
370: final Object[] parameters = new Object[] { new Integer(rows),
371: lastMaxRowInteger };
372: rowCountMessage
373: .setText(rowsMessageFormatter.format(parameters));
374: }
375:
376: /**
377: * Updates the pass message label if either the pass or prepare state changed. The pass reflects the current
378: * processing level, one level for every function dependency level.
379: *
380: * @param activity the current reporting pass.
381: */
382: protected void updateActivityMessage(final int activity) {
383: if (lastActivity != activity) {
384: lastActivity = activity;
385: final Object[] parameters = new Object[] { new Integer(
386: activity) };
387: passCountMessage.setText(passMessageFormatter
388: .format(parameters));
389: }
390: }
391:
392: /**
393: * Updates the progress bar to show the current progress
394: *
395: * @param event the event data used to update the progress bar
396: */
397: protected void updateProgressBar(final ReportProgressEvent event) {
398: progressBar.setValue((int) computePercentageComplete(event));
399: }
400:
401: /**
402: * Returns the current pass message component.
403: *
404: * @return the pass message component.
405: */
406: protected final JLabel getPassCountMessage() {
407: return passCountMessage;
408: }
409:
410: /**
411: * Returns the current pagecount message component.
412: *
413: * @return the page message component.
414: */
415: protected final JLabel getPageCountMessage() {
416: return pageCountMessage;
417: }
418:
419: /**
420: * Returns the current row message component.
421: *
422: * @return the row message component.
423: */
424: protected final JLabel getRowCountMessage() {
425: return rowCountMessage;
426: }
427:
428: /**
429: * Returns the current pass message component.
430: *
431: * @return the pass message component.
432: */
433: protected final MessageFormat getPageMessageFormatter() {
434: return pageMessageFormatter;
435: }
436:
437: /**
438: * Returns the current pass message component.
439: *
440: * @return the pass message component.
441: */
442: protected final MessageFormat getRowsMessageFormatter() {
443: return rowsMessageFormatter;
444: }
445:
446: /**
447: * Returns the current pass message component.
448: *
449: * @return the pass message component.
450: */
451: protected final MessageFormat getPassMessageFormatter() {
452: return passMessageFormatter;
453: }
454:
455: /**
456: * Returns the output text message. This text describes the export phases of the report processing.
457: *
458: * @return the output phase description.
459: */
460: public String getOutputText() {
461: return outputText;
462: }
463:
464: /**
465: * Defines the output text message. This text describes the export phases of the report processing.
466: *
467: * @param outputText the output message.
468: */
469: public void setOutputText(final String outputText) {
470: if (outputText == null) {
471: throw new NullPointerException(
472: messages
473: .getErrorString("ReportProgressDialog.ERROR_0001_OUTPUT_TEXT_NULL")); //$NON-NLS-1$
474: }
475: this .outputText = outputText;
476: }
477:
478: /**
479: * Returns the layout text. This text describes the prepare phases of the report processing.
480: *
481: * @return the layout text.
482: */
483: public String getLayoutText() {
484: return layoutText;
485: }
486:
487: /**
488: * Defines the layout text message. This text describes the prepare phases of the report processing.
489: *
490: * @param layoutText the layout message.
491: */
492: public void setLayoutText(final String layoutText) {
493: if (layoutText == null) {
494: throw new NullPointerException(
495: messages
496: .getErrorString("ReportProgressDialog.ERROR_0002_LAYOUT_TEXT_NULL")); //$NON-NLS-1$
497: }
498: this .layoutText = layoutText;
499: }
500:
501: protected boolean isSameMaxRow(final int row) {
502: return lastMaxRow == row;
503: }
504:
505: public void reportProcessingStarted(final ReportProgressEvent event) {
506: postUpdate(event);
507: }
508:
509: public void reportProcessingUpdate(final ReportProgressEvent event) {
510: postUpdate(event);
511: }
512:
513: public void reportProcessingFinished(final ReportProgressEvent event) {
514: postUpdate(event);
515: }
516:
517: private void postUpdate(final ReportProgressEvent event) {
518: synchronized (this .updateRunnable) {
519: if (this .updateRunnable.update(event)) {
520: if (SwingUtilities.isEventDispatchThread()) {
521: this .updateRunnable.run();
522: } else {
523: SwingUtilities.invokeLater(this .updateRunnable);
524: }
525: }
526: }
527: }
528:
529: /**
530: * Computes the percentage complete (on a scale from 0.0 to 100.0) based on the information found in the report
531: * progress event.
532: *
533: * @param event the data used to calculate the percentage complete
534: * @return the calculated percentage complete
535: */
536: protected double computePercentageComplete(
537: final ReportProgressEvent event) {
538: final double maximumLevel;
539: final double level;
540: if (isOnlyPagination()) {
541: maximumLevel = event.getMaximumLevel();
542: level = event.getLevel();
543: } else {
544: maximumLevel = event.getMaximumLevel() + 1;
545: if (event.getActivity() == ReportProgressEvent.GENERATING_CONTENT) {
546: level = event.getLevel() + 1;
547: } else {
548: level = event.getLevel();
549: }
550: }
551: final double levelPercentage = level / maximumLevel;
552: final double levelSizePercentage = 1.0 / maximumLevel;
553: final double subPercentage = levelSizePercentage
554: * (event.getRow() / (double) event.getMaximumRow());
555: final double percentage = 100.0 * (levelPercentage + subPercentage);
556: return Math.max(0.0, Math.min(100.0, percentage));
557: }
558:
559: public static void main(String[] args) {
560: ReportProgressDialog dialog = new ReportProgressDialog();
561: dialog.pack();
562: dialog.setVisible(true);
563: }
564: }
|