/* * This program is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software * Foundation. * * You should have received a copy of the GNU Lesser General Public License along with this * program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html * or from the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU Lesser General Public License for more details. * * Copyright (c) 2001 - 2016 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved. */ package org.pentaho.reporting.engine.classic.core.modules.gui.base; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Cursor; import java.awt.Dimension; import java.awt.IllegalComponentStateException; import java.awt.Rectangle; import java.awt.Toolkit; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.print.PageFormat; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.text.NumberFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.Locale; import java.util.Map; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JMenu; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.Scrollable; import javax.swing.SwingUtilities; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.pentaho.reporting.engine.classic.core.ClassicEngineBoot; import org.pentaho.reporting.engine.classic.core.MasterReport; import org.pentaho.reporting.engine.classic.core.PageDefinition; import org.pentaho.reporting.engine.classic.core.ReportProcessingException; import org.pentaho.reporting.engine.classic.core.event.ReportProgressEvent; import org.pentaho.reporting.engine.classic.core.event.ReportProgressListener; import org.pentaho.reporting.engine.classic.core.imagemap.ImageMap; import org.pentaho.reporting.engine.classic.core.imagemap.ImageMapEntry; import org.pentaho.reporting.engine.classic.core.layout.model.RenderNode; import org.pentaho.reporting.engine.classic.core.layout.model.RenderableReplacedContentBox; import org.pentaho.reporting.engine.classic.core.layout.output.RenderUtility; import org.pentaho.reporting.engine.classic.core.modules.gui.base.actions.ZoomAction; import org.pentaho.reporting.engine.classic.core.modules.gui.base.event.ReportHyperlinkEvent; import org.pentaho.reporting.engine.classic.core.modules.gui.base.event.ReportHyperlinkListener; import org.pentaho.reporting.engine.classic.core.modules.gui.base.event.ReportMouseEvent; import org.pentaho.reporting.engine.classic.core.modules.gui.base.event.ReportMouseListener; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.ActionCategory; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.ActionPluginComparator; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.CategoryTreeItem; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.PageBackgroundDrawable; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.PreviewDrawablePanel; import org.pentaho.reporting.engine.classic.core.modules.gui.base.internal.PreviewPaneUtilities; import org.pentaho.reporting.engine.classic.core.modules.gui.common.IconTheme; import org.pentaho.reporting.engine.classic.core.modules.gui.common.StatusListener; import org.pentaho.reporting.engine.classic.core.modules.gui.common.StatusType; import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ActionPlugin; import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.CenterLayout; import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ReportEventSource; import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingGuiContext; import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.WindowSizeLimiter; import org.pentaho.reporting.engine.classic.core.modules.output.pageable.graphics.PageDrawable; import org.pentaho.reporting.engine.classic.core.modules.output.pageable.graphics.PrintReportProcessor; import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys; import org.pentaho.reporting.engine.classic.core.util.Worker; import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility; import org.pentaho.reporting.libraries.base.config.Configuration; import org.pentaho.reporting.libraries.base.util.Messages; import org.pentaho.reporting.libraries.base.util.ObjectUtilities; import org.pentaho.reporting.libraries.designtime.swing.KeyedComboBoxModel; import org.pentaho.reporting.libraries.designtime.swing.LibSwingUtil; import org.pentaho.reporting.libraries.xmlns.LibXmlInfo; import org.pentaho.reporting.libraries.xmlns.common.ParserUtil; /** * Creation-Date: 11.11.2006, 19:36:13 * * @author Thomas Morgner */ public class PreviewPane extends JPanel implements ReportEventSource { private static final Log logger = LogFactory.getLog( PreviewPane.class ); private class PreviewGuiContext implements SwingGuiContext { protected PreviewGuiContext() { } public Window getWindow() { return LibSwingUtil.getWindowAncestor( PreviewPane.this ); } public Locale getLocale() { final MasterReport report = getReportJob(); if ( report != null ) { final Locale bundleLocale = report.getResourceBundleFactory().getLocale(); if ( bundleLocale != null ) { return bundleLocale; } return report.getReportEnvironment().getLocale(); } return Locale.getDefault(); } public IconTheme getIconTheme() { return PreviewPane.this.getIconTheme(); } public Configuration getConfiguration() { return PreviewPane.this.computeContextConfiguration(); } public StatusListener getStatusListener() { return PreviewPane.this.getStatusListener(); } public ReportEventSource getEventSource() { return PreviewPane.this; } } private class PreviewPaneStatusUpdater implements Runnable { private StatusType type; private String text; private Throwable error; protected PreviewPaneStatusUpdater( final StatusType type, final String text, final Throwable error ) { this.type = type; this.text = text; this.error = error; } public Throwable getError() { return error; } public StatusType getType() { return type; } public String getText() { return text; } /** * When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread * causes the object's <code>run</code> method to be called in that separately executing thread. * <p/> * The general contract of the method <code>run</code> is that it may take any action whatsoever. * * @see Thread#run() */ public void run() { setStatusType( type ); setStatusText( text ); setError( error ); } } /** * The StatusListener here shields the preview pane from any attempt to tamper with it. */ private class PreviewPaneStatusListener implements StatusListener { protected PreviewPaneStatusListener() { } public void setStatus( final StatusType type, final String text, final Throwable error ) { if ( SwingUtilities.isEventDispatchThread() ) { setStatusType( type ); setStatusText( text ); setError( error ); } else { SwingUtilities.invokeLater( new PreviewPaneStatusUpdater( type, text, error ) ); } } } private class RepaginationRunnable implements Runnable, ReportProgressListener { private PrintReportProcessor processor; protected RepaginationRunnable( final PrintReportProcessor processor ) { this.processor = processor; } public void reportProcessingStarted( final ReportProgressEvent event ) { forwardReportStartedEvent( event ); } public void reportProcessingUpdate( final ReportProgressEvent event ) { forwardReportUpdateEvent( event ); } public void reportProcessingFinished( final ReportProgressEvent event ) { forwardReportFinishedEvent( event ); } /** * When an object implementing interface <code>Runnable</code> is used to create a thread, starting the thread * causes the object's <code>run</code> method to be called in that separately executing thread. * <p/> * The general contract of the method <code>run</code> is that it may take any action whatsoever. * * @see Thread#run() */ public void run() { this.processor.addReportProgressListener( this ); try { final UpdatePaginatingPropertyHandler startPaginationNotify = new UpdatePaginatingPropertyHandler( processor, true, false, 0 ); if ( SwingUtilities.isEventDispatchThread() ) { startPaginationNotify.run(); } else { SwingUtilities.invokeLater( startPaginationNotify ); } // Perform the pagination .. final int pageCount = processor.getNumberOfPages(); final UpdatePaginatingPropertyHandler endPaginationNotify = new UpdatePaginatingPropertyHandler( processor, false, true, pageCount ); if ( SwingUtilities.isEventDispatchThread() ) { endPaginationNotify.run(); } else { SwingUtilities.invokeLater( endPaginationNotify ); } } catch ( Exception e ) { final UpdatePaginatingPropertyHandler endPaginationNotify = new UpdatePaginatingPropertyHandler( processor, false, false, 0 ); if ( SwingUtilities.isEventDispatchThread() ) { endPaginationNotify.run(); } else { SwingUtilities.invokeLater( endPaginationNotify ); } PreviewPane.logger.error( "Pagination failed.", e ); //$NON-NLS-1$ } finally { this.processor.removeReportProgressListener( this ); } } } private class UpdatePaginatingPropertyHandler implements Runnable { private boolean paginating; private boolean paginated; private int pageCount; private PrintReportProcessor processor; protected UpdatePaginatingPropertyHandler( final PrintReportProcessor processor, final boolean paginating, final boolean paginated, final int pageCount ) { this.processor = processor; this.paginating = paginating; this.paginated = paginated; this.pageCount = pageCount; } public void run() { if ( processor != getPrintReportProcessor() ) { PreviewPane.logger.debug( messages.getString( "PreviewPane.DEBUG_NO_LONGER_VALID" ) ); //$NON-NLS-1$ return; } PreviewPane.logger.debug( messages.getString( "PreviewPane.DEBUG_PAGINATION", String.valueOf( paginating ), String.valueOf( pageCount ) ) ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ if ( paginating == false ) { setNumberOfPages( pageCount ); if ( getPageNumber() < 1 ) { setPageNumber( 1 ); } else if ( getPageNumber() > pageCount ) { setPageNumber( pageCount ); } } setPaginating( paginating ); setPaginated( paginated ); if ( processor.isError() ) { setError( processor.getErrorReason() ); setStatusType( StatusType.ERROR ); setStatusText( processor.getErrorReason().getLocalizedMessage() ); } } } private class PreviewUpdateHandler implements PropertyChangeListener { protected PreviewUpdateHandler() { } public void propertyChange( final PropertyChangeEvent evt ) { final String propertyName = evt.getPropertyName(); if ( PreviewPane.PAGINATING_PROPERTY.equals( propertyName ) ) { if ( isPaginating() ) { PreviewPane.this.getReportPreviewArea().setDrawableAsRawObject( getPaginatingDrawable() ); } else { updateVisiblePage( getPageNumber() ); } } else if ( PreviewPane.REPORT_JOB_PROPERTY.equals( propertyName ) ) { if ( getReportJob() == null ) { PreviewPane.this.getReportPreviewArea().setDrawableAsRawObject( getNoReportDrawable() ); } // else the paginating property will be fired anyway .. } else if ( PreviewPane.PAGE_NUMBER_PROPERTY.equals( propertyName ) ) { if ( isPaginating() ) { return; } updateVisiblePage( getPageNumber() ); } } } private class UpdateZoomHandler implements PropertyChangeListener { protected UpdateZoomHandler() { } /** * This method gets called when a bound property is changed. * * @param evt * A PropertyChangeEvent object describing the event source and the property that has changed. */ public void propertyChange( final PropertyChangeEvent evt ) { if ( "zoom".equals( evt.getPropertyName() ) == false ) { //$NON-NLS-1$ return; } final double zoom = getZoom(); pageDrawable.setZoom( zoom ); final KeyedComboBoxModel<Double, String> zoomModel = PreviewPane.this.getZoomModel(); zoomModel.setSelectedKey( new Double( zoom ) ); if ( zoomModel.getSelectedKey() == null ) { zoomModel.setSelectedItem( formatZoomText( zoom ) ); } drawablePanel.revalidate(); } } /** * Maps incomming report-mouse events into Hyperlink events. */ private class HyperLinkEventProcessor implements ReportMouseListener { private boolean mouseLinkActive; private HyperLinkEventProcessor() { } public void reportMouseClicked( final ReportMouseEvent event ) { final RenderNode renderNode = event.getSourceNode(); final String target = extractLink( renderNode, event ); if ( target == null ) { return; } final String window = (String) renderNode.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_WINDOW ); final String title = (String) renderNode.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_TITLE ); final ReportHyperlinkEvent hyEvent = new ReportHyperlinkEvent( PreviewPane.this, renderNode, target, window, title ); fireReportHyperlinkEvent( hyEvent ); } private String extractLink( final RenderNode node, final ReportMouseEvent event ) { if ( node instanceof RenderableReplacedContentBox ) { // process image map final ImageMap imageMap = RenderUtility.extractImageMap( (RenderableReplacedContentBox) node ); if ( imageMap != null ) { final PageDrawable physicalPageDrawable = drawablePanel.getPageDrawable(); final PageFormat pf = physicalPageDrawable.getPageFormat(); final float x1 = (float) ( event.getSourceEvent().getX() / zoom ); final float y1 = (float) ( event.getSourceEvent().getY() / zoom ); final float imageMapX = (float) ( x1 - pf.getImageableX() - StrictGeomUtility.toExternalValue( node.getX() ) ); final float imageMapY = (float) ( y1 - pf.getImageableY() - StrictGeomUtility.toExternalValue( node.getY() ) ); final ImageMapEntry[] imageMapEntries = imageMap.getEntriesForPoint( imageMapX, imageMapY ); for ( int i = 0; i < imageMapEntries.length; i++ ) { final ImageMapEntry entry = imageMapEntries[i]; final Object imageMapTarget = entry.getAttribute( LibXmlInfo.XHTML_NAMESPACE, "href" ); if ( imageMapTarget != null ) { return String.valueOf( imageMapTarget ); } } } } final String target = (String) node.getStyleSheet().getStyleProperty( ElementStyleKeys.HREF_TARGET ); if ( target == null ) { return null; } return target; } public void reportMousePressed( final ReportMouseEvent event ) { // not used. } public void reportMouseReleased( final ReportMouseEvent event ) { // not used. } public void reportMouseMoved( final ReportMouseEvent event ) { if ( isHyperlinkSystemActive() == false ) { return; } final RenderNode renderNode = event.getSourceNode(); final String target = extractLink( renderNode, event ); if ( target == null ) { if ( mouseLinkActive ) { setCursor( Cursor.getDefaultCursor() ); mouseLinkActive = false; } return; } if ( mouseLinkActive == false ) { setCursor( Cursor.getPredefinedCursor( Cursor.HAND_CURSOR ) ); mouseLinkActive = true; } } public void reportMouseDragged( final ReportMouseEvent event ) { } } private static class ScrollablePanel extends JPanel implements Scrollable { /** * Creates a new <code>JPanel</code> with a double buffer and a flow layout. */ private ScrollablePanel() { setLayout( new CenterLayout() ); } /** * Returns the preferred size of the viewport for a view component. For example, the preferred size of a * <code>JList</code> component is the size required to accommodate all of the cells in its list. However, the value * of <code>preferredScrollableViewportSize</code> is the size required for <code>JList.getVisibleRowCount</code> * rows. A component without any properties that would affect the viewport size should just return * <code>getPreferredSize</code> here. * * @return the preferredSize of a <code>JViewport</code> whose view is this <code>Scrollable</code> * @see javax.swing.JViewport#getPreferredSize */ public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } /** * Components that display logical rows or columns should compute the scroll increment that will completely expose * one new row or column, depending on the value of orientation. Ideally, components should handle a partially * exposed row or column by returning the distance required to completely expose the item. * <p/> * Scrolling containers, like JScrollPane, will use this method each time the user requests a unit scroll. * * @param visibleRect * The view area visible within the viewport * @param orientation * Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL. * @param direction * Less than zero to scroll up/left, greater than zero for down/right. * @return The "unit" increment for scrolling in the specified direction. This value should always be positive. */ public int getScrollableUnitIncrement( final Rectangle visibleRect, final int orientation, final int direction ) { return 20; } /** * Components that display logical rows or columns should compute the scroll increment that will completely expose * one block of rows or columns, depending on the value of orientation. * <p/> * Scrolling containers, like JScrollPane, will use this method each time the user requests a block scroll. * * @param visibleRect * The view area visible within the viewport * @param orientation * Either SwingConstants.VERTICAL or SwingConstants.HORIZONTAL. * @param direction * Less than zero to scroll up/left, greater than zero for down/right. * @return The "block" increment for scrolling in the specified direction. This value should always be positive. */ public int getScrollableBlockIncrement( final Rectangle visibleRect, final int orientation, final int direction ) { return 100; } /** * Return true if a viewport should always force the width of this <code>Scrollable</code> to match the width of the * viewport. For example a normal text view that supported line wrapping would return true here, since it would be * undesirable for wrapped lines to disappear beyond the right edge of the viewport. Note that returning true for a * Scrollable whose ancestor is a JScrollPane effectively disables horizontal scrolling. * <p/> * Scrolling containers, like JViewport, will use this method each time they are validated. * * @return True if a viewport should force the Scrollables width to match its own. */ public boolean getScrollableTracksViewportWidth() { return false; } /** * Return true if a viewport should always force the height of this Scrollable to match the height of the viewport. * For example a columnar text view that flowed text in left to right columns could effectively disable vertical * scrolling by returning true here. * <p/> * Scrolling containers, like JViewport, will use this method each time they are validated. * * @return True if a viewport should force the Scrollables height to match its own. */ public boolean getScrollableTracksViewportHeight() { return false; } } /** * A zoom select action. */ private static class ZoomSelectAction extends AbstractAction { private KeyedComboBoxModel source; private PreviewPane pane; /** * Creates a new action. */ protected ZoomSelectAction( final KeyedComboBoxModel source, final PreviewPane pane ) { this.source = source; this.pane = pane; } /** * Invoked when an action occurs. * * @param e * the event. */ public void actionPerformed( final ActionEvent e ) { final Double selected = (Double) source.getSelectedKey(); if ( selected != null ) { pane.setZoom( selected.doubleValue() ); } } } private static final double[] ZOOM_FACTORS = { 0.5, 0.75, 1, 1.25, 1.50, 2.00 }; private static final int DEFAULT_ZOOM_INDEX = 2; public static final String STATUS_TEXT_PROPERTY = "statusText"; //$NON-NLS-1$ public static final String STATUS_TYPE_PROPERTY = "statusType"; //$NON-NLS-1$ public static final String REPORT_CONTROLLER_PROPERTY = "reportController"; //$NON-NLS-1$ public static final String ZOOM_PROPERTY = "zoom"; //$NON-NLS-1$ public static final String CLOSED_PROPERTY = "closed"; //$NON-NLS-1$ public static final String ERROR_PROPERTY = "error"; //$NON-NLS-1$ public static final String REPORT_JOB_PROPERTY = "reportJob"; //$NON-NLS-1$ public static final String PAGINATING_PROPERTY = "paginating"; //$NON-NLS-1$ public static final String PAGINATED_PROPERTY = "paginated"; //$NON-NLS-1$ public static final String PAGE_NUMBER_PROPERTY = "pageNumber"; //$NON-NLS-1$ public static final String NUMBER_OF_PAGES_PROPERTY = "numberOfPages"; //$NON-NLS-1$ public static final String ICON_THEME_PROPERTY = "iconTheme"; //$NON-NLS-1$ public static final String TITLE_PROPERTY = "title"; //$NON-NLS-1$ public static final String MENU_PROPERTY = "menu"; //$NON-NLS-1$ /** * The preferred width key. */ public static final String PREVIEW_PREFERRED_WIDTH = "org.pentaho.reporting.engine.classic.core.modules.gui.base.PreferredWidth"; //$NON-NLS-1$ /** * The preferred height key. */ public static final String PREVIEW_PREFERRED_HEIGHT = "org.pentaho.reporting.engine.classic.core.modules.gui.base.PreferredHeight"; //$NON-NLS-1$ /** * The maximum width key. */ public static final String PREVIEW_MAXIMUM_WIDTH = "org.pentaho.reporting.engine.classic.core.modules.gui.base.MaximumWidth"; //$NON-NLS-1$ /** * The maximum height key. */ public static final String PREVIEW_MAXIMUM_HEIGHT = "org.pentaho.reporting.engine.classic.core.modules.gui.base.MaximumHeight"; //$NON-NLS-1$ /** * The maximum zoom key. */ public static final String ZOOM_MAXIMUM_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.base.MaximumZoom"; //$NON-NLS-1$ /** * The minimum zoom key. */ public static final String ZOOM_MINIMUM_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.base.MinimumZoom"; //$NON-NLS-1$ /** * The default maximum zoom. */ private static final float ZOOM_MAXIMUM_DEFAULT = 20.0f; // 2000% /** * The default minimum zoom. */ private static final float ZOOM_MINIMUM_DEFAULT = 0.01f; // 1% private static final String MENUBAR_AVAILABLE_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.base.MenuBarAvailable"; //$NON-NLS-1$ private static final String TOOLBAR_AVAILABLE_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.base.ToolbarAvailable"; //$NON-NLS-1$ private static final String TOOLBAR_FLOATABLE_KEY = "org.pentaho.reporting.engine.classic.core.modules.gui.base.ToolbarFloatable"; //$NON-NLS-1$ private Object paginatingDrawable; private Object noReportDrawable; private PageBackgroundDrawable pageDrawable; private PreviewDrawablePanel drawablePanel; private ReportController reportController; private JMenu[] menus; private JToolBar toolBar; private String statusText; private Throwable error; private String title; private StatusType statusType; private boolean closed; private MasterReport reportJob; private int numberOfPages; private int pageNumber; private SwingGuiContext swingGuiContext; private IconTheme iconTheme; private double zoom; private boolean paginating; private boolean paginated; private PrintReportProcessor printReportProcessor; private Worker paginationWorker; private JPanel toolbarHolder; private JPanel outerReportControllerHolder; private boolean reportControllerInner; private String reportControllerLocation; private JComponent reportControllerComponent; private KeyedComboBoxModel<Double, String> zoomModel; private PreviewPane.PreviewPaneStatusListener statusListener; private static final JMenu[] EMPTY_MENU = new JMenu[0]; private boolean toolbarFloatable; private ArrayList reportProgressListener; private double maxZoom; private double minZoom; private Messages messages; private WindowSizeLimiter sizeLimiter; private boolean deferredRepagination; private ArrayList hyperlinkListeners; private transient ReportHyperlinkListener[] cachedHyperlinkListeners; private Map<ActionCategory, ActionPlugin[]> actionPlugins; private Map<ActionCategory, ZoomAction[]> zoomActions; private JComboBox zoomSelectorBox; private JScrollPane reportPaneScrollPane; private int reportControllerSliderSize; private boolean performInitializationRunning; /** * Creates a new <code>JPanel</code> with a double buffer and a flow layout. */ public PreviewPane() { this( true ); } public PreviewPane( final boolean init ) { messages = new Messages( getLocale(), SwingPreviewModule.BUNDLE_NAME, ObjectUtilities .getClassLoader( SwingPreviewModule.class ) ); sizeLimiter = new WindowSizeLimiter(); zoomActions = new HashMap<ActionCategory, ZoomAction[]>(); this.menus = PreviewPane.EMPTY_MENU; setLayout( new BorderLayout() ); zoomModel = new KeyedComboBoxModel(); zoomModel.setAllowOtherValue( true ); zoom = PreviewPane.ZOOM_FACTORS[PreviewPane.DEFAULT_ZOOM_INDEX]; final Configuration configuration = ClassicEngineBoot.getInstance().getGlobalConfig(); minZoom = getMinimumZoom( configuration ); maxZoom = getMaximumZoom( configuration ); pageDrawable = new PageBackgroundDrawable(); drawablePanel = new PreviewDrawablePanel(); drawablePanel.setOpaque( false ); drawablePanel.setBackground( null ); drawablePanel.setDoubleBuffered( true ); drawablePanel.setDrawableAsRawObject( pageDrawable ); drawablePanel.addReportMouseListener( new HyperLinkEventProcessor() ); swingGuiContext = new PreviewGuiContext(); final JPanel reportPaneHolder = new ScrollablePanel(); reportPaneHolder.setOpaque( false ); reportPaneHolder.setBackground( null ); reportPaneHolder.setBorder( BorderFactory.createEmptyBorder( 10, 10, 10, 10 ) ); reportPaneHolder.add( drawablePanel ); reportPaneScrollPane = new JScrollPane( reportPaneHolder ); reportPaneScrollPane.getVerticalScrollBar().setUnitIncrement( 20 ); reportPaneScrollPane.setBackground( null ); reportPaneScrollPane.setOpaque( false ); reportPaneScrollPane.getViewport().setOpaque( false ); ( (JComponent) reportPaneScrollPane.getViewport().getView() ).setOpaque( false ); toolbarHolder = new JPanel(); toolbarHolder.setLayout( new BorderLayout() ); outerReportControllerHolder = new JPanel(); outerReportControllerHolder.setOpaque( false ); outerReportControllerHolder.setBackground( null ); outerReportControllerHolder.setLayout( new BorderLayout() ); outerReportControllerHolder.add( toolbarHolder, BorderLayout.NORTH ); outerReportControllerHolder.add( reportPaneScrollPane, BorderLayout.CENTER ); add( outerReportControllerHolder, BorderLayout.CENTER ); addPropertyChangeListener( new PreviewUpdateHandler() ); addPropertyChangeListener( "zoom", new UpdateZoomHandler() ); //$NON-NLS-1$ statusListener = new PreviewPaneStatusListener(); zoomSelectorBox = createZoomSelector( this ); setReportController( new ParameterReportController() ); if ( init ) { initializeWithoutJob(); } } protected JComboBox createZoomSelector( final PreviewPane pane ) { final JComboBox zoomSelect = new JComboBox( pane.getZoomModel() ); zoomSelect.addActionListener( new ZoomSelectAction( pane.getZoomModel(), pane ) ); zoomSelect.setAlignmentX( Component.RIGHT_ALIGNMENT ); return zoomSelect; } public PreviewDrawablePanel getReportPreviewArea() { return drawablePanel; } public boolean isDeferredRepagination() { return deferredRepagination; } public void setDeferredRepagination( final boolean deferredRepagination ) { this.deferredRepagination = deferredRepagination; } public synchronized PrintReportProcessor getPrintReportProcessor() { return printReportProcessor; } protected synchronized void setPrintReportProcessor( final PrintReportProcessor printReportProcessor ) { this.printReportProcessor = printReportProcessor; } public JMenu[] getMenu() { return menus; } protected void setMenu( final JMenu[] menus ) { if ( menus == null ) { throw new NullPointerException(); } final JMenu[] oldmenu = this.menus; this.menus = (JMenu[]) menus.clone(); firePropertyChange( PreviewPane.MENU_PROPERTY, oldmenu, this.menus ); } public JToolBar getToolBar() { return toolBar; } public String getStatusText() { return statusText; } public void setStatusText( final String statusText ) { final String oldStatus = this.statusText; this.statusText = statusText; firePropertyChange( PreviewPane.STATUS_TEXT_PROPERTY, oldStatus, statusText ); } public Throwable getError() { return error; } public void setError( final Throwable error ) { final Throwable oldError = this.error; this.error = error; firePropertyChange( PreviewPane.ERROR_PROPERTY, oldError, error ); } public StatusType getStatusType() { return statusType; } public void setStatusType( final StatusType statusType ) { final StatusType oldType = this.statusType; this.statusType = statusType; firePropertyChange( PreviewPane.STATUS_TYPE_PROPERTY, oldType, statusType ); } public ReportController getReportController() { return reportController; } public void setReportController( final ReportController reportController ) { final ReportController oldController = this.reportController; this.reportController = reportController; firePropertyChange( PreviewPane.REPORT_CONTROLLER_PROPERTY, oldController, reportController ); if ( this.reportController != oldController ) { if ( oldController != null ) { oldController.deinitialize( this ); } // Now add the controller to the GUI .. refreshReportController( reportController ); } } private void refreshReportController( final ReportController newReportController ) { for ( int i = 0; i < outerReportControllerHolder.getComponentCount(); i++ ) { final Component maybeSplitPane = outerReportControllerHolder.getComponent( i ); if ( maybeSplitPane instanceof JSplitPane ) { final JSplitPane splitPane = (JSplitPane) maybeSplitPane; reportControllerSliderSize = splitPane.getDividerLocation(); break; } } if ( newReportController == null ) { if ( reportControllerComponent != null ) { // thats relatively easy. outerReportControllerHolder.removeAll(); outerReportControllerHolder.add( toolbarHolder, BorderLayout.NORTH ); outerReportControllerHolder.add( reportPaneScrollPane, BorderLayout.CENTER ); reportControllerComponent = null; reportControllerInner = false; reportControllerLocation = null; } } else { final JComponent rcp = newReportController.getControlPanel(); if ( rcp == null ) { if ( reportControllerComponent != null ) { outerReportControllerHolder.removeAll(); outerReportControllerHolder.add( toolbarHolder, BorderLayout.NORTH ); outerReportControllerHolder.add( reportPaneScrollPane, BorderLayout.CENTER ); reportControllerComponent = null; reportControllerInner = false; reportControllerLocation = null; } } else if ( reportControllerComponent != rcp || reportControllerInner != newReportController.isInnerComponent() || ObjectUtilities.equal( reportControllerLocation, newReportController.getControllerLocation() ) == false ) { // if either the controller component or its position (inner vs outer) // and border-position has changed, then refresh .. this.reportControllerLocation = newReportController.getControllerLocation(); this.reportControllerInner = newReportController.isInnerComponent(); this.reportControllerComponent = newReportController.getControlPanel(); outerReportControllerHolder.removeAll(); if ( reportControllerInner ) { final JSplitPane innerHolder = new JSplitPane(); innerHolder.setOpaque( false ); if ( BorderLayout.SOUTH.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.VERTICAL_SPLIT ); innerHolder.setTopComponent( reportPaneScrollPane ); innerHolder.setBottomComponent( reportControllerComponent ); } else if ( BorderLayout.EAST.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.HORIZONTAL_SPLIT ); innerHolder.setLeftComponent( reportPaneScrollPane ); innerHolder.setRightComponent( reportControllerComponent ); } else if ( BorderLayout.WEST.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.HORIZONTAL_SPLIT ); innerHolder.setRightComponent( reportPaneScrollPane ); innerHolder.setLeftComponent( reportControllerComponent ); } else { innerHolder.setOrientation( JSplitPane.VERTICAL_SPLIT ); innerHolder.setBottomComponent( reportPaneScrollPane ); innerHolder.setTopComponent( reportControllerComponent ); } if ( reportControllerSliderSize > 0 ) { innerHolder.setDividerLocation( reportControllerSliderSize ); } outerReportControllerHolder.add( toolbarHolder, BorderLayout.NORTH ); outerReportControllerHolder.add( innerHolder, BorderLayout.CENTER ); } else { final JPanel reportPaneHolder = new JPanel(); reportPaneHolder.setOpaque( false ); reportPaneHolder.setLayout( new BorderLayout() ); reportPaneHolder.add( toolbarHolder, BorderLayout.NORTH ); reportPaneHolder.add( reportPaneScrollPane, BorderLayout.CENTER ); final JSplitPane innerHolder = new JSplitPane(); if ( BorderLayout.SOUTH.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.VERTICAL_SPLIT ); innerHolder.setTopComponent( reportPaneHolder ); innerHolder.setBottomComponent( reportControllerComponent ); } else if ( BorderLayout.EAST.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.HORIZONTAL_SPLIT ); innerHolder.setLeftComponent( reportPaneHolder ); innerHolder.setRightComponent( reportControllerComponent ); } else if ( BorderLayout.WEST.equals( reportControllerLocation ) ) { innerHolder.setOrientation( JSplitPane.HORIZONTAL_SPLIT ); innerHolder.setRightComponent( reportPaneHolder ); innerHolder.setLeftComponent( reportControllerComponent ); } else { innerHolder.setOrientation( JSplitPane.VERTICAL_SPLIT ); innerHolder.setBottomComponent( reportPaneHolder ); innerHolder.setTopComponent( reportControllerComponent ); } if ( reportControllerSliderSize > 0 ) { innerHolder.setDividerLocation( reportControllerSliderSize ); } outerReportControllerHolder.add( innerHolder, BorderLayout.CENTER ); } } } } public MasterReport getReportJob() { return reportJob; } public void setReportJob( final MasterReport reportJob ) { final MasterReport oldJob = this.reportJob; this.reportJob = reportJob; firePropertyChange( PreviewPane.REPORT_JOB_PROPERTY, oldJob, reportJob ); if ( reportJob == null ) { killThePaginationWorker(); setPaginated( false ); setPageNumber( 0 ); setNumberOfPages( 0 ); refreshReportController( reportController ); initializeWithoutJob(); } else { refreshReportController( reportController ); initializeFromReport(); } } public double getZoom() { return zoom; } public void setZoom( final double zoom ) { final double oldZoom = this.zoom; this.zoom = Math.max( Math.min( zoom, maxZoom ), minZoom ); if ( this.zoom != oldZoom ) { firePropertyChange( PreviewPane.ZOOM_PROPERTY, oldZoom, zoom ); updateZoomModel( zoom ); } } private void updateZoomModel( final double zoom ) { for ( int i = 0; i < zoomModel.getSize(); i++ ) { final Object o = zoomModel.getKeyAt( i ); if ( o instanceof Double ) { Double d = (Double) o; if ( d.doubleValue() == zoom ) { zoomModel.setSelectedKey( d ); return; } } } zoomModel.setSelectedItem( formatZoomText( zoom ) ); } public boolean isClosed() { return closed; } public void setClosed( final boolean closed ) { final boolean oldClosed = this.closed; this.closed = closed; firePropertyChange( PreviewPane.CLOSED_PROPERTY, oldClosed, closed ); if ( closed ) { prepareShutdown(); } } private void prepareShutdown() { synchronized ( this ) { paginationWorker = null; if ( printReportProcessor != null ) { printReportProcessor.cancel(); printReportProcessor.close(); printReportProcessor = null; } closeToolbar(); } } private int getUserDefinedCategoryPosition() { return ParserUtil.parseInt( swingGuiContext.getConfiguration().getConfigProperty( "org.pentaho.reporting.engine.classic.core.modules.gui.swing.user-defined-category.position" ), 15000 ); //$NON-NLS-1$ } public Locale getLocale() { if ( getParent() == null ) { try { return super.getLocale(); } catch ( IllegalComponentStateException ex ) { return Locale.getDefault(); } } return super.getLocale(); } public int getNumberOfPages() { return numberOfPages; } public void setNumberOfPages( final int numberOfPages ) { final int oldPageNumber = this.numberOfPages; this.numberOfPages = numberOfPages; firePropertyChange( PreviewPane.NUMBER_OF_PAGES_PROPERTY, oldPageNumber, numberOfPages ); } public int getPageNumber() { return pageNumber; } public void setPageNumber( final int pageNumber ) { final int oldPageNumber = this.pageNumber; this.pageNumber = pageNumber; // Log.debug("Setting PageNumber: " + pageNumber); firePropertyChange( PreviewPane.PAGE_NUMBER_PROPERTY, oldPageNumber, pageNumber ); } public IconTheme getIconTheme() { return iconTheme; } protected void setIconTheme( final IconTheme theme ) { final IconTheme oldTheme = this.iconTheme; this.iconTheme = theme; firePropertyChange( PreviewPane.ICON_THEME_PROPERTY, oldTheme, theme ); } protected void initializeFromReport() { final PageDefinition pageDefinition = reportJob.getPageDefinition(); if ( pageDefinition.getPageCount() > 0 ) { final PageFormat pageFormat = pageDefinition.getPageFormat( 0 ); pageDrawable.setDefaultWidth( (int) pageFormat.getWidth() ); pageDrawable.setDefaultHeight( (int) pageFormat.getHeight() ); } if ( reportJob.getTitle() == null ) { setTitle( messages.getString( "PreviewPane.EMPTY_TITLE" ) ); //$NON-NLS-1$ } else { setTitle( messages.getString( "PreviewPane.PREVIEW_TITLE", reportJob.getTitle() ) ); //$NON-NLS-1$ } final Configuration configuration = reportJob.getConfiguration(); setIconTheme( PreviewPaneUtilities.createIconTheme( configuration ) ); performInitialization( configuration ); if ( deferredRepagination == false ) { startPagination(); } } protected void initializeWithoutJob() { if ( printReportProcessor != null ) { printReportProcessor.close(); printReportProcessor = null; } setTitle( messages.getString( "PreviewPane.EMPTY_TITLE" ) ); //$NON-NLS-1$ final Configuration configuration = ClassicEngineBoot.getInstance().getGlobalConfig(); setIconTheme( PreviewPaneUtilities.createIconTheme( configuration ) ); performInitialization( configuration ); } private synchronized void performInitialization( final Configuration configuration ) { if ( performInitializationRunning ) { throw new IllegalStateException( "This method is not re-entrant" ); } try { performInitializationRunning = true; applyDefinedDimension( configuration ); final Double key = zoomModel.getSelectedKey(); zoomModel.clear(); for ( int i = 0; i < PreviewPane.ZOOM_FACTORS.length; i++ ) { zoomModel.add( new Double( PreviewPane.ZOOM_FACTORS[i] ), formatZoomText( PreviewPane.ZOOM_FACTORS[i] ) ); } if ( key == null ) { updateZoomModel( zoom ); } else { zoomModel.setSelectedKey( key ); } if ( this.actionPlugins != null ) { for ( final ActionPlugin[] plugins : this.actionPlugins.values() ) { for ( int i = 0; i < plugins.length; i++ ) { final ActionPlugin plugin = plugins[i]; plugin.deinitialize( swingGuiContext ); plugins[i] = null; } } this.actionPlugins = null; } this.actionPlugins = PreviewPaneUtilities.loadActions( swingGuiContext ); if ( "true".equals( configuration.getConfigProperty( PreviewPane.MENUBAR_AVAILABLE_KEY ) ) ) { //$NON-NLS-1$ buildMenu(); } else { setMenu( PreviewPane.EMPTY_MENU ); } if ( toolBar != null ) { toolbarHolder.remove( toolBar ); } if ( "true".equals( configuration.getConfigProperty( PreviewPane.TOOLBAR_AVAILABLE_KEY ) ) ) { //$NON-NLS-1$ final boolean floatable = isToolbarFloatable() || "true".equals( configuration.getConfigProperty( PreviewPane.TOOLBAR_FLOATABLE_KEY ) ); //$NON-NLS-1$ toolBar = buildToolbar( floatable ); } else { toolBar = null; } if ( toolBar != null ) { toolbarHolder.add( toolBar, BorderLayout.NORTH ); } } finally { performInitializationRunning = false; } } /** * Read the defined dimensions from the report's configuration and set them to the Dialog. If a maximum size is * defined, add a WindowSizeLimiter to check the maximum size * * @param configuration * the report-configuration of this dialog. */ private void applyDefinedDimension( final Configuration configuration ) { String width = configuration.getConfigProperty( PreviewPane.PREVIEW_PREFERRED_WIDTH ); String height = configuration.getConfigProperty( PreviewPane.PREVIEW_PREFERRED_HEIGHT ); // only apply if both values are set. if ( width != null && height != null ) { try { final Dimension pref = createCorrectedDimensions( Integer.parseInt( width ), Integer.parseInt( height ) ); setPreferredSize( pref ); } catch ( Exception nfe ) { PreviewPane.logger.warn( "Preferred viewport size is defined, but the specified values are invalid." ); //$NON-NLS-1$ } } width = configuration.getConfigProperty( PreviewPane.PREVIEW_MAXIMUM_WIDTH ); height = configuration.getConfigProperty( PreviewPane.PREVIEW_MAXIMUM_HEIGHT ); removeComponentListener( sizeLimiter ); // only apply if at least one value is set. if ( width != null || height != null ) { try { final int iWidth = ( width == null ) ? Short.MAX_VALUE : (int) parseRelativeFloat( width ); final int iHeight = ( height == null ) ? Short.MAX_VALUE : (int) parseRelativeFloat( height ); final Dimension pref = createCorrectedDimensions( iWidth, iHeight ); setMaximumSize( pref ); addComponentListener( sizeLimiter ); } catch ( Exception nfe ) { PreviewPane.logger.warn( "Maximum viewport size is defined, but the specified values are invalid." ); //$NON-NLS-1$ } } } protected float parseRelativeFloat( final String value ) { if ( value == null ) { throw new NumberFormatException(); } final String tvalue = value.trim(); if ( tvalue.length() > 0 && tvalue.charAt( tvalue.length() - 1 ) == '%' ) { //$NON-NLS-1$ final String number = tvalue.substring( 0, tvalue.length() - 1 ); //$NON-NLS-1$ return Float.parseFloat( number ) * -1.0f; } else { return Float.parseFloat( tvalue ); } } /** * Correct the given width and height. If the values are negative, the height and width is considered a proportional * value where -100 corresponds to 100%. The proportional attributes are given is relation to the screen width and * height. * * @param w * the to be corrected width * @param h * the height that should be corrected * @return the dimension of width and height, where all relative values are normalized. */ private Dimension createCorrectedDimensions( int w, int h ) { final Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize(); if ( w < 0 ) { w = ( w * screenSize.width / -100 ); } if ( h < 0 ) { h = ( h * screenSize.height / -100 ); } return new Dimension( w, h ); } /** * Gets a list of Actions to add to the toolbar ahead of all other toolbar actions. Left protected for subclasses to * override. * * @return Action[] an array of javax.swing.Action objects */ protected Action[] getToolbarPreActions() { return new Action[] {}; } protected JToolBar buildToolbar( final boolean floatable ) { toolBar = new JToolBar(); toolBar.setFloatable( floatable ); final Action[] preActions = getToolbarPreActions(); if ( preActions != null && preActions.length > 0 ) { for ( int i = 0; i < preActions.length; i++ ) { toolBar.add( preActions[i] ); } toolBar.addSeparator(); } final ArrayList<ActionPlugin> list = new ArrayList<ActionPlugin>(); for ( final ActionPlugin[] plugins : actionPlugins.values() ) { list.addAll( Arrays.asList( plugins ) ); } final ActionPlugin[] plugins = list.toArray( new ActionPlugin[list.size()] ); Arrays.sort( plugins, new ActionPluginComparator() ); PreviewPaneUtilities.addActionsToToolBar( toolBar, plugins, zoomSelectorBox, this ); return toolBar; } public void setToolbarFloatable( final boolean toolbarFloatable ) { this.toolbarFloatable = toolbarFloatable; } public boolean isToolbarFloatable() { return toolbarFloatable; } private void closeToolbar() { if ( toolBar == null ) { return; } if ( toolBar.getParent() != toolbarHolder ) { // ha!, we detected that the toolbar is floating ... // Log.debug (currentToolbar.getParent()); final Window w = SwingUtilities.windowForComponent( toolBar ); if ( w != null ) { w.setVisible( false ); w.dispose(); } } toolBar.setVisible( false ); } public SwingGuiContext getSwingGuiContext() { return swingGuiContext; } public KeyedComboBoxModel<Double, String> getZoomModel() { return zoomModel; } protected final String formatZoomText( final double zoom ) { final NumberFormat numberFormat = NumberFormat.getPercentInstance( swingGuiContext.getLocale() ); return ( numberFormat.format( zoom ) ); } private void buildMenu() { for ( final ZoomAction[] zoomActions : this.zoomActions.values() ) { for ( int i = 0; i < zoomActions.length; i++ ) { final ZoomAction zoomAction = zoomActions[i]; zoomAction.deinitialize(); } } this.zoomActions.clear(); final HashMap<ActionCategory, JMenu> menus = new HashMap<ActionCategory, JMenu>(); final int userPos = getUserDefinedCategoryPosition(); boolean insertedUserDefinedActions = false; final ArrayList<ActionCategory> collectedCategories = new ArrayList<ActionCategory>(); for ( final Map.Entry<ActionCategory, ActionPlugin[]> entry : actionPlugins.entrySet() ) { final ActionCategory cat = entry.getKey(); collectedCategories.add( cat ); final ActionPlugin[] plugins = entry.getValue(); if ( plugins.length > 0 ) { if ( plugins[0] == null ) { throw new NullPointerException(); } } if ( insertedUserDefinedActions == false && cat.getPosition() > userPos ) { final ReportController controller = getReportController(); if ( controller != null ) { controller.initialize( this ); final JMenu[] controlerMenus = controller.getMenus(); for ( int i = 0; i < controlerMenus.length; i++ ) { final ActionCategory userCategory = new ActionCategory(); userCategory.setName( "X-User-Category-" + i ); //$NON-NLS-1$ userCategory.setPosition( userPos + i ); userCategory.setUserDefined( true ); menus.put( userCategory, controlerMenus[i] ); collectedCategories.add( userCategory ); } } insertedUserDefinedActions = true; } final JMenu menu = PreviewPaneUtilities.createMenu( cat ); zoomActions.put( cat, PreviewPaneUtilities.buildMenu( menu, plugins, this ) ); menus.put( cat, menu ); } final ActionCategory[] categories = collectedCategories.toArray( new ActionCategory[collectedCategories.size()] ); final CategoryTreeItem[] categoryTreeItems = PreviewPaneUtilities.buildMenuTree( categories ); final ArrayList<CategoryTreeItem> menuList = new ArrayList<CategoryTreeItem>(); for ( int i = 0; i < categoryTreeItems.length; i++ ) { final CategoryTreeItem item = categoryTreeItems[i]; final JMenu menu = menus.get( item.getCategory() ); // now connect all menus .. final CategoryTreeItem[] childs = item.getChilds(); Arrays.sort( childs ); for ( int j = 0; j < childs.length; j++ ) { final CategoryTreeItem child = childs[j]; final JMenu childMenu = menus.get( child.getCategory() ); if ( childMenu != null ) { menu.add( childMenu ); } } if ( item.getParent() == null ) { menuList.add( item ); } } Collections.sort( menuList ); final ArrayList<JMenu> retval = new ArrayList<JMenu>(); for ( int i = 0; i < menuList.size(); i++ ) { final CategoryTreeItem item = menuList.get( i ); final JMenu menu = menus.get( item.getCategory() ); if ( item.getCategory().isUserDefined() || menu.getItemCount() > 0 ) { retval.add( menu ); } } setMenu( retval.toArray( new JMenu[retval.size()] ) ); } public String getTitle() { return title; } public void setTitle( final String title ) { final String oldTitle = this.title; this.title = title; firePropertyChange( PreviewPane.TITLE_PROPERTY, oldTitle, title ); } public double[] getZoomFactors() { return (double[]) PreviewPane.ZOOM_FACTORS.clone(); } public boolean isPaginated() { return paginated; } public void setPaginated( final boolean paginated ) { final boolean oldPaginated = this.paginated; this.paginated = paginated; firePropertyChange( PreviewPane.PAGINATED_PROPERTY, oldPaginated, paginated ); } public boolean isPaginating() { return paginating; } public void setPaginating( final boolean paginating ) { final boolean oldPaginating = this.paginating; this.paginating = paginating; firePropertyChange( PreviewPane.PAGINATING_PROPERTY, oldPaginating, paginating ); } public synchronized void startPagination() { killThePaginationWorker(); if ( printReportProcessor != null ) { printReportProcessor.close(); printReportProcessor = null; } // Reset the pagination to automatic mode now. This way changes to the page-format will trigger // pagination again in a safe manor. deferredRepagination = false; try { final MasterReport reportJob = getReportJob(); printReportProcessor = new PrintReportProcessor( reportJob ); paginationWorker = createWorker(); paginationWorker.setWorkload( new RepaginationRunnable( printReportProcessor ) ); } catch ( ReportProcessingException e ) { PreviewPane.logger.error( "Unable to start report pagination:", e ); // NON-NLS setStatusType( StatusType.ERROR ); setStatusText( messages.getString( "PreviewPane.ERROR_ON_PAGINATION" ) ); } } private void killThePaginationWorker() { if ( printReportProcessor != null ) { printReportProcessor.cancel(); } if ( paginationWorker != null ) { // make sure that old pagination handler does not run longer than // necessary.. // noinspection SynchronizeOnNonFinalField final Worker paginationWorker = this.paginationWorker; while ( paginationWorker.isAvailable() == false && paginationWorker.isFinish() == false ) { try { synchronized ( paginationWorker ) { paginationWorker.wait( 500 ); } } catch ( InterruptedException e ) { // Got interrupted while waiting ... } } this.paginationWorker = null; } } protected Worker createWorker() { return new Worker(); } public Object getNoReportDrawable() { return noReportDrawable; } public void setNoReportDrawable( final Object noReportDrawable ) { this.noReportDrawable = noReportDrawable; } public Object getPaginatingDrawable() { return paginatingDrawable; } public void setPaginatingDrawable( final Object paginatingDrawable ) { this.paginatingDrawable = paginatingDrawable; } protected void updateVisiblePage( final int pageNumber ) { // if ( printReportProcessor == null ) { throw new IllegalStateException(); } // todo: This can be very expensive - so we better move this off the event-dispatcher final int pageIndex = getPageNumber() - 1; if ( pageIndex < 0 || pageIndex >= printReportProcessor.getNumberOfPages() ) { this.pageDrawable.setBackend( null ); this.drawablePanel.setDrawableAsRawObject( pageDrawable ); } else { final PageDrawable drawable = printReportProcessor.getPageDrawable( pageIndex ); this.pageDrawable.setBackend( drawable ); this.drawablePanel.setDrawableAsRawObject( pageDrawable ); } } protected StatusListener getStatusListener() { return statusListener; } public void addReportProgressListener( final ReportProgressListener progressListener ) { if ( progressListener == null ) { throw new NullPointerException(); } if ( reportProgressListener == null ) { reportProgressListener = new ArrayList(); } reportProgressListener.add( progressListener ); } public void removeReportProgressListener( final ReportProgressListener progressListener ) { if ( reportProgressListener == null ) { return; } reportProgressListener.remove( progressListener ); } protected void forwardReportStartedEvent( final ReportProgressEvent event ) { if ( reportProgressListener == null ) { return; } for ( int i = 0; i < reportProgressListener.size(); i++ ) { final ReportProgressListener listener = (ReportProgressListener) reportProgressListener.get( i ); listener.reportProcessingStarted( event ); } } protected void forwardReportUpdateEvent( final ReportProgressEvent event ) { if ( reportProgressListener == null ) { return; } for ( int i = 0; i < reportProgressListener.size(); i++ ) { final ReportProgressListener listener = (ReportProgressListener) reportProgressListener.get( i ); listener.reportProcessingUpdate( event ); } } protected void forwardReportFinishedEvent( final ReportProgressEvent event ) { if ( reportProgressListener == null ) { return; } for ( int i = 0; i < reportProgressListener.size(); i++ ) { final ReportProgressListener listener = (ReportProgressListener) reportProgressListener.get( i ); listener.reportProcessingFinished( event ); } } private double getMaximumZoom( final Configuration configuration ) { final String value = configuration.getConfigProperty( PreviewPane.ZOOM_MAXIMUM_KEY ); return ParserUtil.parseFloat( value, PreviewPane.ZOOM_MAXIMUM_DEFAULT ); } private double getMinimumZoom( final Configuration configuration ) { final String value = configuration.getConfigProperty( PreviewPane.ZOOM_MINIMUM_KEY ); return ParserUtil.parseFloat( value, PreviewPane.ZOOM_MINIMUM_DEFAULT ); } public void addReportHyperlinkListener( final ReportHyperlinkListener listener ) { if ( listener == null ) { throw new NullPointerException(); } if ( hyperlinkListeners == null ) { hyperlinkListeners = new ArrayList(); } hyperlinkListeners.add( listener ); cachedHyperlinkListeners = null; } public void removeReportHyperlinkListener( final ReportHyperlinkListener listener ) { if ( listener == null ) { throw new NullPointerException(); } if ( hyperlinkListeners == null ) { return; } hyperlinkListeners.remove( listener ); cachedHyperlinkListeners = null; } protected boolean isHyperlinkSystemActive() { if ( hyperlinkListeners == null ) { return false; } return hyperlinkListeners.isEmpty() == false; } protected void fireReportHyperlinkEvent( final ReportHyperlinkEvent event ) { if ( hyperlinkListeners == null ) { return; } if ( cachedHyperlinkListeners == null ) { cachedHyperlinkListeners = (ReportHyperlinkListener[]) hyperlinkListeners .toArray( new ReportHyperlinkListener[hyperlinkListeners.size()] ); } final ReportHyperlinkListener[] myListeners = cachedHyperlinkListeners; for ( int i = 0; i < myListeners.length; i++ ) { final ReportHyperlinkListener listener = myListeners[i]; listener.hyperlinkActivated( event ); } } protected Configuration computeContextConfiguration() { final MasterReport report = getReportJob(); if ( report != null ) { return report.getConfiguration(); } return ClassicEngineBoot.getInstance().getGlobalConfig(); } }