/*
* 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();
}
}