/*
* 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.tools.configeditor.editor;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.util.Arrays;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.text.html.HTMLEditorKit;
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;
/**
* 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 report configuration used in this module editor.
*/
private HierarchicalConfiguration config;
/**
* The list of keynames used in the editor.
*/
private ConfigDescriptionEntry[] keyNames;
/**
* The contentpane that holds all other components.
*/
private final JPanel contentpane;
/**
* 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;
/**
* 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;
/**
* 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 (ConfigDescriptionEntry[]) 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 = (ConfigDescriptionEntry[]) 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();
}
}
}