/*
* 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 - 2013 Object Refinery Ltd, Pentaho Corporation and Contributors.. All rights reserved.
*/
package org.pentaho.reporting.tools.configeditor.editor;
import org.pentaho.reporting.libraries.base.boot.Module;
import org.pentaho.reporting.libraries.base.config.HierarchicalConfiguration;
import org.pentaho.reporting.tools.configeditor.Messages;
import org.pentaho.reporting.tools.configeditor.model.ClassConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.ConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.EnumConfigDescriptionEntry;
import org.pentaho.reporting.tools.configeditor.model.ModuleNodeFactory;
import org.pentaho.reporting.tools.configeditor.util.ConfigDescriptionEntryComparator;
import org.pentaho.reporting.tools.configeditor.util.VerticalLayout;
import javax.swing.*;
import javax.swing.text.html.HTMLEditorKit;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* The default module editor provides a simple default implementation to edit all configuration keys for a given
* module.
*
* @author Thomas Morgner
*/
public class DefaultModuleEditor implements ModuleEditor {
/**
* Handles the selection of an checkbox and enables the assigned editor component.
*/
private static class EnableAction implements ActionListener {
/**
* The key editor that is assigned to the checkbox.
*/
private final KeyEditor editor;
/**
* The source checkbox, to which this action is assigned.
*/
private final JCheckBox source;
/**
* Creates a new enable action for the given checkbox.
*
* @param ed the key editor that is assigned to the checkbox
* @param source the checkbox on which this action is registered-
*/
private EnableAction( final KeyEditor ed, final JCheckBox source ) {
this.editor = ed;
this.source = source;
}
/**
* Enables the key editor if the checkbox is selected.
*
* @param e not used
*/
public void actionPerformed( final ActionEvent e ) {
editor.setEnabled( source.isSelected() );
}
}
/**
* A editor carrier implementation used to collect all active editor components and their assigned checkboxes.
*/
private static class EditorCarrier {
/**
* The editor component.
*/
private final KeyEditor editor;
/**
* The checkbox that enabled the editor.
*/
private final JCheckBox enableBox;
/**
* Creates a new carrier for the given editor and checkbox.
*
* @param editor the editor component to which the checkbox is assigned
* @param enableBox the checkbox that enabled the editor.
*/
private EditorCarrier( final KeyEditor editor, final JCheckBox enableBox ) {
this.editor = editor;
this.enableBox = enableBox;
}
/**
* Return the key editor.
*
* @return the editor.
*/
public KeyEditor getEditor() {
return editor;
}
/**
* Resets the keyeditor and the checkbox to the default value.
*/
public void reset() {
enableBox.setSelected( editor.isDefined() );
editor.setEnabled( editor.isDefined() );
}
}
/**
* The contentpane that holds all other components.
*/
private final JPanel contentpane;
/**
* The rootpane holds the editor and the help area.
*/
private final JSplitPane rootpane;
/**
* The rootpane holds the editor and the help area.
*/
private final JEditorPane helpPane;
/**
* Externalized string access
*/
private final Messages messages;
/**
* The report configuration used in this module editor.
*/
private HierarchicalConfiguration config;
/**
* The list of keynames used in the editor.
*/
private ConfigDescriptionEntry[] keyNames;
/**
* all active key editors as array.
*/
private EditorCarrier[] activeEditors;
/**
* The module which we edit.
*/
private Module module;
/**
* The package of the module implementation.
*/
private String modulePackage;
/**
* Creates a new, uninitialized module editor.
*/
public DefaultModuleEditor() {
messages = Messages.getInstance();
contentpane = new JPanel();
contentpane.setLayout( new VerticalLayout() );
helpPane = new JEditorPane();
helpPane.setEditable( false );
helpPane.setEditorKit( new HTMLEditorKit() );
helpPane.setPreferredSize( new Dimension( 600, 100 ) );
final JPanel toolbar = new JPanel();
toolbar.setLayout( new BorderLayout() );
toolbar.add( new JScrollPane( helpPane ) );
toolbar.setMinimumSize( new Dimension( 100, 150 ) );
rootpane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
try {
// An ugly way of calling
// rootpane.setResizeWeight(1);
final Method m = rootpane.getClass().getMethod
( "setResizeWeight", new Class[] { Double.TYPE } ); //$NON-NLS-1$
m.invoke( rootpane, new Object[] { new Double( 1 ) } );
} catch ( Exception e ) {
// ignored ...
}
rootpane.setBottomComponent( toolbar );
rootpane.setTopComponent( new JScrollPane( contentpane ) );
}
/**
* Creates a new, initialized instance of the default module editor.
*
* @param module the module that should be edited.
* @param config the report configuration used to fill the values of the editors.
* @param keyNames the list of keynames this module editor should handle.
* @return the created new editor instance.
* @see ModuleEditor#createInstance(Module, HierarchicalConfiguration, ConfigDescriptionEntry[])
*/
public ModuleEditor createInstance
( final Module module, final HierarchicalConfiguration config,
final ConfigDescriptionEntry[] keyNames ) {
final DefaultModuleEditor ed = new DefaultModuleEditor();
ed.setConfig( config );
ed.setKeyNames( keyNames );
ed.setModule( module );
ed.build();
return ed;
}
/**
* Returns the currently edited module.
*
* @return the module of this editor.
*/
protected Module getModule() {
return module;
}
/**
* Defines the module for this editor.
*
* @param module the module, which should be handled by this editor.
*/
protected void setModule( final Module module ) {
if ( module == null ) {
throw new NullPointerException();
}
this.module = module;
this.modulePackage = ModuleNodeFactory.getPackage( module.getClass() );
}
/**
* Checks whether this module editor can handle the given module.
*
* @param module the module to be edited.
* @return true, if this editor may be used to edit the module, false otherwise.
* @see ModuleEditor#canHandle(Module)
*/
public boolean canHandle( final Module module ) {
return true;
}
/**
* Returns the report configuration used when loading values for this editor.
*
* @return the report configuration.
*/
protected HierarchicalConfiguration getConfig() {
return config;
}
/**
* Defines the report configuration for this editor.
*
* @param config the report configuration.
*/
protected void setConfig( final HierarchicalConfiguration config ) {
this.config = config;
}
/**
* Returns the key names used in this editor.
*
* @return the keynames.
*/
protected ConfigDescriptionEntry[] getKeyNames() {
return keyNames.clone();
}
/**
* Defines the suggested key names for the module editor. This implementation will use these keys to build the key
* editors.
*
* @param keyNames the key names for the editor.
*/
protected void setKeyNames( final ConfigDescriptionEntry[] keyNames ) {
this.keyNames = keyNames.clone();
Arrays.sort( this.keyNames, new ConfigDescriptionEntryComparator() );
}
/**
* Returns the editor component of the module. Calling this method is only valid on instances created with
* createInstance.
*
* @return the editor component for the GUI.
*/
public JComponent getComponent() {
return rootpane;
}
/**
* Creates a cut down display name for the given key. The display name will replace the module package with '~'.
*
* @param keyName the keyname which should be shortend.
* @return the modified keyname suitable to be displayed as label.
*/
private String createDisplayName( final String keyName ) {
if ( keyName.startsWith( modulePackage ) ) {
return '~' + keyName.substring( modulePackage.length() );
}
return keyName;
}
/**
* Initializes all component for the module editor and creates and layouts all keyeditors.
*/
protected void build() {
final StringWriter writer = new StringWriter();
writer.write( "<html><head><title></title></head><body>" ); //$NON-NLS-1$
final JTextArea mangleInfo = new JTextArea();
mangleInfo.setText( messages.getString( "DefaultModuleEditor.USER_GUIDE", modulePackage ) ); //$NON-NLS-1$
mangleInfo.setName( "DescriptionArea" ); //$NON-NLS-1$
mangleInfo.setMinimumSize( new Dimension( 100, 10 ) );
mangleInfo.setEditable( false );
mangleInfo.setLineWrap( true );
mangleInfo.setWrapStyleWord( true );
mangleInfo.setOpaque( false );
contentpane.add( mangleInfo );
final ConfigDescriptionEntry[] keyNames = getKeyNames();
if ( keyNames == null ) {
throw new IllegalStateException(
messages.getString( "DefaultModuleEditor.ERROR_0001_NO_KEYS_DEFINED" ) ); //$NON-NLS-1$
}
activeEditors = new EditorCarrier[ keyNames.length ];
for ( int i = 0; i < keyNames.length; i++ ) {
final KeyEditor editor;
final String displayName = createDisplayName( keyNames[ i ].getKeyName() );
if ( keyNames[ i ] instanceof EnumConfigDescriptionEntry ) {
final EnumConfigDescriptionEntry entry = (EnumConfigDescriptionEntry) keyNames[ i ];
editor = new EnumKeyEditor( getConfig(), entry, displayName );
} else if ( keyNames[ i ] instanceof ClassConfigDescriptionEntry ) {
final ClassConfigDescriptionEntry entry = (ClassConfigDescriptionEntry) keyNames[ i ];
editor = new ClassKeyEditor( getConfig(), entry, displayName );
} else {
editor = new TextKeyEditor( getConfig(), keyNames[ i ], displayName );
}
final JCheckBox enableCB = new JCheckBox();
enableCB.addActionListener( new EnableAction( editor, enableCB ) );
final JPanel panel = new JPanel();
panel.setLayout( new BorderLayout() );
panel.add( enableCB, BorderLayout.WEST );
panel.add( editor.getComponent(), BorderLayout.CENTER );
contentpane.add( panel );
activeEditors[ i ] = new EditorCarrier( editor, enableCB );
writer.write( "<h3><b>" ); //$NON-NLS-1$
writer.write( keyNames[ i ].getKeyName() );
writer.write( "</b></h3>" ); //$NON-NLS-1$
writer.write( "<p>" ); //$NON-NLS-1$
writer.write( keyNames[ i ].getDescription() );
writer.write( "</p><hr>" ); //$NON-NLS-1$
}
int width = 0;
for ( int i = 0; i < activeEditors.length; i++ ) {
width = Math.max( width, activeEditors[ i ].getEditor().getLabelWidth() );
}
for ( int i = 0; i < activeEditors.length; i++ ) {
activeEditors[ i ].getEditor().setLabelWidth( width );
}
writer.write( "</body></html>" ); //$NON-NLS-1$
helpPane.setText( writer.toString() );
}
/**
* Resets all keys to the values from the report configuration.
*/
public void reset() {
for ( int i = 0; i < activeEditors.length; i++ ) {
activeEditors[ i ].reset();
}
}
/**
* Stores all values for the editor's keys into the report configuration.
*/
public void store() {
for ( int i = 0; i < activeEditors.length; i++ ) {
activeEditors[ i ].getEditor().store();
}
}
}