/*
* 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 - 2009 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.Iterator;
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.JScrollBar;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JToolBar;
import javax.swing.JViewport;
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.commonswing.ActionPlugin;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.CenterLayout;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.KeyedComboBoxModel;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.ReportEventSource;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.StatusType;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingGuiContext;
import org.pentaho.reporting.engine.classic.core.modules.gui.commonswing.SwingUtil;
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.StringUtil;
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.xmlns.LibXmlInfo;
/**
* 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 SwingUtil.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 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 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.
* @see JScrollBar#setUnitIncrement
*/
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.
* @see JScrollBar#setBlockIncrement
*/
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%
/**
* @deprecated use the paginating property instead
*/
public static final String LOCK_INTERFACE_PROPERTY = "lockInterface"; //$NON-NLS-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 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 actionPlugins;
private Map 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();
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 // if (BorderLayout.NORTH.equals(reportControllerLocation))
{
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 // if (BorderLayout.NORTH.equals(reportControllerLocation))
{
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)
{
if (paginationWorker != null)
{
//noinspection SynchronizeOnNonFinalField
synchronized (paginationWorker)
{
paginationWorker.finish();
}
paginationWorker = null;
}
if (printReportProcessor != null)
{
printReportProcessor.close();
printReportProcessor = null;
}
closeToolbar();
}
}
private int getUserDefinedCategoryPosition()
{
return StringUtil.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 void performInitialization(final Configuration configuration)
{
if (performInitializationRunning)
{
throw new IllegalStateException("This method is not re-entrant");
}
try
{
performInitializationRunning = true;
applyDefinedDimension(configuration);
final Object 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)
{
final ActionPlugin[][] plugins = (ActionPlugin[][])
this.actionPlugins.values().toArray(new ActionPlugin[this.actionPlugins.size()][]);
for (int i = 0; i < plugins.length; i++)
{
final ActionPlugin[] plugin = plugins[i];
for (int j = 0; j < plugin.length; j++)
{
final ActionPlugin actionPlugin = plugin[j];
actionPlugin.deinitialize(swingGuiContext);
}
}
}
this.actionPlugins = PreviewPaneUtilities.loadActions(swingGuiContext);
if ("true".equals(configuration.getConfigProperty(PreviewPane.MENUBAR_AVAILABLE_KEY))) //$NON-NLS-1$
{
buildMenu(actionPlugins);
}
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(actionPlugins, 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 Map actions, final boolean floatable)
{
toolBar = new JToolBar();
toolBar.setFloatable(floatable);
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 list = new ArrayList();
final Iterator iterator = actions.values().iterator();
while (iterator.hasNext())
{
final ActionPlugin[] plugins = (ActionPlugin[]) iterator.next();
list.addAll(Arrays.asList(plugins));
}
final ActionPlugin[] plugins = (ActionPlugin[]) 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 getZoomModel()
{
return zoomModel;
}
protected final String formatZoomText(final double zoom)
{
final NumberFormat numberFormat =
NumberFormat.getPercentInstance(swingGuiContext.getLocale());
return (numberFormat.format(zoom));
}
private void buildMenu(final Map actions)
{
final Iterator oldActions = this.zoomActions.values().iterator();
while (oldActions.hasNext())
{
final ZoomAction[] zoomActions = (ZoomAction[]) oldActions.next();
for (int i = 0; i < zoomActions.length; i++)
{
final ZoomAction zoomAction = zoomActions[i];
zoomAction.deinitialize();
}
}
this.zoomActions.clear();
final HashMap menus = new HashMap();
final int userPos = getUserDefinedCategoryPosition();
boolean insertedUserDefinedActions = false;
final Iterator iterator = actions.entrySet().iterator();
final ArrayList collectedCategories = new ArrayList();
while (iterator.hasNext())
{
final Map.Entry entry = (Map.Entry) iterator.next();
final ActionCategory cat = (ActionCategory) entry.getKey();
collectedCategories.add(cat);
final ActionPlugin[] plugins = (ActionPlugin[]) entry.getValue();
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 = (ActionCategory[])
collectedCategories.toArray(new ActionCategory[collectedCategories.size()]);
final CategoryTreeItem[] categoryTreeItems =
PreviewPaneUtilities.buildMenuTree(categories);
final ArrayList menuList = new ArrayList();
for (int i = 0; i < categoryTreeItems.length; i++)
{
final CategoryTreeItem item = categoryTreeItems[i];
final JMenu menu = (JMenu) 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 = (JMenu) menus.get(child.getCategory());
if (childMenu != null)
{
menu.add(childMenu);
}
}
if (item.getParent() == null)
{
menuList.add(item);
}
}
Collections.sort(menuList);
final ArrayList retval = new ArrayList();
for (int i = 0; i < menuList.size(); i++)
{
final CategoryTreeItem item = (CategoryTreeItem) menuList.get(i);
final JMenu menu = (JMenu) menus.get(item.getCategory());
if (item.getCategory().isUserDefined() || menu.getItemCount() > 0)
{
retval.add(menu);
}
}
setMenu((JMenu[]) 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);
firePropertyChange(PreviewPane.LOCK_INTERFACE_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 (paginationWorker != null)
{
// make sure that old pagination handler does not run longer than
// necessary..
//noinspection SynchronizeOnNonFinalField
final Worker paginationWorker = this.paginationWorker;
paginationWorker.finish();
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 StringUtil.parseFloat(value, PreviewPane.ZOOM_MAXIMUM_DEFAULT);
}
private double getMinimumZoom(final Configuration configuration)
{
final String value = configuration.getConfigProperty(PreviewPane.ZOOM_MINIMUM_KEY);
return StringUtil.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();
}
}