/*!
* 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) 2002-2013 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.designer.core;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.pentaho.reporting.designer.core.actions.elements.InsertElementAction;
import org.pentaho.reporting.designer.core.actions.elements.MorphAction;
import org.pentaho.reporting.designer.core.actions.elements.barcode.BarcodeTypeAction;
import org.pentaho.reporting.designer.core.actions.global.AboutAction;
import org.pentaho.reporting.designer.core.actions.global.OpenReportAction;
import org.pentaho.reporting.designer.core.actions.global.OpenSampleReportAction;
import org.pentaho.reporting.designer.core.actions.global.QuitAction;
import org.pentaho.reporting.designer.core.actions.global.SelectTabAction;
import org.pentaho.reporting.designer.core.actions.global.SettingsAction;
import org.pentaho.reporting.designer.core.actions.report.CloseAllReportsAction;
import org.pentaho.reporting.designer.core.actions.report.CloseChildReportsAction;
import org.pentaho.reporting.designer.core.actions.report.CloseOtherReportsAction;
import org.pentaho.reporting.designer.core.actions.report.CloseReportAction;
import org.pentaho.reporting.designer.core.actions.report.CloseUnmodifiedReportsAction;
import org.pentaho.reporting.designer.core.editor.ContextMenuUtility;
import org.pentaho.reporting.designer.core.editor.ElementPropertiesPanel;
import org.pentaho.reporting.designer.core.editor.ReportDocumentContext;
import org.pentaho.reporting.designer.core.editor.ReportRenderContext;
import org.pentaho.reporting.designer.core.editor.ReportRendererComponent;
import org.pentaho.reporting.designer.core.editor.fieldselector.FieldSelectorPaletteDialog;
import org.pentaho.reporting.designer.core.editor.palette.PaletteButton;
import org.pentaho.reporting.designer.core.frame.RecentFilesUpdateHandler;
import org.pentaho.reporting.designer.core.inspections.InspectionSidePanePanel;
import org.pentaho.reporting.designer.core.settings.SettingsListener;
import org.pentaho.reporting.designer.core.settings.WorkspaceSettings;
import org.pentaho.reporting.designer.core.status.StatusBar;
import org.pentaho.reporting.designer.core.util.IconLoader;
import org.pentaho.reporting.designer.core.util.docking.Category;
import org.pentaho.reporting.designer.core.util.docking.GlobalPane;
import org.pentaho.reporting.designer.core.util.docking.InternalWindow;
import org.pentaho.reporting.designer.core.util.exceptions.UncaughtExceptionsModel;
import org.pentaho.reporting.designer.core.welcome.SamplesTreeBuilder;
import org.pentaho.reporting.designer.core.welcome.WelcomePane;
import org.pentaho.reporting.designer.core.widgets.FancyTabbedPane;
import org.pentaho.reporting.designer.core.widgets.TabRenderer;
import org.pentaho.reporting.designer.core.xul.ActionSwingMenuitem;
import org.pentaho.reporting.designer.core.xul.XulDesignerFrame;
import org.pentaho.reporting.engine.classic.core.metadata.ElementMetaData;
import org.pentaho.reporting.engine.classic.core.metadata.ElementTypeRegistry;
import org.pentaho.reporting.engine.classic.core.metadata.GroupedMetaDataComparator;
import org.pentaho.reporting.engine.classic.core.metadata.MaturityLevel;
import org.pentaho.reporting.engine.classic.extensions.modules.sbarcodes.BarcodeTypePropertyEditor;
import org.pentaho.reporting.libraries.base.util.DebugLog;
import org.pentaho.reporting.libraries.base.util.ObjectUtilities;
import org.pentaho.reporting.libraries.base.util.StringUtils;
import org.pentaho.reporting.libraries.designtime.swing.MacOSXIntegration;
import org.pentaho.ui.xul.XulComponent;
import org.pentaho.ui.xul.XulException;
import org.pentaho.ui.xul.components.XulMenuitem;
import org.pentaho.ui.xul.containers.XulMenupopup;
import org.pentaho.ui.xul.swing.tags.SwingMenuseparator;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.plaf.TabbedPaneUI;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeModel;
import java.awt.*;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragSource;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.event.AWTEventListener;
import java.awt.event.ActionEvent;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
public class ReportDesignerFrame extends JFrame {
/**
* Class for handling cursor changes during drags See PRD-1674
*/
public class DefaultDropTargetListener extends DropTargetAdapter {
public void dragEnter( final DropTargetDragEvent dtde ) {
final DropTarget dropTarget = (DropTarget) dtde.getSource();
dropTarget.getComponent().setCursor( DragSource.DefaultCopyNoDrop );
}
public void dragExit( final DropTargetEvent dte ) {
final DropTarget dropTarget = (DropTarget) dte.getSource();
dropTarget.getComponent().setCursor( Cursor.getDefaultCursor() );
}
public void drop( final DropTargetDropEvent dtde ) {
final DropTarget dropTarget = (DropTarget) dtde.getSource();
dropTarget.getComponent().setCursor( Cursor.getDefaultCursor() );
dtde.rejectDrop();
}
}
private static class ScrollbarSyncHandler extends ComponentAdapter {
private final JScrollBar theVerticalScrollBar;
private final JToolBar toolBar;
public ScrollbarSyncHandler( final JScrollBar theVerticalScrollBar, final JToolBar toolBar ) {
this.theVerticalScrollBar = theVerticalScrollBar;
this.toolBar = toolBar;
}
public void componentResized( final ComponentEvent e ) {
if ( theVerticalScrollBar.isShowing() ) {
toolBar.setBorder( BorderFactory.createEmptyBorder( 25, 0, 0, 19 ) );
} else {
toolBar.setBorder( BorderFactory.createEmptyBorder( 25, 0, 0, 4 ) );
}
}
}
private class WindowCloseHandler extends WindowAdapter {
private WindowCloseHandler() {
}
/**
* Invoked when a window is in the process of being closed. The close operation can be overridden at this point.
*/
public void windowClosing( final WindowEvent e ) {
final QuitAction quitAction = new QuitAction();
quitAction.setReportDesignerContext( context );
quitAction.actionPerformed( new ActionEvent( this, 0, "quit", 0 ) ); // NON-NLS
}
}
private class ReportTabActivationHandler implements ChangeListener, PropertyChangeListener {
private ReportTabActivationHandler() {
}
public void propertyChange( final PropertyChangeEvent evt ) {
final ReportDesignerDocumentContext activeContext = getContext().getActiveDocument();
final JTabbedPane editorPanes = getReportEditorPane();
final int i = findTabForContext( activeContext );
if ( i != -1 && editorPanes.getSelectedIndex() != i ) {
editorPanes.setSelectedIndex( i );
}
updateFrameTitle();
}
private int findTabForContext( final ReportDesignerDocumentContext activeContext ) {
final JTabbedPane editorPanes = getReportEditorPane();
final int count = editorPanes.getTabCount();
for ( int i = 0; i < count; i++ ) {
final Component c = editorPanes.getComponentAt( i );
if ( c instanceof ReportRendererComponent ) {
final ReportRendererComponent rrc = (ReportRendererComponent) c;
if ( rrc.getRenderContext() == activeContext ) {
return i;
}
}
}
return -1;
}
/**
* Invoked when the target of the listener has changed its state.
*
* @param e a ChangeEvent object
*/
public void stateChanged( final ChangeEvent e ) {
final JTabbedPane editorPanes = getReportEditorPane();
final DefaultReportDesignerContext context = getContext();
if ( editorPanes.getTabCount() == 0 ) {
context.setActiveDocument( null );
return;
}
final int index = editorPanes.getSelectedIndex();
if ( index == -1 || editorPanes.getComponentAt( index ) instanceof ReportRendererComponent == false ) {
context.setActiveDocument( null );
} else {
// try to sync the context.
final ReportRendererComponent rendererComponent = (ReportRendererComponent) editorPanes.getComponentAt( index );
final ReportDocumentContext rendererContext = rendererComponent.getRenderContext();
for ( int i = 0; i < context.getReportRenderContextCount(); i++ ) {
final ReportRenderContext context1 = context.getReportRenderContext( i );
if ( context1 == rendererContext ) {
context.setActiveDocument( rendererContext );
rendererContext.onDocumentActivated();
return;
}
}
if ( context.getReportRenderContextCount() > 0 ) {
// we couldn't find a context, but we do at least have a context
// instead of blowing away the context, let's grab one and
// attempt to use it
// this works around PRD-1609
context.setActiveDocument( context.getReportRenderContext( context.getReportRenderContextCount() - 1 ) );
} else {
// not found, so we are probably in a remove at the moment.
context.setActiveDocument( null );
}
}
}
}
private class StatusTextHandler implements PropertyChangeListener {
private StatusTextHandler() {
}
/**
* 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 ) {
getStatusBar().setGeneralInfoText( getContext().getStatusText() );
}
}
private class ReportEditorContextHandler implements PropertyChangeListener {
private ReportEditorContextHandler() {
}
/**
* 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 ( ReportDesignerContext.REPORT_RENDER_CONTEXT_PROPERTY.equals( evt.getPropertyName() ) == false ) {
return;
}
if ( !( evt instanceof IndexedPropertyChangeEvent ) ) {
throw new UnsupportedOperationException();
}
final JTabbedPane editorPanes = getReportEditorPane();
final IndexedPropertyChangeEvent ievt = (IndexedPropertyChangeEvent) evt;
if ( ievt.getNewValue() != null ) {
// added
final ReportRenderContext renderContext = (ReportRenderContext) ievt.getNewValue();
DefaultReportDesignerContext reportDesignerContext = getContext();
final ReportRendererComponent rendererComponent =
new ReportRendererComponent( reportDesignerContext, renderContext );
final String title = renderContext.getTabName();
final Icon icon = renderContext.getIcon();
final TabRenderer tabRenderer = new TabRenderer( icon, title, reportDesignerContext, editorPanes );
editorPanes.addTab( title, null, rendererComponent );
editorPanes.setTabComponentAt( editorPanes.getTabCount() - 1, tabRenderer );
editorPanes.setSelectedComponent( rendererComponent );
renderContext.addPropertyChangeListener( "tabName",
new DocumentNameChangeHandler( renderContext, tabRenderer, editorPanes.getTabCount() - 1 ) );
} else if ( ievt.getOldValue() != null ) {
if ( editorPanes.getTabCount() <= 0 ) {
return;
}
// removed
int index = ievt.getIndex();
final ReportRenderContext reportRenderContext = (ReportRenderContext) ievt.getOldValue();
boolean removedTab = false;
for ( int i = 0; i < editorPanes.getTabCount(); i++ ) {
final Component tabContent = editorPanes.getComponentAt( i );
if ( tabContent instanceof ReportRendererComponent ) {
final ReportRendererComponent myReportRenderer = (ReportRendererComponent) tabContent;
if ( myReportRenderer.getRenderContext() == reportRenderContext ) {
index = i;
editorPanes.removeTabAt( index );
removedTab = true;
myReportRenderer.dispose();
break;
}
}
}
// if we didn't remove a tab, the tab was probably not a report, so remove it by index
if ( !removedTab ) {
editorPanes.removeTabAt( index );
}
// unregister the listeners ...
if ( editorPanes.getTabCount() > 0 ) {
if ( index < editorPanes.getTabCount() ) {
editorPanes.setSelectedIndex( index );
} else {
editorPanes.setSelectedIndex( editorPanes.getTabCount() - 1 );
}
}
}
recomputeAllTabTitles();
rebuildReportMenu();
}
}
private class ReportTabPanePopupHandler extends MouseAdapter {
private ReportTabPanePopupHandler() {
}
private void handlePopup( final MouseEvent e ) {
final JTabbedPane reportEditorPane = getReportEditorPane();
final TabbedPaneUI ui = reportEditorPane.getUI();
final int tabIndex = ui.tabForCoordinate( reportEditorPane, e.getX(), e.getY() );
final JPopupMenu popupMenu = new JPopupMenu();
final CloseReportAction closeThisAction = new CloseReportAction( tabIndex );
closeThisAction.setReportDesignerContext( getContext() );
final CloseChildReportsAction closeChildsAction = new CloseChildReportsAction( tabIndex );
closeChildsAction.setReportDesignerContext( getContext() );
final CloseOtherReportsAction closeOthersAction = new CloseOtherReportsAction( tabIndex );
closeOthersAction.setReportDesignerContext( getContext() );
final CloseAllReportsAction closeAllAction = new CloseAllReportsAction();
closeAllAction.setReportDesignerContext( getContext() );
final CloseUnmodifiedReportsAction closeUnmodifiedReportsAction = new CloseUnmodifiedReportsAction();
closeUnmodifiedReportsAction.setReportDesignerContext( getContext() );
popupMenu.add( new JMenuItem( closeThisAction ) );
popupMenu.addSeparator();
popupMenu.add( new JMenuItem( closeChildsAction ) );
popupMenu.add( new JMenuItem( closeUnmodifiedReportsAction ) );
popupMenu.add( new JMenuItem( closeOthersAction ) );
popupMenu.add( new JMenuItem( closeAllAction ) );
popupMenu.show( reportEditorPane, e.getX(), e.getY() );
}
public void mouseClicked( final MouseEvent e ) {
if ( e.isPopupTrigger() ) {
handlePopup( e );
}
}
public void mousePressed( final MouseEvent e ) {
if ( e.isPopupTrigger() ) {
handlePopup( e );
}
}
public void mouseReleased( final MouseEvent e ) {
if ( e.isPopupTrigger() ) {
handlePopup( e );
}
}
}
private class FrameViewController implements ReportDesignerView {
private PropertyChangeSupport propertyChangeSupport;
private XulDesignerFrame xulDesignerFrame;
private Component parent;
private FrameViewController( final Component parent ) {
this.propertyChangeSupport = new PropertyChangeSupport( this );
this.parent = parent;
}
private void initializeXulDesignerFrame( final ReportDesignerContext context ) throws XulException {
this.xulDesignerFrame = new XulDesignerFrame();
this.xulDesignerFrame.setReportDesignerContext( context );
}
public void addPropertyChangeListener( final PropertyChangeListener listener ) {
propertyChangeSupport.addPropertyChangeListener( listener );
}
public void removePropertyChangeListener( final PropertyChangeListener listener ) {
propertyChangeSupport.removePropertyChangeListener( listener );
}
public void addPropertyChangeListener( final String propertyName, final PropertyChangeListener listener ) {
propertyChangeSupport.addPropertyChangeListener( propertyName, listener );
}
public void removePropertyChangeListener( final String propertyName, final PropertyChangeListener listener ) {
propertyChangeSupport.removePropertyChangeListener( propertyName, listener );
}
public boolean isStructureVisible() {
final Category reportTreeToolWindow = getReportTreeToolWindow();
return reportTreeToolWindow != null && reportTreeToolWindow.isMinimized() == false;
}
public void setStructureVisible( final boolean visible ) {
final Category reportTreeToolWindow = getReportTreeToolWindow();
final boolean oldValue = reportTreeToolWindow.isMinimized() == false;
reportTreeToolWindow.setMinimized( visible == false );
propertyChangeSupport.firePropertyChange( STRUCTURE_VISIBLE_PROPERTY, oldValue, visible );
}
public boolean isPropertiesEditorVisible() {
final Category attributeToolWindow = getAttributeToolWindow();
if ( attributeToolWindow == null ) {
return false;
}
return attributeToolWindow.isMinimized() == false;
}
public void setPropertiesEditorVisible( final boolean visible ) {
final Category attributeToolWindow = getAttributeToolWindow();
final boolean oldValue = attributeToolWindow.isMinimized() == false;
attributeToolWindow.setMinimized( visible == false );
propertyChangeSupport.firePropertyChange( PROPERTIES_EDITOR_VISIBLE_PROPERTY, oldValue, visible );
}
public boolean isMessagesVisible() {
final Category inspectionsToolWindow = getInspectionsToolWindow();
return inspectionsToolWindow != null && !inspectionsToolWindow.isMinimized();
}
public void setMessagesVisible( final boolean visible ) {
final Category inspectionsToolWindow = getInspectionsToolWindow();
final boolean oldValue = inspectionsToolWindow.isMinimized() == false;
inspectionsToolWindow.setMinimized( visible == false );
propertyChangeSupport.firePropertyChange( MESSAGES_VISIBLE_PROPERTY, oldValue, visible );
}
public boolean isPreviewVisible() {
final JTabbedPane reportEditorPane = getReportEditorPane();
if ( reportEditorPane == null || reportEditorPane.getTabCount() == 0 ) {
return false;
}
if ( reportEditorPane.getSelectedComponent() instanceof ReportRendererComponent ) {
final ReportRendererComponent rendererComponent =
(ReportRendererComponent) reportEditorPane.getSelectedComponent();
return !rendererComponent.isDesignVisible();
}
return false;
}
public void setPreviewVisible( final boolean visible ) {
final boolean oldValue = isPreviewVisible();
final JTabbedPane reportEditorPane = getReportEditorPane();
if ( reportEditorPane != null && reportEditorPane.getTabCount() > 0 ) {
if ( reportEditorPane.getSelectedComponent() instanceof ReportRendererComponent ) {
final ReportRendererComponent rendererComponent =
(ReportRendererComponent) reportEditorPane.getSelectedComponent();
if ( visible ) {
rendererComponent.showPreview();
} else {
rendererComponent.showDesign();
}
}
}
propertyChangeSupport.firePropertyChange( PREVIEW_VISIBLE_PROPERTY, oldValue, visible );
}
public boolean isWelcomeVisible() {
if ( welcomePane == null ) {
return false;
}
return welcomePane.isVisible();
}
public void setWelcomeVisible( final boolean visible ) {
if ( welcomePane == null ) {
return;
}
welcomePane.setVisible( visible );
welcomePane.toFront();
}
public boolean isFieldSelectorVisible() {
if ( fieldSelectorPaletteDialog == null ) {
return false;
}
return fieldSelectorPaletteDialog.isVisible();
}
public void setFieldSelectorVisible( final boolean visible ) {
if ( fieldSelectorPaletteDialog == null ) {
return;
}
fieldSelectorPaletteDialog.setVisible( visible );
fieldSelectorPaletteDialog.toFront();
}
public void redrawAll() {
getReportEditorPane().invalidate();
getReportEditorPane().revalidate();
getReportEditorPane().repaint();
}
public void showDataTree() {
treePanel.refreshTabPanel( getAttributeEditorPanel() );
treePanel.showDataTab();
}
public JPopupMenu getPopupMenu( final String id ) {
return xulDesignerFrame.getComponent( id, JPopupMenu.class );
}
public JComponent getToolBar( final String id ) {
JComponent toolBar = xulDesignerFrame.getComponent( id, JComponent.class );
if ( toolBar instanceof JToolBar ) {
final JToolBar realToolBar = (JToolBar) toolBar;
realToolBar.setFloatable( false );
}
return toolBar;
}
public Component getParent() {
return parent;
}
public <T extends JComponent> T getComponent( final String id, final Class<T> type ) {
T xulComponentById = xulDesignerFrame.getComponent( id, type );
if ( type.isInstance( xulComponentById ) ) {
return (T) xulComponentById;
}
return null;
}
public <T extends XulComponent> T getXulComponent( final String id, final Class<T> type ) {
T xulComponentById = xulDesignerFrame.getXulComponent( id, type );
if ( type.isInstance( xulComponentById ) ) {
return (T) xulComponentById;
}
return null;
}
public ActionSwingMenuitem createMenuItem( final Action action ) {
return xulDesignerFrame.createMenu( action );
}
public XulMenupopup createPopupMenu( final String label, final XulComponent parent ) throws XulException {
return xulDesignerFrame.createPopupMenu( label, parent );
}
}
private class ShowWelcomeScreenTask implements Runnable {
public void run() {
viewController.setWelcomeVisible( WorkspaceSettings.getInstance().isShowLauncher() );
}
}
private class DragSelectionToggleHandler implements AWTEventListener {
private DragSelectionToggleHandler() {
}
/**
* Invoked when an event is dispatched in the AWT.
*/
public void eventDispatched( final AWTEvent event ) {
if ( event instanceof KeyEvent == false ) {
return;
}
final KeyEvent keyevent = (KeyEvent) event;
if ( KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow() ==
ReportDesignerFrame.this ) {
context.setSelectionWaiting( keyevent.isShiftDown() );
}
}
}
private class PageTextHandler implements PropertyChangeListener {
/**
* 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 ) {
statusBar.setPages( context.getPage(), context.getPageTotal() );
}
}
private class DocumentNameChangeHandler implements PropertyChangeListener {
private ReportDesignerDocumentContext documentContext;
private TabRenderer tabRenderer;
private int tabIndex;
private DocumentNameChangeHandler( final ReportDesignerDocumentContext documentContext,
final TabRenderer tabRenderer,
final int tabIndex ) {
this.documentContext = documentContext;
this.tabRenderer = tabRenderer;
this.tabIndex = tabIndex;
}
public void propertyChange( final PropertyChangeEvent evt ) {
String tabName = documentContext.getTabName();
tabRenderer.setRawTabName( tabName );
getReportEditorPane().setTitleAt( tabIndex, tabName );
recomputeAllTabTitles();
rebuildReportMenu();
}
}
private static final Log logger = LogFactory.getLog( ReportDesignerFrame.class );
private final VisibleElementsUpdateHandler visibleElementsUpdateHandler;
private JTabbedPane reportEditorPane;
private GlobalPane dockingPane;
private StatusBar statusBar;
private DefaultReportDesignerContext context;
private Category attributeToolWindow;
private Category reportTreeToolWindow;
private Category inspectionsToolWindow;
private FrameViewController viewController;
private ElementPropertiesPanel attributeEditorPanel;
private WelcomePane welcomePane;
private FieldSelectorPaletteDialog fieldSelectorPaletteDialog;
private TreeSidePanel treePanel;
private JComponent paletteToolBar;
public ReportDesignerFrame() throws XulException {
setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
final ImageIcon icon = IconLoader.getInstance().getProductIcon();
if ( icon != null ) {
setIconImage( icon.getImage() );
}
setTitle( Messages.getString( "ReportDesignerFrame.Title" ) );
addWindowListener( new WindowCloseHandler() );
viewController = new FrameViewController( this );
context = new DefaultReportDesignerContext( viewController );
viewController.initializeXulDesignerFrame( context );
welcomePane = new WelcomePane( ReportDesignerFrame.this, getContext() );
fieldSelectorPaletteDialog = new FieldSelectorPaletteDialog( ReportDesignerFrame.this, getContext() );
statusBar = new StatusBar( context );
reportEditorPane = new FancyTabbedPane();
reportEditorPane.setTabLayoutPolicy( JTabbedPane.SCROLL_TAB_LAYOUT );
reportEditorPane.getModel().addChangeListener( new ReportTabActivationHandler() );
reportEditorPane.addMouseListener( new ReportTabPanePopupHandler() );
dockingPane = new GlobalPane( false );
dockingPane.setMainComponent( reportEditorPane );
attributeEditorPanel = new ElementPropertiesPanel();
attributeEditorPanel.setAllowAttributeCard( true );
attributeEditorPanel.setReportDesignerContext( context );
treePanel = new TreeSidePanel( context, attributeEditorPanel );
initializeToolWindows();
paletteToolBar = createPaletteToolBar();
final JPanel contentPane = new JPanel();
contentPane.setLayout( new BorderLayout() );
contentPane.add( context.getToolBar( "main-toolbar" ), BorderLayout.NORTH ); // NON-NLS
contentPane.add( dockingPane, BorderLayout.CENTER );
contentPane.add( statusBar, BorderLayout.SOUTH );
contentPane.add( paletteToolBar, BorderLayout.WEST );
setContentPane( contentPane );
setJMenuBar( createMenuBar() );
setDropTarget( new DropTarget( this, DnDConstants.ACTION_COPY_OR_MOVE, new DefaultDropTargetListener() ) );
context
.addPropertyChangeListener( ReportDesignerContext.ACTIVE_CONTEXT_PROPERTY, new ReportTabActivationHandler() );
context.addPropertyChangeListener( ReportDesignerContext.STATUS_TEXT_PROPERTY, new StatusTextHandler() );
context.addPropertyChangeListener( ReportDesignerContext.PAGE_PROPERTY, new PageTextHandler() );
context.addPropertyChangeListener( ReportDesignerContext.REPORT_RENDER_CONTEXT_PROPERTY,
new ReportEditorContextHandler() );
Toolkit.getDefaultToolkit().addAWTEventListener( new DragSelectionToggleHandler(), AWTEvent.KEY_EVENT_MASK );
if ( MacOSXIntegration.MAC_OS_X ) {
try {
final AboutAction aboutAction = new AboutAction();
aboutAction.setReportDesignerContext( context );
final SettingsAction settingsAction = new SettingsAction();
settingsAction.setReportDesignerContext( context );
final QuitAction quitAction = new QuitAction();
quitAction.setReportDesignerContext( context );
final OpenReportAction openReportAction = new OpenReportAction();
openReportAction.setReportDesignerContext( context );
MacOSXIntegration.setOpenFileAction( openReportAction );
MacOSXIntegration.setAboutAction( aboutAction );
MacOSXIntegration.setPreferencesAction( settingsAction );
MacOSXIntegration.setQuitAction( quitAction );
} catch ( Throwable e ) {
DebugLog.log( "Failed to activate MacOS-X integration", e ); // NON-NLS
}
}
visibleElementsUpdateHandler = new VisibleElementsUpdateHandler();
WorkspaceSettings.getInstance().addSettingsListener( visibleElementsUpdateHandler );
}
public void initWindowLocations( final File[] filesToOpen ) {
fieldSelectorPaletteDialog.initWindowLocation();
viewController.setFieldSelectorVisible( WorkspaceSettings.getInstance().isFieldSelectorVisible() );
welcomePane.setLocationRelativeTo( ReportDesignerFrame.this );
if ( filesToOpen == null || filesToOpen.length == 0 ) {
if ( WorkspaceSettings.getInstance().isReopenLastReport() ) {
final File[] recentFiles = context.getRecentFilesModel().getRecentFiles();
if ( recentFiles.length > 0 ) {
SwingUtilities.invokeLater( new OpenReportAction.OpenReportTask( recentFiles[ 0 ], context ) );
return;
}
}
/*
* PRD-1676: Since the report designer frame will steal the focus from the Welcome dialog
* we must let the report designer frame finish it's initialization before we try to display
* the welcome panel.
*/
SwingUtilities.invokeLater( new ShowWelcomeScreenTask() );
} else {
for ( int i = 0; i < filesToOpen.length; i++ ) {
final File file = filesToOpen[ i ];
SwingUtilities.invokeLater( new OpenReportAction.OpenReportTask( file, context ) );
}
}
}
protected Category getAttributeToolWindow() {
return attributeToolWindow;
}
protected Category getReportTreeToolWindow() {
return reportTreeToolWindow;
}
protected Category getInspectionsToolWindow() {
return inspectionsToolWindow;
}
protected ElementPropertiesPanel getAttributeEditorPanel() {
return attributeEditorPanel;
}
protected JTabbedPane getReportEditorPane() {
return reportEditorPane;
}
protected DefaultReportDesignerContext getContext() {
return context;
}
protected StatusBar getStatusBar() {
return statusBar;
}
private JMenuBar createMenuBar() {
createRecentFilesMenu();
createZoomMenu();
createMorphMenu();
createInsertElementsMenu();
createInsertDataSourcesMenu();
createSamplesMenu();
return context.getView().getComponent( "main-menubar", JMenuBar.class );
}
private void createInsertDataSourcesMenu() {
final JMenu insertDataSourcesMenu =
context.getView().getComponent( "insert-datasources-menu", JMenu.class );// NON-NLS
if ( insertDataSourcesMenu != null ) {
ContextMenuUtility.createDataSourceMenu( context, insertDataSourcesMenu );
}
}
private void createInsertElementsMenu() {
final XulMenupopup insertElementsMenu =
context.getView().getXulComponent( "insert-elements-popup", XulMenupopup.class );// NON-NLS
if ( insertElementsMenu == null ) {
return;
}
final ElementMetaData[] datas = ElementTypeRegistry.getInstance().getAllElementTypes();
Arrays.sort( datas, new GroupedMetaDataComparator() );
Object grouping = null;
boolean firstElement = true;
for ( int i = 0; i < datas.length; i++ ) {
final ElementMetaData data = datas[ i ];
if ( data.isHidden() ) {
continue;
}
final String currentGrouping = data.getGrouping( Locale.getDefault() );
if ( firstElement == false ) {
if ( ObjectUtilities.equal( currentGrouping, grouping ) == false ) {
grouping = currentGrouping;
SwingMenuseparator separator = new SwingMenuseparator( null, null, null, "menuseparator" );
insertElementsMenu.addChild( separator );
}
} else {
grouping = currentGrouping;
firstElement = false;
}
final InsertElementAction action = new InsertElementAction( data );
action.setReportDesignerContext( context );
ActionSwingMenuitem menuItem = new ActionSwingMenuitem( ActionSwingMenuitem.MENUITEM );
menuItem.setAction( action );
insertElementsMenu.addChild( menuItem );
}
}
private void createRecentFilesMenu() {
final XulMenupopup reopenMenu =
context.getView().getXulComponent( "file-reopen-popup", XulMenupopup.class ); // NON-NLS
final XulMenuitem clearMenuitem =
context.getView().getXulComponent( "file-clear-recent", XulMenuitem.class );// NON-NLS
if ( reopenMenu != null && clearMenuitem != null ) {
final RecentFilesUpdateHandler updateHandler = new RecentFilesUpdateHandler( context, reopenMenu, clearMenuitem );
updateHandler.settingsChanged();
context.getRecentFilesModel().addSettingsListener( updateHandler );
}
}
private void createZoomMenu() {
final XulComponent zoomMenu =
context.getView().getXulComponent( "view-zoom-selection-popup", XulMenupopup.class );// NON-NLS
if ( zoomMenu == null ) {
return;
}
final InternalZoomAction zoom50action = new InternalZoomAction( 50 );
final InternalZoomAction zoom100action = new InternalZoomAction( 100 );
final InternalZoomAction zoom200action = new InternalZoomAction( 200 );
final InternalZoomAction zoom400action = new InternalZoomAction( 400 );
zoom50action.setReportDesignerContext( context );
zoom100action.setReportDesignerContext( context );
zoom200action.setReportDesignerContext( context );
zoom400action.setReportDesignerContext( context );
zoomMenu.addChild( context.getView().createMenuItem( zoom50action ) );
zoomMenu.addChild( context.getView().createMenuItem( zoom100action ) );
zoomMenu.addChild( context.getView().createMenuItem( zoom200action ) );
zoomMenu.addChild( context.getView().createMenuItem( zoom400action ) );
}
private void createSamplesMenu() {
final XulMenupopup samplesPopup =
context.getView().getXulComponent( "help-samples-popup", XulMenupopup.class );// NON-NLS
if ( samplesPopup == null ) {
return;
}
for ( final XulComponent childNode : new ArrayList<XulComponent>( samplesPopup.getChildNodes() ) ) {
samplesPopup.removeChild( childNode );
}
final TreeModel treeModel = SamplesTreeBuilder.getSampleTreeModel();
final Object root = treeModel.getRoot();
try {
insertReports( treeModel, root, samplesPopup );
} catch ( XulException e ) {
logger.warn( "Failed to initialize sample menu", e );
}
}
private void insertReports( final TreeModel model,
final Object currentLevel,
final XulMenupopup popup ) throws XulException {
final int childCount = model.getChildCount( currentLevel );
for ( int i = 0; i < childCount; i += 1 ) {
final ReportDesignerView frame = context.getView();
final Object child = model.getChild( currentLevel, i );
if ( model.isLeaf( child ) ) {
final DefaultMutableTreeNode node = (DefaultMutableTreeNode) child;
final File file = new File( String.valueOf( node.getUserObject() ) );
final OpenSampleReportAction action = new OpenSampleReportAction( file, node.toString() );
action.setReportDesignerContext( context );
popup.addChild( frame.createMenuItem( action ) );
} else {
final XulMenupopup childPopup = frame.createPopupMenu( String.valueOf( child ), popup );
insertReports( model, child, childPopup );
}
}
}
private void createMorphMenu() {
final JMenu morphMenu = context.getView().getComponent( "format-morph-menu", JMenu.class );// NON-NLS PRD-4452
if ( morphMenu == null ) {
return;
}
final ElementMetaData[] datas = ElementTypeRegistry.getInstance().getAllElementTypes();
Arrays.sort( datas, new GroupedMetaDataComparator() );
Object grouping = null;
boolean firstElement = true;
for ( int i = 0; i < datas.length; i++ ) {
final ElementMetaData data = datas[ i ];
if ( data.isHidden() ) {
continue;
}
final String currentGrouping = data.getGrouping( Locale.getDefault() );
if ( firstElement == false ) {
if ( ObjectUtilities.equal( currentGrouping, grouping ) == false ) {
grouping = currentGrouping;
morphMenu.addSeparator();
}
} else {
grouping = currentGrouping;
firstElement = false;
}
try {
final MorphAction action = new MorphAction( data.create() );
action.setReportDesignerContext( context );
morphMenu.add( new JMenuItem( action ) );
} catch ( InstantiationException e ) {
UncaughtExceptionsModel.getInstance().addException( e );
}
}
}
private void initializeToolWindows() {
this.reportTreeToolWindow = createStructureTreeToolWindow();
this.attributeToolWindow = createAttributesToolWindow();
this.inspectionsToolWindow = createInspectionsToolWindow();
dockingPane.add( GlobalPane.Alignment.RIGHT, reportTreeToolWindow );
dockingPane.add( GlobalPane.Alignment.RIGHT, attributeToolWindow );
dockingPane.add( GlobalPane.Alignment.BOTTOM, inspectionsToolWindow );
dockingPane.setPreferredContentSize( GlobalPane.Alignment.BOTTOM, 100 );
dockingPane.setPreferredContentSize( GlobalPane.Alignment.RIGHT, 300 );
// initialize add elements
initAddElements( "popup-Band.add-element-menu" );// NON-NLS
initAddElements( "popup-RootLevelBand.add-element-menu" );// NON-NLS
initSetBarcodeTypeElements( "popup-Barcode.type" );// NON-NLS
}
private Category createAttributesToolWindow() {
final ImageIcon propertyTableIcon = IconLoader.getInstance().getPropertyTableIcon();
return new Category( propertyTableIcon, Messages.getString( "Attribute.Title" ), attributeEditorPanel );// NON-NLS
}
private Category createInspectionsToolWindow() {
// this is the bottom 'messages' panel
final InspectionSidePanePanel inspectionsMessagePanel = new InspectionSidePanePanel();
inspectionsMessagePanel.setReportDesignerContext( context );
final InternalWindow inspectionGadgetInternalWindow =
new InternalWindow( Messages.getString( "InspectionGadget.Title" ) );// NON-NLS
inspectionGadgetInternalWindow.add( inspectionsMessagePanel, BorderLayout.CENTER );
final Category category = new Category
( IconLoader.getInstance().getMessagesIcon(),
Messages.getString( "Messages.Title" ),// NON-NLS
inspectionGadgetInternalWindow );
category.setMinimized( true );
return category;
}
private Category createStructureTreeToolWindow() {
return new Category( IconLoader.getInstance().getReportTreeIcon(),
Messages.getString( "StructureView.Title" ),// NON-NLS
treePanel );
}
private void initSetBarcodeTypeElements( final String id ) {
final JMenu menu = context.getView().getComponent( id, JMenu.class );
if ( menu == null ) {
return;
}
final BarcodeTypePropertyEditor editor = new BarcodeTypePropertyEditor();
final String[] tags = editor.getTags();
for ( int i = 0; i < tags.length; i++ ) {
final String tag = tags[ i ];
final BarcodeTypeAction action = new BarcodeTypeAction( tag );
action.setReportDesignerContext( context );
menu.add( new JRadioButtonMenuItem( action ) );
}
}
private void initAddElements( final String id ) {
final JMenu menu = context.getView().getComponent( id, JMenu.class );
if ( menu == null ) {
return;
}
final ElementMetaData[] datas = ElementTypeRegistry.getInstance().getAllElementTypes();
Arrays.sort( datas, new GroupedMetaDataComparator() );
Object grouping = null;
boolean firstElement = true;
for ( int i = 0; i < datas.length; i++ ) {
final ElementMetaData data = datas[ i ];
if ( data.isHidden() ) {
continue;
}
if ( !WorkspaceSettings.getInstance().isVisible( data ) ) {
continue;
}
final String currentGrouping = data.getGrouping( Locale.getDefault() );
if ( firstElement == false ) {
if ( ObjectUtilities.equal( currentGrouping, grouping ) == false ) {
grouping = currentGrouping;
menu.addSeparator();
}
} else {
grouping = currentGrouping;
firstElement = false;
}
final InsertElementAction action = new InsertElementAction( data );
action.setReportDesignerContext( context );
menu.add( new JMenuItem( action ) );
}
}
private JComponent createPaletteToolBar() {
final JToolBar toolBar = new JToolBar();
toolBar.setFloatable( false );
toolBar.setOrientation( JToolBar.VERTICAL );
final ElementMetaData[] datas = ElementTypeRegistry.getInstance().getAllElementTypes();
Arrays.sort( datas, new GroupedMetaDataComparator() );
Object grouping = null;
boolean firstElement = true;
for ( int i = 0; i < datas.length; i++ ) {
final ElementMetaData data = datas[ i ];
if ( data.isHidden() ) {
continue;
}
if ( !WorkspaceSettings.getInstance().isVisible( data ) ) {
continue;
}
final String currentGrouping = data.getGrouping( Locale.getDefault() );
if ( firstElement == false ) {
if ( ObjectUtilities.equal( currentGrouping, grouping ) == false ) {
grouping = currentGrouping;
toolBar.addSeparator();
}
} else {
grouping = currentGrouping;
firstElement = false;
}
final InsertElementAction action = new InsertElementAction( data );
action.setReportDesignerContext( context );
toolBar.add( new PaletteButton( data, context ) );
}
final JScrollPane paletteScrollpane = new JScrollPane( toolBar );
paletteScrollpane.setHorizontalScrollBarPolicy( JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );
paletteScrollpane
.addComponentListener( new ScrollbarSyncHandler( paletteScrollpane.getVerticalScrollBar(), toolBar ) );
return paletteScrollpane;
}
private void rebuildReportMenu() {
final XulComponent reopenMenu = context.getView().getXulComponent( "window.reports-area", XulComponent.class );
if ( reopenMenu == null ) {
return;
}
final List<XulComponent> xulComponents = reopenMenu.getChildNodes();
final XulComponent[] objects = xulComponents.toArray( new XulComponent[ xulComponents.size() ] );
for ( int i = 0; i < objects.length; i++ ) {
final XulComponent object = objects[ i ];
reopenMenu.removeChild( object );
}
final JTabbedPane tabbedPane = getReportEditorPane();
final int count = tabbedPane.getTabCount();
if ( count > 0 ) {
reopenMenu.addChild( new SwingMenuseparator( null, null, null, "menu-separator" ) ); // NON-NLS
for ( int i = 0; i < count; i++ ) {
final Component at = tabbedPane.getTabComponentAt( i );
final String tabName;
if ( at instanceof TabRenderer ) {
final TabRenderer renderer = (TabRenderer) at;
tabName = renderer.getTitle();
} else {
tabName = tabbedPane.getTitleAt( i );
}
final SelectTabAction action = new SelectTabAction( i, tabName );
final ActionSwingMenuitem actionSwingMenuitem = context.getView().createMenuItem( action );
actionSwingMenuitem.setReportDesignerContext( context );
reopenMenu.addChild( actionSwingMenuitem );
}
}
}
protected void updateFrameTitle() {
final int i = getReportEditorPane().getSelectedIndex();
final String report;
final String reportName;
if ( getContext() != null && getContext().getActiveContext() != null ) {
if ( i == -1 ) {
reportName = null;
} else {
final Component at = getReportEditorPane().getTabComponentAt( i );
if ( at instanceof TabRenderer ) {
final TabRenderer renderer = (TabRenderer) at;
reportName = renderer.getTitle();
} else {
reportName = getReportEditorPane().getTitleAt( i );
}
}
report = getContext().getActiveContext().getDocumentFile();
} else {
report = null;
reportName = null;
}
setTitle( computeFrameTitle( reportName, report ) );
}
private String computeFrameTitle( final String tabName, final String path ) {
if ( tabName == null || path == null ) {
return Messages.getString( "ReportDesignerFrame.Title" );// NON-NLS
}
if ( StringUtils.isEmpty( path ) ) {
return Messages.getString( "ReportDesignerFrame.TitleWithName", tabName );// NON-NLS
} else {
return Messages.getString( "ReportDesignerFrame.TitleWithNameAndPath", tabName, path );// NON-NLS
}
}
private void recomputeAllTabTitles() {
final JTabbedPane editorPane = getReportEditorPane();
final int count = editorPane.getTabCount();
for ( int i = 0; i < count; i++ ) {
final Component at = editorPane.getTabComponentAt( i );
if ( at instanceof TabRenderer ) {
final TabRenderer renderer = (TabRenderer) at;
renderer.setTitle( renderer.recomputeTabName() );
}
}
updateFrameTitle();
}
private class VisibleElementsUpdateHandler implements SettingsListener {
private boolean deprecated;
private boolean expert;
private MaturityLevel maturityLevel;
private VisibleElementsUpdateHandler() {
deprecated = WorkspaceSettings.getInstance().isShowDeprecatedItems();
expert = WorkspaceSettings.getInstance().isShowExpertItems();
maturityLevel = WorkspaceSettings.getInstance().getMaturityLevel();
}
public void settingsChanged() {
if ( isChanged() ) {
try {
// throw away the old model and re-parse the XUL file from scratch.
// This is easier than trying to patch a wild and undocumented implementation of the Xul backend
viewController.initializeXulDesignerFrame( context );
getContentPane().remove( paletteToolBar );
paletteToolBar = createPaletteToolBar();
getContentPane().add( paletteToolBar, BorderLayout.WEST );
setJMenuBar( createMenuBar() );
revalidate();
repaint();
} catch ( XulException e ) {
UncaughtExceptionsModel.getInstance().addException( e );
}
deprecated = WorkspaceSettings.getInstance().isShowDeprecatedItems();
expert = WorkspaceSettings.getInstance().isShowExpertItems();
maturityLevel = WorkspaceSettings.getInstance().getMaturityLevel();
}
}
private boolean isChanged() {
if ( WorkspaceSettings.getInstance().isShowDeprecatedItems() != deprecated ) {
return true;
}
if ( WorkspaceSettings.getInstance().isShowExpertItems() != expert ) {
return true;
}
if ( WorkspaceSettings.getInstance().getMaturityLevel() != maturityLevel ) {
return true;
}
return false;
}
}
}