/*
* org.openmicroscopy.shoola.agents.metadata.util.ScriptComponent
*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2015 University of Dundee. All rights reserved.
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.util.ui;
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Font;
import java.util.Iterator;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import info.clearthought.layout.TableLayout;
import org.apache.commons.collections.CollectionUtils;
import org.openmicroscopy.shoola.util.CommonsLangUtils;
import org.openmicroscopy.shoola.agents.util.EditorUtil;
import org.openmicroscopy.shoola.env.data.model.ScriptObject;
import org.openmicroscopy.shoola.util.ui.NumericalTextField;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
/**
* Hosts information related to a parameter for the script.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* @since 3.0-Beta4
*/
class ScriptComponent
extends JPanel
implements ChangeListener
{
/** The number of columns. */
static int COLUMNS = 10;
/** Indicates the tabulation value. */
private static final int TAB = 15;
/** The default text for the component.*/
private static final String DEFAULT_TEXT = "";
/** The component to host. */
private JComponent component;
/** The text associated to the component. */
private JLabel label;
/** The text associated to the component. */
private JLabel requireLabel;
/** Component indicating the units or other text. */
private JLabel unitLabel;
/**
* The text explaining the component. It should only be set for
* collections and maps.
*/
private JLabel info;
/** Indicates if a value is required. */
private boolean required;
/** The index of the parent for grouping. */
private String parentIndex;
/** The name associated to the component. */
private String nameLabel;
/** The name of the parameter. */
private String name;
/** The grouping value or empty string. */
private String grouping;
/** The components related to the current component. */
private List<ScriptComponent> children;
/** The parent of the component. */
private ScriptComponent parent;
/**
* Iterates through the components and sets the background color.
*
* @param c The component to handle
* @param background The color to set.
*/
private void setComponentColor(JComponent c, Color background)
{
c.setBackground(background);
if (c.getComponentCount() > 0) {
Component[] components = c.getComponents();
for (int i = 0; i < components.length; i++) {
if (components[i] instanceof JComponent) {
setComponentColor((JComponent) components[i], background);
}
}
}
}
/**
* Creates a row.
*
* @return See above.
*/
private JPanel createRow()
{
JPanel row = new JPanel();
row.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
row.add(getLabel());
row.add(getComponent());
setComponentColor(row, ScriptingDialog.BG_COLOR);
return row;
}
/**
* Returns the tabulation index. Returns <code>0</code> if no parent index
* is set, otherwise the number of level in the parent e.g.
* if parent is <code>3</code>, the method returns <code>1</code>,
* if parent is <code>3.1</code>, the method returns <code>2</code>.
*
* @return See above.
*/
private int getTabulationLevel()
{
if (parentIndex == null || parentIndex.length() == 0)
return 0;
String[] values = parentIndex.split("\\.");
return values.length;
}
/**
* Returns the label associated to the component.
*
* @return See above.
*/
private JComponent getLabel()
{
JPanel p = new JPanel();
p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
if (requireLabel != null) {
JPanel lp = new JPanel();
lp.setLayout(new BoxLayout(lp, BoxLayout.X_AXIS));
lp.add(label);
lp.add(requireLabel);
p.add(lp);
} else p.add(label);
if (info != null) p.add(info);
return UIUtilities.buildComponentPanel(p, 0, 0);
}
/**
* Returns the component hosted.
*
* @return See above.
*/
private JComponent getComponent()
{
JPanel p = new JPanel();
p.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
p.add(component);
if (unitLabel != null) p.add(unitLabel);
return p;
}
/** Handles the selection. */
private void handleSelection()
{
if (children == null) return;
if (component instanceof JCheckBox) {
Iterator<ScriptComponent> i = children.iterator();
ScriptComponent c;
JCheckBox box = (JCheckBox) component;
boolean selected = box.isSelected();
while (i.hasNext()) {
c = i.next();
c.setEnabled(selected);
}
}
}
/** Creates a new instance.*/
ScriptComponent()
{
this(new JLabel(), DEFAULT_TEXT);
}
/**
* Creates a new instance.
*
* @param component The component to host.
* @param name The name of the parameter
*/
ScriptComponent(JComponent component, String name)
{
if (component == null)
throw new IllegalArgumentException("No component specified.");
if (name == null) name = DEFAULT_TEXT;
if (component instanceof NumericalTextField)
((NumericalTextField) component).setHorizontalAlignment(
JTextField.LEFT);
this.component = component;
this.name = name;
parentIndex = null;
//format
if (!name.equals(DEFAULT_TEXT))
name = " "+name+":";
if (name.contains(ScriptObject.PARAMETER_SEPARATOR)) {
label = UIUtilities.setTextFont(name.replace(
ScriptObject.PARAMETER_SEPARATOR,
ScriptObject.PARAMETER_UI_SEPARATOR));
} else {
label = UIUtilities.setTextFont(name);
}
label.setToolTipText(component.getToolTipText());
required = false;
}
/**
* Sets the parameter name.
*
* @param name The value to set.
*/
void setParameterName(String name) { this.name = name;}
/**
* Returns the name of the parameter.
*
* @return See above.
*/
String getParameterName() { return name; }
/**
* Sets the text explaining the component when the component is a list
* or a map.
*
* @param text The value to set.
*/
void setInfo(String text)
{
if (CommonsLangUtils.isBlank(text)) return;
info = new JLabel();
Font f = info.getFont();
info.setFont(f.deriveFont(Font.ITALIC, f.getSize()-2));
}
/**
* Sets to <code>true</code> if a value is required for the field,
* <code>false</code> otherwise.
*
* @param required The value to set.
*/
void setRequired(boolean required)
{
this.required = required;
if (required) {
requireLabel = UIUtilities.setTextFont(EditorUtil.MANDATORY_SYMBOL);
requireLabel.setForeground(UIUtilities.REQUIRED_FIELDS_COLOR);
requireLabel.setToolTipText(label.getToolTipText());
}
}
/**
* Sets the text of the unit label.
*
* @param text The value to set.
*/
void setUnit(String text)
{
if (CommonsLangUtils.isBlank(text)) return;
if (unitLabel == null) unitLabel = new JLabel();
unitLabel.setText(text);
}
/**
* Returns <code>true</code> if a value is required for that component.
*
* @return See above.
*/
boolean isRequired() { return required; }
/** Builds and lays out the UI. */
void buildUI()
{
int width = TAB*getTabulationLevel();
if (DEFAULT_TEXT.equals(name) || CommonsLangUtils.isNumber(name)){
width = 0;
}
if (CollectionUtils.isEmpty(children)) {
double[][] size = {{width, TableLayout.PREFERRED, 5,
TableLayout.FILL}, {TableLayout.PREFERRED}};
setLayout(new TableLayout(size));
add(Box.createHorizontalStrut(width), "0, 0");
add(getLabel(), "1, 0");
add(getComponent(), "3, 0");
} else {
//create a row.
double[][] size = {{TableLayout.FILL}, {TableLayout.PREFERRED}};
TableLayout layout = new TableLayout(size);
setLayout(layout);
Iterator<ScriptComponent> i = children.iterator();
ScriptComponent child;
setBorder(BorderFactory.createLineBorder(Color.LIGHT_GRAY));
add(createRow(), "0, 0");
int index = 1;
while (i.hasNext()) {
child = i.next();
String v = child.parentIndex;
if (name != null && name.equals(v)) {
child.parentIndex = null;
}
child.buildUI();
child.parentIndex = v;
layout.insertRow(index, TableLayout.PREFERRED);
add(child, "0, "+index);
index++;
}
}
}
/**
* Returns the value associated to a script.
*
* @return See above.
*/
Object getValue()
{
if (parent != null) {
Object v = parent.getValue();
if (v instanceof Boolean) {
boolean b = ((Boolean) v).booleanValue();
if (!b) return null;
}
}
return ScriptComponent.getComponentValue(component);
}
/**
* Helper method. Returns the value associated to a script.
*
* @param c The component to handle.
* @return See above.
*/
static Object getComponentValue(JComponent c)
{
if (c == null) return null;
if (c instanceof JCheckBox) {
JCheckBox box = (JCheckBox) c;
return box.isSelected();
} else if (c instanceof NumericalTextField) {
return ((NumericalTextField) c).getValueAsNumber();
} else if (c instanceof JTextField) {
JTextField field = (JTextField) c;
String value = field.getText();
if (CommonsLangUtils.isBlank(value))
return null;
return value;
} else if (c instanceof JComboBox) {
JComboBox box = (JComboBox) c;
return box.getSelectedItem();
} else if (c instanceof ComplexParamPane) {
return ((ComplexParamPane) c).getValue();
} else if (c instanceof IdentifierParamPane) {
return ((IdentifierParamPane) c).isReady();
}
return null;
}
/**
* Sets the parent grouping values.
*
* @param parentIndex The value to set.
*/
void setParentIndex(String parentIndex)
{
if (parentIndex != null) parentIndex = parentIndex.trim();
this.parentIndex = parentIndex;
}
/**
* Returns the parent grouping values.
*
* @return See above.
*/
String getParentIndex() { return parentIndex; }
/**
* Returns the grouping.
*
* @param grouping The value to set.
*/
void setGrouping(String grouping) { this.grouping = grouping; }
/**
* Returns the grouping.
*
* @return See above.
*/
String getGrouping() { return grouping; }
/**
* Returns the children associated to that component.
*
* @param children The value to set.
*/
void setChildren(List<ScriptComponent> children)
{
this.children = children;
if (component instanceof JCheckBox) {
JCheckBox box = (JCheckBox) component;
box.addChangeListener(this);
Iterator<ScriptComponent> i = children.iterator();
while (i.hasNext())
i.next().setParent(this);
}
handleSelection();
}
/**
* Sets the parent of the component.
*
* @param parent The value to set.
*/
void setParent(ScriptComponent parent)
{
if (children != null) return;
this.parent = parent;
}
/**
* Returns <code>true</code> if the component has children,
* <code>false</code> otherwise.
*
* @return See above.
*/
boolean hasChildren() { return CollectionUtils.isNotEmpty(children);}
/**
* Sets the name used to sort the object.
*
* @param nameLabel The value to set.
*/
void setNameLabel(String nameLabel) { this.nameLabel = nameLabel; }
/**
* Controls the children is any set.
* @see ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e) { handleSelection(); }
/**
* Overridden to handle grouping.
* @see JPanel#setEnabled(boolean)
*/
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
if (parentIndex == null) return;
component.setEnabled(enabled);
}
/**
* Overridden to sort the object using its label.
* @see JPanel#toString()
*/
public String toString() { return nameLabel; }
}