0001: /**
0002: * ===========================================
0003: * JFreeReport : a free Java reporting library
0004: * ===========================================
0005: *
0006: * Project Info: http://reporting.pentaho.org/
0007: *
0008: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0009: *
0010: * This library is free software; you can redistribute it and/or modify it under the terms
0011: * of the GNU Lesser General Public License as published by the Free Software Foundation;
0012: * either version 2.1 of the License, or (at your option) any later version.
0013: *
0014: * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
0015: * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
0016: * See the GNU Lesser General Public License for more details.
0017: *
0018: * You should have received a copy of the GNU Lesser General Public License along with this
0019: * library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330,
0020: * Boston, MA 02111-1307, USA.
0021: *
0022: * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
0023: * in the United States and other countries.]
0024: *
0025: * ------------
0026: * PreviewPane.java
0027: * ------------
0028: * (C) Copyright 2001-2007, by Object Refinery Ltd, Pentaho Corporation and Contributors.
0029: */package org.jfree.report.modules.gui.base;
0030:
0031: import java.awt.BorderLayout;
0032: import java.awt.Color;
0033: import java.awt.Dimension;
0034: import java.awt.IllegalComponentStateException;
0035: import java.awt.Toolkit;
0036: import java.awt.Window;
0037: import java.awt.print.PageFormat;
0038: import java.beans.PropertyChangeEvent;
0039: import java.beans.PropertyChangeListener;
0040: import java.text.NumberFormat;
0041: import java.util.ArrayList;
0042: import java.util.Arrays;
0043: import java.util.Collections;
0044: import java.util.HashMap;
0045: import java.util.Iterator;
0046: import java.util.Locale;
0047: import java.util.Map;
0048: import javax.swing.BorderFactory;
0049: import javax.swing.JComponent;
0050: import javax.swing.JMenu;
0051: import javax.swing.JPanel;
0052: import javax.swing.JScrollPane;
0053: import javax.swing.JToolBar;
0054: import javax.swing.SwingUtilities;
0055:
0056: import org.jfree.layout.CenterLayout;
0057: import org.jfree.report.JFreeReport;
0058: import org.jfree.report.JFreeReportBoot;
0059: import org.jfree.report.PageDefinition;
0060: import org.jfree.report.ReportProcessingException;
0061: import org.jfree.report.event.ReportProgressEvent;
0062: import org.jfree.report.event.ReportProgressListener;
0063: import org.jfree.report.modules.gui.base.internal.ActionCategory;
0064: import org.jfree.report.modules.gui.base.internal.ActionPluginComparator;
0065: import org.jfree.report.modules.gui.base.internal.CategoryTreeItem;
0066: import org.jfree.report.modules.gui.base.internal.PageBackgroundDrawable;
0067: import org.jfree.report.modules.gui.base.internal.PreviewPaneUtilities;
0068: import org.jfree.report.modules.gui.common.IconTheme;
0069: import org.jfree.report.modules.gui.common.StatusListener;
0070: import org.jfree.report.modules.gui.commonswing.ActionPlugin;
0071: import org.jfree.report.modules.gui.commonswing.ReportEventSource;
0072: import org.jfree.report.modules.gui.commonswing.StatusType;
0073: import org.jfree.report.modules.gui.commonswing.SwingGuiContext;
0074: import org.jfree.report.modules.gui.commonswing.SwingUtil;
0075: import org.jfree.report.modules.gui.commonswing.WindowSizeLimiter;
0076: import org.jfree.report.modules.output.pageable.graphics.PageDrawable;
0077: import org.jfree.report.modules.output.pageable.graphics.PrintReportProcessor;
0078: import org.jfree.report.util.StringUtil;
0079: import org.jfree.report.util.Worker;
0080: import org.jfree.report.util.i18n.Messages;
0081: import org.jfree.ui.Drawable;
0082: import org.jfree.ui.DrawablePanel;
0083: import org.jfree.ui.KeyedComboBoxModel;
0084: import org.jfree.util.Configuration;
0085: import org.jfree.util.Log;
0086: import org.jfree.util.ObjectUtilities;
0087:
0088: /**
0089: * Creation-Date: 11.11.2006, 19:36:13
0090: *
0091: * @author Thomas Morgner
0092: */
0093: public class PreviewPane extends JPanel implements ReportEventSource {
0094: private class PreviewGuiContext implements SwingGuiContext {
0095: protected PreviewGuiContext() {
0096: }
0097:
0098: public Window getWindow() {
0099: return SwingUtil.getWindowAncestor(PreviewPane.this );
0100: }
0101:
0102: public Locale getLocale() {
0103: final JFreeReport report = getReportJob();
0104: if (report != null) {
0105: return report.getResourceBundleFactory().getLocale();
0106: }
0107: return Locale.getDefault();
0108: }
0109:
0110: public IconTheme getIconTheme() {
0111: return PreviewPane.this .getIconTheme();
0112: }
0113:
0114: public Configuration getConfiguration() {
0115: final JFreeReport report = getReportJob();
0116: if (report != null) {
0117: return report.getConfiguration();
0118: }
0119: return JFreeReportBoot.getInstance().getGlobalConfig();
0120: }
0121:
0122: public StatusListener getStatusListener() {
0123: return PreviewPane.this .getStatusListener();
0124: }
0125:
0126: public ReportEventSource getEventSource() {
0127: return PreviewPane.this ;
0128: }
0129: }
0130:
0131: private class PreviewPaneStatusUpdater implements Runnable {
0132: private StatusType type;
0133: private String text;
0134:
0135: protected PreviewPaneStatusUpdater(final StatusType type,
0136: final String text) {
0137: this .type = type;
0138: this .text = text;
0139: }
0140:
0141: public StatusType getType() {
0142: return type;
0143: }
0144:
0145: public String getText() {
0146: return text;
0147: }
0148:
0149: /**
0150: * When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread
0151: * causes the object's <code>run</code> method to be called in that separately executing thread.
0152: * <p/>
0153: * The general contract of the method <code>run</code> is that it may take any action whatsoever.
0154: *
0155: * @see Thread#run()
0156: */
0157: public void run() {
0158: setStatusType(type);
0159: setStatusText(text);
0160: }
0161: }
0162:
0163: /**
0164: * The StatusListener here shields the preview pane from any attempt to tamper with it.
0165: */
0166: private class PreviewPaneStatusListener implements StatusListener {
0167: protected PreviewPaneStatusListener() {
0168: }
0169:
0170: public void setStatus(final StatusType type, final String text) {
0171: if (SwingUtilities.isEventDispatchThread()) {
0172: setStatusType(type);
0173: setStatusText(text);
0174: } else {
0175: SwingUtilities
0176: .invokeLater(new PreviewPaneStatusUpdater(type,
0177: text));
0178: }
0179: }
0180: }
0181:
0182: private class RepaginationRunnable implements Runnable,
0183: ReportProgressListener {
0184: private PrintReportProcessor processor;
0185: private boolean interrupted;
0186:
0187: protected RepaginationRunnable(
0188: final PrintReportProcessor processor) {
0189: this .processor = processor;
0190: }
0191:
0192: public void signalAbort() {
0193: this .interrupted = true;
0194: }
0195:
0196: public void reportProcessingStarted(
0197: final ReportProgressEvent event) {
0198: if (interrupted == false) {
0199: return;
0200: }
0201: forwardReportStartedEvent(event);
0202: }
0203:
0204: public void reportProcessingUpdate(
0205: final ReportProgressEvent event) {
0206: if (interrupted == false) {
0207: return;
0208: }
0209: forwardReportUpdateEvent(event);
0210: }
0211:
0212: public void reportProcessingFinished(
0213: final ReportProgressEvent event) {
0214: if (interrupted == false) {
0215: return;
0216: }
0217: forwardReportFinishedEvent(event);
0218: }
0219:
0220: /**
0221: * When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread
0222: * causes the object's <code>run</code> method to be called in that separately executing thread.
0223: * <p/>
0224: * The general contract of the method <code>run</code> is that it may take any action whatsoever.
0225: *
0226: * @see Thread#run()
0227: */
0228: public void run() {
0229: this .processor.addReportProgressListener(this );
0230: try {
0231: final UpdatePaginatingPropertyHandler startPaginationNotify = new UpdatePaginatingPropertyHandler(
0232: processor, true, false, 0);
0233: if (SwingUtilities.isEventDispatchThread()) {
0234: startPaginationNotify.run();
0235: } else {
0236: SwingUtilities.invokeLater(startPaginationNotify);
0237: }
0238:
0239: // Perform the pagination ..
0240: final int pageCount = processor.getNumberOfPages();
0241:
0242: final UpdatePaginatingPropertyHandler endPaginationNotify = new UpdatePaginatingPropertyHandler(
0243: processor, false, true, pageCount);
0244: if (SwingUtilities.isEventDispatchThread()) {
0245: endPaginationNotify.run();
0246: } else {
0247: SwingUtilities.invokeLater(endPaginationNotify);
0248: }
0249: } catch (Exception e) {
0250: final UpdatePaginatingPropertyHandler endPaginationNotify = new UpdatePaginatingPropertyHandler(
0251: processor, false, false, 0);
0252: if (SwingUtilities.isEventDispatchThread()) {
0253: endPaginationNotify.run();
0254: } else {
0255: SwingUtilities.invokeLater(endPaginationNotify);
0256: }
0257: Log.error("Pagination failed.", e); //$NON-NLS-1$
0258: } finally {
0259: this .processor.removeReportProgressListener(this );
0260: }
0261: }
0262: }
0263:
0264: private class UpdatePaginatingPropertyHandler implements Runnable {
0265: private boolean paginating;
0266: private boolean paginated;
0267: private int pageCount;
0268: private PrintReportProcessor processor;
0269:
0270: protected UpdatePaginatingPropertyHandler(
0271: final PrintReportProcessor processor,
0272: final boolean paginating, final boolean paginated,
0273: final int pageCount) {
0274: this .processor = processor;
0275: this .paginating = paginating;
0276: this .paginated = paginated;
0277: this .pageCount = pageCount;
0278: }
0279:
0280: public void run() {
0281: if (processor != getPrintReportProcessor()) {
0282: Log
0283: .debug(messages
0284: .getString("PreviewPane.DEBUG_NO_LONGER_VALID")); //$NON-NLS-1$
0285: return;
0286: }
0287:
0288: Log
0289: .debug(messages
0290: .getString(
0291: "PreviewPane.DEBUG_PAGINATION", String.valueOf(paginating), String.valueOf(pageCount))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
0292: if (paginating == false) {
0293: setNumberOfPages(pageCount);
0294: if (getPageNumber() < 1) {
0295: setPageNumber(1);
0296: } else if (getPageNumber() > pageCount) {
0297: setPageNumber(pageCount);
0298: }
0299: }
0300: setPaginating(paginating);
0301: setPaginated(paginated);
0302: }
0303: }
0304:
0305: private class PreviewUpdateHandler implements
0306: PropertyChangeListener {
0307: protected PreviewUpdateHandler() {
0308: }
0309:
0310: public void propertyChange(final PropertyChangeEvent evt) {
0311: final String propertyName = evt.getPropertyName();
0312: if (PAGINATING_PROPERTY.equals(propertyName)) {
0313: if (isPaginating()) {
0314: drawablePanel.setDrawable(getPaginatingDrawable());
0315: } else {
0316: updateVisiblePage(getPageNumber());
0317: }
0318: } else if (REPORT_JOB_PROPERTY.equals(propertyName)) {
0319: if (getReportJob() == null) {
0320: drawablePanel.setDrawable(getNoReportDrawable());
0321: }
0322: // else the paginating property will be fired anyway ..
0323: } else if (PAGE_NUMBER_PROPERTY.equals(propertyName)) {
0324: if (isPaginating()) {
0325: return;
0326: }
0327:
0328: updateVisiblePage(getPageNumber());
0329: }
0330: }
0331: }
0332:
0333: private class UpdateZoomHandler implements PropertyChangeListener {
0334: protected UpdateZoomHandler() {
0335: }
0336:
0337: /**
0338: * This method gets called when a bound property is changed.
0339: *
0340: * @param evt A PropertyChangeEvent object describing the event source and the property that has changed.
0341: */
0342:
0343: public void propertyChange(final PropertyChangeEvent evt) {
0344: if ("zoom".equals(evt.getPropertyName()) == false) //$NON-NLS-1$
0345: {
0346: return;
0347: }
0348:
0349: final double zoom = getZoom();
0350: pageDrawable.setZoom(zoom);
0351: zoomModel.setSelectedKey(new Double(zoom));
0352: if (zoomModel.getSelectedKey() == null) {
0353: zoomModel.setSelectedItem(formatZoomText(zoom));
0354: }
0355: drawablePanel.revalidate();
0356: }
0357: }
0358:
0359: private static final double[] ZOOM_FACTORS = { 0.5, 0.75, 1, 1.25,
0360: 1.50, 2.00 };
0361: private static final int DEFAULT_ZOOM_INDEX = 2;
0362: public static final String STATUS_TEXT_PROPERTY = "statusText"; //$NON-NLS-1$
0363: public static final String STATUS_TYPE_PROPERTY = "statusType"; //$NON-NLS-1$
0364: public static final String REPORT_CONTROLLER_PROPERTY = "reportController"; //$NON-NLS-1$
0365: public static final String ZOOM_PROPERTY = "zoom"; //$NON-NLS-1$
0366: public static final String CLOSED_PROPERTY = "closed"; //$NON-NLS-1$
0367:
0368: public static final String REPORT_JOB_PROPERTY = "reportJob"; //$NON-NLS-1$
0369: public static final String PAGINATING_PROPERTY = "paginating"; //$NON-NLS-1$
0370: public static final String PAGINATED_PROPERTY = "paginated"; //$NON-NLS-1$
0371: public static final String PAGE_NUMBER_PROPERTY = "pageNumber"; //$NON-NLS-1$
0372: public static final String NUMBER_OF_PAGES_PROPERTY = "numberOfPages"; //$NON-NLS-1$
0373:
0374: public static final String ICON_THEME_PROPERTY = "iconTheme"; //$NON-NLS-1$
0375: public static final String TITLE_PROPERTY = "title"; //$NON-NLS-1$
0376: public static final String MENU_PROPERTY = "menu"; //$NON-NLS-1$
0377:
0378: /**
0379: * The preferred width key.
0380: */
0381: public static final String PREVIEW_PREFERRED_WIDTH = "org.jfree.report.modules.gui.base.PreferredWidth"; //$NON-NLS-1$
0382:
0383: /**
0384: * The preferred height key.
0385: */
0386: public static final String PREVIEW_PREFERRED_HEIGHT = "org.jfree.report.modules.gui.base.PreferredHeight"; //$NON-NLS-1$
0387:
0388: /**
0389: * The maximum width key.
0390: */
0391: public static final String PREVIEW_MAXIMUM_WIDTH = "org.jfree.report.modules.gui.base.MaximumWidth"; //$NON-NLS-1$
0392:
0393: /**
0394: * The maximum height key.
0395: */
0396: public static final String PREVIEW_MAXIMUM_HEIGHT = "org.jfree.report.modules.gui.base.MaximumHeight"; //$NON-NLS-1$
0397:
0398: /**
0399: * The maximum zoom key.
0400: */
0401: public static final String ZOOM_MAXIMUM_KEY = "org.jfree.report.modules.gui.base.MaximumZoom"; //$NON-NLS-1$
0402:
0403: /**
0404: * The minimum zoom key.
0405: */
0406: public static final String ZOOM_MINIMUM_KEY = "org.jfree.report.modules.gui.base.MinimumZoom"; //$NON-NLS-1$
0407:
0408: /**
0409: * The default maximum zoom.
0410: */
0411: private static final float ZOOM_MAXIMUM_DEFAULT = 20.0f; // 2000%
0412:
0413: /**
0414: * The default minimum zoom.
0415: */
0416: private static final float ZOOM_MINIMUM_DEFAULT = 0.01f; // 1%
0417:
0418: /**
0419: * @deprecated use the paginating property instead
0420: */
0421: public static final String LOCK_INTERFACE_PROPERTY = "lockInterface"; //$NON-NLS-1$
0422: private static final String MENUBAR_AVAILABLE_KEY = "org.jfree.report.modules.gui.base.MenuBarAvailable"; //$NON-NLS-1$
0423: private static final String TOOLBAR_AVAILABLE_KEY = "org.jfree.report.modules.gui.base.ToolbarAvailable"; //$NON-NLS-1$
0424: private static final String TOOLBAR_FLOATABLE_KEY = "org.jfree.report.modules.gui.base.ToolbarFloatable"; //$NON-NLS-1$
0425:
0426: private Drawable paginatingDrawable;
0427: private Drawable noReportDrawable;
0428: private PageBackgroundDrawable pageDrawable;
0429:
0430: private DrawablePanel drawablePanel;
0431: private ReportController reportController;
0432: private JMenu[] menus;
0433: private JToolBar toolBar;
0434: private String statusText;
0435: private String title;
0436: private StatusType statusType;
0437: private boolean closed;
0438: private JFreeReport reportJob;
0439:
0440: private int numberOfPages;
0441: private int pageNumber;
0442: private SwingGuiContext swingGuiContext;
0443: private IconTheme iconTheme;
0444: private double zoom;
0445: private boolean paginating;
0446: private boolean paginated;
0447:
0448: private PrintReportProcessor printReportProcessor;
0449:
0450: private Worker paginationWorker;
0451: private JPanel innerReportControllerHolder;
0452: private JPanel toolbarHolder;
0453: private JPanel outerReportControllerHolder;
0454: private boolean reportControllerInner;
0455: private String reportControllerLocation;
0456: private JComponent reportControllerComponent;
0457: private KeyedComboBoxModel zoomModel;
0458: private PreviewPane.PreviewPaneStatusListener statusListener;
0459: private static final JMenu[] EMPTY_MENU = new JMenu[0];
0460: private boolean toolbarFloatable;
0461: private ArrayList reportProgressListener;
0462:
0463: private double maxZoom;
0464: private double minZoom;
0465:
0466: private Messages messages;
0467: private WindowSizeLimiter sizeLimiter;
0468:
0469: /**
0470: * Deferres the first repagination until pagination has been manually. After that initial pagination, the common
0471: * automatic pagiantion rules apply.
0472: */
0473: private boolean deferredRepagination;
0474:
0475: /**
0476: * Creates a new <code>JPanel</code> with a double buffer and a flow layout.
0477: */
0478: public PreviewPane() {
0479: messages = new Messages(getLocale(),
0480: SwingPreviewModule.BUNDLE_NAME);
0481: sizeLimiter = new WindowSizeLimiter();
0482:
0483: this .menus = EMPTY_MENU;
0484: setLayout(new BorderLayout());
0485:
0486: zoomModel = new KeyedComboBoxModel();
0487: zoomModel.setAllowOtherValue(true);
0488: zoom = ZOOM_FACTORS[DEFAULT_ZOOM_INDEX];
0489:
0490: final Configuration configuration = JFreeReportBoot
0491: .getInstance().getGlobalConfig();
0492: minZoom = getMinimumZoom(configuration);
0493: maxZoom = getMaximumZoom(configuration);
0494:
0495: pageDrawable = new PageBackgroundDrawable();
0496:
0497: drawablePanel = new DrawablePanel();
0498: drawablePanel.setOpaque(false);
0499: drawablePanel.setBackground(Color.green);
0500: drawablePanel.setDoubleBuffered(true);
0501: drawablePanel.setDrawable(pageDrawable);
0502:
0503: swingGuiContext = new PreviewGuiContext();
0504:
0505: final JPanel reportPaneHolder = new JPanel(new CenterLayout());
0506: reportPaneHolder.setBorder(BorderFactory.createEmptyBorder(10,
0507: 10, 10, 10));
0508: reportPaneHolder.add(drawablePanel);
0509:
0510: final JScrollPane s1 = new JScrollPane(reportPaneHolder);
0511: s1.getVerticalScrollBar().setUnitIncrement(20);
0512:
0513: innerReportControllerHolder = new JPanel();
0514: innerReportControllerHolder.setLayout(new BorderLayout());
0515: innerReportControllerHolder.add(s1, BorderLayout.CENTER);
0516:
0517: toolbarHolder = new JPanel();
0518: toolbarHolder.setLayout(new BorderLayout());
0519: toolbarHolder.add(innerReportControllerHolder,
0520: BorderLayout.CENTER);
0521:
0522: outerReportControllerHolder = new JPanel();
0523: outerReportControllerHolder.setLayout(new BorderLayout());
0524: outerReportControllerHolder.add(toolbarHolder,
0525: BorderLayout.CENTER);
0526:
0527: add(outerReportControllerHolder, BorderLayout.CENTER);
0528:
0529: addPropertyChangeListener(new PreviewUpdateHandler());
0530: addPropertyChangeListener("zoom", new UpdateZoomHandler()); //$NON-NLS-1$
0531: statusListener = new PreviewPaneStatusListener();
0532:
0533: initializeWithoutJob();
0534: }
0535:
0536: public boolean isDeferredRepagination() {
0537: return deferredRepagination;
0538: }
0539:
0540: public void setDeferredRepagination(
0541: final boolean deferredRepagination) {
0542: this .deferredRepagination = deferredRepagination;
0543: }
0544:
0545: public synchronized PrintReportProcessor getPrintReportProcessor() {
0546: return printReportProcessor;
0547: }
0548:
0549: protected synchronized void setPrintReportProcessor(
0550: final PrintReportProcessor printReportProcessor) {
0551: this .printReportProcessor = printReportProcessor;
0552: }
0553:
0554: public JMenu[] getMenu() {
0555: return menus;
0556: }
0557:
0558: protected void setMenu(final JMenu[] menus) {
0559: if (menus == null) {
0560: throw new NullPointerException();
0561: }
0562: final JMenu[] oldmenu = this .menus;
0563: this .menus = (JMenu[]) menus.clone();
0564: firePropertyChange(MENU_PROPERTY, oldmenu, this .menus);
0565: }
0566:
0567: public JToolBar getToolBar() {
0568: return toolBar;
0569: }
0570:
0571: public String getStatusText() {
0572: return statusText;
0573: }
0574:
0575: public void setStatusText(final String statusText) {
0576: final String oldStatus = this .statusText;
0577: this .statusText = statusText;
0578:
0579: firePropertyChange(STATUS_TEXT_PROPERTY, oldStatus, statusText);
0580: }
0581:
0582: public StatusType getStatusType() {
0583: return statusType;
0584: }
0585:
0586: public void setStatusType(final StatusType statusType) {
0587: final StatusType oldType = this .statusType;
0588: this .statusType = statusType;
0589:
0590: firePropertyChange(STATUS_TYPE_PROPERTY, oldType, statusType);
0591: }
0592:
0593: public ReportController getReportController() {
0594: return reportController;
0595: }
0596:
0597: public void setReportController(
0598: final ReportController reportController) {
0599: final ReportController oldController = this .reportController;
0600: this .reportController = reportController;
0601: firePropertyChange(REPORT_CONTROLLER_PROPERTY, oldController,
0602: reportController);
0603:
0604: // Now add the controller to the GUI ..
0605: refreshReportController(reportController);
0606: }
0607:
0608: private void refreshReportController(
0609: final ReportController newReportController) {
0610: if (newReportController != null) {
0611: final JComponent rcp = newReportController
0612: .getControlPanel();
0613: // if either the controller component or its position (inner vs outer)
0614: // and border-position has changed, then refresh ..
0615: if (reportControllerComponent != rcp
0616: || reportControllerInner != newReportController
0617: .isInnerComponent()
0618: || ObjectUtilities
0619: .equal(reportControllerLocation,
0620: newReportController
0621: .getControllerLocation())) {
0622: if (reportControllerComponent != null) {
0623: outerReportControllerHolder
0624: .remove(reportControllerComponent);
0625: innerReportControllerHolder
0626: .remove(reportControllerComponent);
0627: }
0628: final String sanLocation = sanitizeLocation(newReportController
0629: .getControllerLocation());
0630: final boolean innerComponent = newReportController
0631: .isInnerComponent();
0632: if (rcp != null) {
0633: if (innerComponent) {
0634: innerReportControllerHolder.add(rcp,
0635: sanLocation);
0636: } else {
0637: outerReportControllerHolder.add(rcp,
0638: sanLocation);
0639: }
0640: }
0641: reportControllerComponent = rcp;
0642: reportControllerLocation = sanLocation;
0643: reportControllerInner = innerComponent;
0644: }
0645: } else {
0646: if (reportControllerComponent != null) {
0647: outerReportControllerHolder
0648: .remove(reportControllerComponent);
0649: innerReportControllerHolder
0650: .remove(reportControllerComponent);
0651: }
0652: reportControllerComponent = null;
0653: }
0654:
0655: if (reportJob != null) {
0656: initializeFromReport();
0657: } else {
0658: initializeWithoutJob();
0659: }
0660: }
0661:
0662: private String sanitizeLocation(final String location) {
0663: if (BorderLayout.NORTH.equals(location)) {
0664: return BorderLayout.NORTH;
0665: }
0666: if (BorderLayout.SOUTH.equals(location)) {
0667: return BorderLayout.SOUTH;
0668: }
0669: if (BorderLayout.WEST.equals(location)) {
0670: return BorderLayout.WEST;
0671: }
0672: if (BorderLayout.EAST.equals(location)) {
0673: return BorderLayout.EAST;
0674: }
0675: return BorderLayout.NORTH;
0676: }
0677:
0678: public JFreeReport getReportJob() {
0679: return reportJob;
0680: }
0681:
0682: public void setReportJob(final JFreeReport reportJob) {
0683: final JFreeReport oldJob = this .reportJob;
0684: this .reportJob = reportJob;
0685:
0686: firePropertyChange(REPORT_JOB_PROPERTY, oldJob, reportJob);
0687: if (reportJob == null) {
0688: setPaginated(false);
0689: setPageNumber(0);
0690: setNumberOfPages(0);
0691: initializeWithoutJob();
0692: } else {
0693: initializeFromReport();
0694: }
0695: }
0696:
0697: public double getZoom() {
0698: return zoom;
0699: }
0700:
0701: public void setZoom(final double zoom) {
0702: final double oldZoom = this .zoom;
0703: this .zoom = Math.max(Math.min(zoom, maxZoom), minZoom);
0704: if (this .zoom != oldZoom) {
0705: firePropertyChange(ZOOM_PROPERTY, oldZoom, zoom);
0706: }
0707: }
0708:
0709: public boolean isClosed() {
0710: return closed;
0711: }
0712:
0713: public void setClosed(final boolean closed) {
0714: final boolean oldClosed = this .closed;
0715: this .closed = closed;
0716: firePropertyChange(CLOSED_PROPERTY, oldClosed, closed);
0717: if (closed) {
0718: prepareShutdown();
0719: }
0720: }
0721:
0722: private void prepareShutdown() {
0723: synchronized (this ) {
0724: if (paginationWorker != null) {
0725: //noinspection SynchronizeOnNonFinalField
0726: synchronized (paginationWorker) {
0727: paginationWorker.finish();
0728: }
0729: paginationWorker = null;
0730: }
0731: if (printReportProcessor != null) {
0732: printReportProcessor.close();
0733: printReportProcessor = null;
0734: }
0735: closeToolbar();
0736: }
0737: }
0738:
0739: private int getUserDefinedCategoryPosition() {
0740: return StringUtil
0741: .parseInt(
0742: swingGuiContext
0743: .getConfiguration()
0744: .getConfigProperty(
0745: "org.jfree.report.modules.gui.swing.user-defined-category.position"), 15000); //$NON-NLS-1$
0746: }
0747:
0748: public Locale getLocale() {
0749: if (getParent() == null) {
0750: try {
0751: return super .getLocale();
0752: } catch (IllegalComponentStateException ex) {
0753: return Locale.getDefault();
0754: }
0755: }
0756: return super .getLocale();
0757: }
0758:
0759: public int getNumberOfPages() {
0760: return numberOfPages;
0761: }
0762:
0763: public void setNumberOfPages(final int numberOfPages) {
0764: final int oldPageNumber = this .numberOfPages;
0765: this .numberOfPages = numberOfPages;
0766: firePropertyChange(NUMBER_OF_PAGES_PROPERTY, oldPageNumber,
0767: numberOfPages);
0768: }
0769:
0770: public int getPageNumber() {
0771: return pageNumber;
0772: }
0773:
0774: public void setPageNumber(final int pageNumber) {
0775: final int oldPageNumber = this .pageNumber;
0776: this .pageNumber = pageNumber;
0777: //Log.debug("Setting PageNumber: " + pageNumber);
0778: firePropertyChange(PAGE_NUMBER_PROPERTY, oldPageNumber,
0779: pageNumber);
0780: }
0781:
0782: public IconTheme getIconTheme() {
0783: return iconTheme;
0784: }
0785:
0786: protected void setIconTheme(final IconTheme theme) {
0787: final IconTheme oldTheme = this .iconTheme;
0788: this .iconTheme = theme;
0789: firePropertyChange(ICON_THEME_PROPERTY, oldTheme, theme);
0790: }
0791:
0792: protected void initializeFromReport() {
0793: final PageDefinition pageDefinition = reportJob
0794: .getPageDefinition();
0795: if (pageDefinition.getPageCount() > 0) {
0796: final PageFormat pageFormat = pageDefinition
0797: .getPageFormat(0);
0798: pageDrawable.setDefaultWidth((int) pageFormat.getWidth());
0799: pageDrawable.setDefaultHeight((int) pageFormat.getHeight());
0800: }
0801:
0802: setTitle(messages.getString(
0803: "PreviewPane.PREVIEW_TITLE", reportJob.getName())); //$NON-NLS-1$
0804:
0805: final Configuration configuration = reportJob
0806: .getConfiguration();
0807: setIconTheme(PreviewPaneUtilities
0808: .createIconTheme(configuration));
0809:
0810: performInitialization(configuration);
0811:
0812: if (deferredRepagination == false) {
0813: startPagination();
0814: }
0815: }
0816:
0817: protected void initializeWithoutJob() {
0818: setTitle(messages.getString("PreviewPane.EMPTY_TITLE")); //$NON-NLS-1$
0819: final Configuration configuration = JFreeReportBoot
0820: .getInstance().getGlobalConfig();
0821: setIconTheme(PreviewPaneUtilities
0822: .createIconTheme(configuration));
0823: performInitialization(configuration);
0824: }
0825:
0826: private void performInitialization(final Configuration configuration) {
0827: applyDefinedDimension(configuration);
0828:
0829: zoomModel.clear();
0830: for (int i = 0; i < ZOOM_FACTORS.length; i++) {
0831: zoomModel.add(new Double(ZOOM_FACTORS[i]),
0832: formatZoomText(ZOOM_FACTORS[i]));
0833: }
0834: zoom = ZOOM_FACTORS[DEFAULT_ZOOM_INDEX];
0835: zoomModel.setSelectedKey(new Double(
0836: ZOOM_FACTORS[DEFAULT_ZOOM_INDEX]));
0837:
0838: final HashMap actions = PreviewPaneUtilities
0839: .loadActions(swingGuiContext);
0840:
0841: if ("true".equals(configuration.getConfigProperty(MENUBAR_AVAILABLE_KEY))) //$NON-NLS-1$
0842: {
0843: buildMenu(actions);
0844: } else {
0845: setMenu(EMPTY_MENU);
0846: }
0847:
0848: if (toolBar != null) {
0849: toolbarHolder.remove(toolBar);
0850: }
0851: if ("true".equals(configuration.getConfigProperty(TOOLBAR_AVAILABLE_KEY))) //$NON-NLS-1$
0852: {
0853: final boolean floatable = isToolbarFloatable()
0854: || "true".equals(configuration.getConfigProperty(TOOLBAR_FLOATABLE_KEY)); //$NON-NLS-1$
0855: toolBar = buildToolbar(actions, floatable);
0856: } else {
0857: toolBar = null;
0858: }
0859: if (toolBar != null) {
0860: toolbarHolder.add(toolBar, BorderLayout.NORTH);
0861: }
0862: }
0863:
0864: /**
0865: * Read the defined dimensions from the report's configuration and set them to
0866: * the Dialog. If a maximum size is defined, add a WindowSizeLimiter to check
0867: * the maximum size
0868: *
0869: * @param configuration the report-configuration of this dialog.
0870: */
0871: private void applyDefinedDimension(final Configuration configuration) {
0872: String width = configuration
0873: .getConfigProperty(PREVIEW_PREFERRED_WIDTH);
0874: String height = configuration
0875: .getConfigProperty(PREVIEW_PREFERRED_HEIGHT);
0876:
0877: // only apply if both values are set.
0878: if (width != null && height != null) {
0879: try {
0880: final Dimension pref = createCorrectedDimensions(
0881: Integer.parseInt(width), Integer
0882: .parseInt(height));
0883: setPreferredSize(pref);
0884: } catch (Exception nfe) {
0885: Log
0886: .warn("Preferred viewport size is defined, but the specified values are invalid."); //$NON-NLS-1$
0887: }
0888: }
0889:
0890: width = configuration.getConfigProperty(PREVIEW_MAXIMUM_WIDTH);
0891: height = configuration
0892: .getConfigProperty(PREVIEW_MAXIMUM_HEIGHT);
0893:
0894: removeComponentListener(sizeLimiter);
0895:
0896: // only apply if at least one value is set.
0897: if (width != null || height != null) {
0898: try {
0899: final int iWidth = (width == null) ? Short.MAX_VALUE
0900: : (int) parseRelativeFloat(width);
0901: final int iHeight = (height == null) ? Short.MAX_VALUE
0902: : (int) parseRelativeFloat(height);
0903: final Dimension pref = createCorrectedDimensions(
0904: iWidth, iHeight);
0905: setMaximumSize(pref);
0906: addComponentListener(sizeLimiter);
0907: } catch (Exception nfe) {
0908: Log
0909: .warn("Maximum viewport size is defined, but the specified values are invalid."); //$NON-NLS-1$
0910: }
0911: }
0912: }
0913:
0914: protected float parseRelativeFloat(final String value) {
0915: if (value == null) {
0916: throw new NumberFormatException();
0917: }
0918: final String tvalue = value.trim();
0919: if (tvalue.length() > 0
0920: && tvalue.charAt(tvalue.length() - 1) == '%') //$NON-NLS-1$
0921: {
0922: final String number = tvalue.substring(0,
0923: tvalue.length() - 1); //$NON-NLS-1$
0924: return Float.parseFloat(number) * -1.0f;
0925: } else {
0926: return Float.parseFloat(tvalue);
0927: }
0928: }
0929:
0930: /**
0931: * Correct the given width and height. If the values are negative, the height
0932: * and width is considered a proportional value where -100 corresponds to
0933: * 100%. The proportional attributes are given is relation to the screen width
0934: * and height.
0935: *
0936: * @param w the to be corrected width
0937: * @param h the height that should be corrected
0938: * @return the dimension of width and height, where all relative values are
0939: * normalized.
0940: */
0941: private Dimension createCorrectedDimensions(int w, int h) {
0942: final Dimension screenSize = Toolkit.getDefaultToolkit()
0943: .getScreenSize();
0944: if (w < 0) {
0945: w = (w * screenSize.width / -100);
0946: }
0947: if (h < 0) {
0948: h = (h * screenSize.height / -100);
0949: }
0950: return new Dimension(w, h);
0951: }
0952:
0953: private JToolBar buildToolbar(final HashMap actions,
0954: final boolean floatable) {
0955: final JToolBar toolBar = new JToolBar();
0956: toolBar.setFloatable(floatable);
0957:
0958: final ArrayList list = new ArrayList();
0959: final Iterator iterator = actions.values().iterator();
0960: while (iterator.hasNext()) {
0961: final ActionPlugin[] plugins = (ActionPlugin[]) iterator
0962: .next();
0963: for (int i = 0; i < plugins.length; i++) {
0964: list.add(plugins[i]);
0965: }
0966: }
0967: final ActionPlugin[] plugins = (ActionPlugin[]) list
0968: .toArray(new ActionPlugin[list.size()]);
0969: Arrays.sort(plugins, new ActionPluginComparator());
0970: PreviewPaneUtilities
0971: .addActionsToToolBar(toolBar, plugins, this );
0972: return toolBar;
0973: }
0974:
0975: public void setToolbarFloatable(final boolean toolbarFloatable) {
0976: this .toolbarFloatable = toolbarFloatable;
0977: }
0978:
0979: public boolean isToolbarFloatable() {
0980: return toolbarFloatable;
0981: }
0982:
0983: private void closeToolbar() {
0984: if (toolBar == null) {
0985: return;
0986: }
0987: if (toolBar.getParent() != toolbarHolder) {
0988: // ha!, we detected that the toolbar is floating ...
0989: // Log.debug (currentToolbar.getParent());
0990: final Window w = SwingUtilities.windowForComponent(toolBar);
0991: if (w != null) {
0992: w.setVisible(false);
0993: w.dispose();
0994: }
0995: }
0996: toolBar.setVisible(false);
0997: }
0998:
0999: public SwingGuiContext getSwingGuiContext() {
1000: return swingGuiContext;
1001: }
1002:
1003: public KeyedComboBoxModel getZoomModel() {
1004: return zoomModel;
1005: }
1006:
1007: private String formatZoomText(final double zoom) {
1008: final NumberFormat numberFormat = NumberFormat
1009: .getPercentInstance(swingGuiContext.getLocale());
1010: return (numberFormat.format(zoom));
1011: }
1012:
1013: private void buildMenu(final HashMap actions) {
1014: final HashMap menus = new HashMap();
1015: final int userPos = getUserDefinedCategoryPosition();
1016:
1017: boolean insertedUserDefinedActions = false;
1018: int catCount = 0;
1019: final Iterator iterator = actions.entrySet().iterator();
1020: final ArrayList collectedCategories = new ArrayList();
1021: while (iterator.hasNext()) {
1022: final Map.Entry entry = (Map.Entry) iterator.next();
1023: final ActionCategory cat = (ActionCategory) entry.getKey();
1024: collectedCategories.add(cat);
1025:
1026: final ActionPlugin[] plugins = (ActionPlugin[]) entry
1027: .getValue();
1028:
1029: if (insertedUserDefinedActions == false
1030: && cat.getPosition() > userPos) {
1031: final ReportController controller = getReportController();
1032: if (controller != null) {
1033: controller.initialize(this );
1034: final JMenu[] controlerMenus = controller
1035: .getMenus();
1036: for (int i = 0; i < controlerMenus.length; i++) {
1037: final ActionCategory userCategory = new ActionCategory();
1038: userCategory.setName("X-User-Category-" + i); //$NON-NLS-1$
1039: userCategory.setPosition(userPos + i);
1040: userCategory.setUserDefined(true);
1041: menus.put(userCategory, controlerMenus[i]);
1042: collectedCategories.add(userCategory);
1043: }
1044: }
1045:
1046: insertedUserDefinedActions = true;
1047: }
1048:
1049: final JMenu menu = PreviewPaneUtilities.createMenu(cat);
1050: PreviewPaneUtilities.buildMenu(menu, plugins, this );
1051: menus.put(cat, menu);
1052: }
1053:
1054: final ActionCategory[] categories = (ActionCategory[]) collectedCategories
1055: .toArray(new ActionCategory[collectedCategories.size()]);
1056: final CategoryTreeItem[] categoryTreeItems = PreviewPaneUtilities
1057: .buildMenuTree(categories);
1058:
1059: final ArrayList menuList = new ArrayList();
1060: for (int i = 0; i < categoryTreeItems.length; i++) {
1061: final CategoryTreeItem item = categoryTreeItems[i];
1062: final JMenu menu = (JMenu) menus.get(item.getCategory());
1063: // now connect all menus ..
1064: final CategoryTreeItem[] childs = item.getChilds();
1065: Arrays.sort(childs);
1066: for (int j = 0; j < childs.length; j++) {
1067: final CategoryTreeItem child = childs[j];
1068: final JMenu childMenu = (JMenu) menus.get(child
1069: .getCategory());
1070: if (childMenu != null) {
1071: menu.add(childMenu);
1072: }
1073: }
1074:
1075: if (item.getParent() == null) {
1076: menuList.add(item);
1077: }
1078: }
1079:
1080: Collections.sort(menuList);
1081: final ArrayList retval = new ArrayList();
1082: for (int i = 0; i < menuList.size(); i++) {
1083: final CategoryTreeItem item = (CategoryTreeItem) menuList
1084: .get(i);
1085: final JMenu menu = (JMenu) menus.get(item.getCategory());
1086: if (item.getCategory().isUserDefined()
1087: || menu.getItemCount() > 0) {
1088: retval.add(menu);
1089: }
1090: }
1091:
1092: setMenu((JMenu[]) retval.toArray(new JMenu[retval.size()]));
1093: }
1094:
1095: public String getTitle() {
1096: return title;
1097: }
1098:
1099: public void setTitle(final String title) {
1100: final String oldTitle = this .title;
1101: this .title = title;
1102: firePropertyChange(TITLE_PROPERTY, oldTitle, title);
1103: }
1104:
1105: public double[] getZoomFactors() {
1106: return (double[]) ZOOM_FACTORS.clone();
1107: }
1108:
1109: public boolean isPaginated() {
1110: return paginated;
1111: }
1112:
1113: public void setPaginated(final boolean paginated) {
1114: final boolean oldPaginated = this .paginated;
1115: this .paginated = paginated;
1116: firePropertyChange(PAGINATED_PROPERTY, oldPaginated, paginated);
1117: }
1118:
1119: public boolean isPaginating() {
1120: return paginating;
1121: }
1122:
1123: public void setPaginating(final boolean paginating) {
1124: final boolean oldPaginating = this .paginating;
1125: this .paginating = paginating;
1126: firePropertyChange(PAGINATING_PROPERTY, oldPaginating,
1127: paginating);
1128: firePropertyChange(LOCK_INTERFACE_PROPERTY, oldPaginating,
1129: paginating);
1130: }
1131:
1132: public synchronized void startPagination() {
1133: if (paginationWorker != null) {
1134: // make sure that old pagination handler does not run longer than
1135: // necessary..
1136: //noinspection SynchronizeOnNonFinalField
1137: synchronized (paginationWorker) {
1138: paginationWorker.finish();
1139:
1140: while (paginationWorker.isAvailable() == false) {
1141: try {
1142: paginationWorker.wait(500);
1143: } catch (InterruptedException e) {
1144: // Got interrupted while waiting ...
1145: }
1146: }
1147: }
1148:
1149: paginationWorker = null;
1150: }
1151:
1152: if (printReportProcessor != null) {
1153: printReportProcessor.close();
1154: printReportProcessor = null;
1155: }
1156:
1157: // Reset the pagination to automatic mode now. This way changes to the page-format will trigger
1158: // pagination again in a safe manor.
1159: deferredRepagination = false;
1160:
1161: try {
1162: final JFreeReport reportJob = getReportJob();
1163: printReportProcessor = new PrintReportProcessor(reportJob);
1164:
1165: paginationWorker = new Worker();
1166: paginationWorker.setWorkload(new RepaginationRunnable(
1167: printReportProcessor));
1168: } catch (ReportProcessingException e) {
1169: Log.error("Unable to start report pagination:", e);
1170: }
1171:
1172: }
1173:
1174: public Drawable getNoReportDrawable() {
1175: return noReportDrawable;
1176: }
1177:
1178: public void setNoReportDrawable(final Drawable noReportDrawable) {
1179: this .noReportDrawable = noReportDrawable;
1180: }
1181:
1182: public Drawable getPaginatingDrawable() {
1183: return paginatingDrawable;
1184: }
1185:
1186: public void setPaginatingDrawable(final Drawable paginatingDrawable) {
1187: this .paginatingDrawable = paginatingDrawable;
1188: }
1189:
1190: protected void updateVisiblePage(final int pageNumber) {
1191: //
1192: if (printReportProcessor == null) {
1193: throw new IllegalStateException();
1194: }
1195:
1196: // todo: This can be very expensive - so we better move this off the event-dispatcher
1197: final int pageIndex = getPageNumber() - 1;
1198: if (pageIndex < 0
1199: || pageIndex >= printReportProcessor.getNumberOfPages()) {
1200: this .pageDrawable.setBackend(null);
1201: this .drawablePanel.setDrawable(pageDrawable);
1202: } else {
1203: final PageDrawable drawable = printReportProcessor
1204: .getPageDrawable(pageIndex);
1205: this .pageDrawable.setBackend(drawable);
1206: this .drawablePanel.setDrawable(pageDrawable);
1207: }
1208: }
1209:
1210: private StatusListener getStatusListener() {
1211: return statusListener;
1212: }
1213:
1214: public void addReportProgressListener(
1215: final ReportProgressListener progressListener) {
1216: if (progressListener == null) {
1217: throw new NullPointerException();
1218: }
1219:
1220: if (reportProgressListener == null) {
1221: reportProgressListener = new ArrayList();
1222: }
1223: reportProgressListener.add(progressListener);
1224: }
1225:
1226: public void removeReportProgressListener(
1227: final ReportProgressListener progressListener) {
1228: if (reportProgressListener == null) {
1229: return;
1230: }
1231: reportProgressListener.remove(progressListener);
1232: }
1233:
1234: protected void forwardReportStartedEvent(
1235: final ReportProgressEvent event) {
1236: if (reportProgressListener == null) {
1237: return;
1238: }
1239: for (int i = 0; i < reportProgressListener.size(); i++) {
1240: final ReportProgressListener listener = (ReportProgressListener) reportProgressListener
1241: .get(i);
1242: listener.reportProcessingStarted(event);
1243: }
1244: }
1245:
1246: protected void forwardReportUpdateEvent(
1247: final ReportProgressEvent event) {
1248: if (reportProgressListener == null) {
1249: return;
1250: }
1251: for (int i = 0; i < reportProgressListener.size(); i++) {
1252: final ReportProgressListener listener = (ReportProgressListener) reportProgressListener
1253: .get(i);
1254: listener.reportProcessingUpdate(event);
1255: }
1256: }
1257:
1258: protected void forwardReportFinishedEvent(
1259: final ReportProgressEvent event) {
1260: if (reportProgressListener == null) {
1261: return;
1262: }
1263: for (int i = 0; i < reportProgressListener.size(); i++) {
1264: final ReportProgressListener listener = (ReportProgressListener) reportProgressListener
1265: .get(i);
1266: listener.reportProcessingFinished(event);
1267: }
1268: }
1269:
1270: private double getMaximumZoom(final Configuration configuration) {
1271: final String value = configuration
1272: .getConfigProperty(ZOOM_MAXIMUM_KEY);
1273: return StringUtil.parseFloat(value, ZOOM_MAXIMUM_DEFAULT);
1274: }
1275:
1276: private double getMinimumZoom(final Configuration configuration) {
1277: final String value = configuration
1278: .getConfigProperty(ZOOM_MINIMUM_KEY);
1279: return StringUtil.parseFloat(value, ZOOM_MINIMUM_DEFAULT);
1280: }
1281: }
|