/**
* Copyright (C) 2008-2011 Daniel Senff
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
package de.danielsenff.imageflow.models.unit;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.awt.image.ImageObserver;
import java.net.URL;
import java.util.ArrayList;
import javax.swing.ActionMap;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.JTextComponent;
import visualap.Node;
import de.danielsenff.imageflow.ImageFlow;
import de.danielsenff.imageflow.ImageFlowView;
import de.danielsenff.imageflow.gui.PropertiesDialog;
import de.danielsenff.imageflow.gui.UnitElementInfoPanel;
import de.danielsenff.imageflow.gui.UnitPropertiesDialog;
import de.danielsenff.imageflow.models.Displayable;
import de.danielsenff.imageflow.models.MacroElement;
import de.danielsenff.imageflow.models.connection.Connection;
import de.danielsenff.imageflow.models.connection.Input;
import de.danielsenff.imageflow.models.connection.Output;
import de.danielsenff.imageflow.models.parameter.AbstractParameter;
import de.danielsenff.imageflow.models.parameter.BooleanParameter;
import de.danielsenff.imageflow.models.parameter.ChoiceParameter;
import de.danielsenff.imageflow.models.parameter.ParamChangeListener;
import de.danielsenff.imageflow.models.parameter.Parameter;
import de.danielsenff.imageflow.models.parameter.ParameterFactory;
import de.danielsenff.imageflow.models.unit.UnitModelComponent.Size;
import de.danielsenff.imageflow.utils.PaintUtil;
/**
* Backend-unit logic of a single node-element.
* Model for an instance of a workflow-element.
* This model is a basic model for an element in the graph and
* contains most of the graph logic.
*
* Next to meta data, it basically it contains 3 major parts.
* 3 Lists with {@link Input}s, {@link Output}s and {@link Input}.
*
* In the heart of the model is an Macro-Object, which
* contains the syntax, in which the parameters injected on runtime
* and which is executed by ImageJ.
* @author danielsenff
*
*/
public class UnitElement extends AbstractUnit implements ProcessingUnit, Displayable {
/**
* Pixel radius of tolerance round the pins
*/
protected int pinTolerance = 18;
/**
* name of this unit, this is not displayed, Label is displayed
*/
protected String unitName;
protected URL iconURL;
/**
* unit color
*/
protected Color color = new Color(0xA0A0A0);
/**
* help text, describing the unit's functionality
*/
protected String infoText;
/**
* unitIcon on the graphpanel
*/
protected Image unitIcon;
protected NodeIcon unitComponentIcon;
/**
* function icon, illustrates the purpose of the element
*/
protected Image preview;
public static int ICON_SIZE = 48;
/**
* unused, status of this unit
*/
public enum Status {OK, ERROR, WAITING };
/**
* Type of this UnitElement
*
*/
public enum Type {SOURCE, FILTER, SINK};
/**
* {@link Status} of this Unit.
*/
protected Status status;
UnitElement originalNode = null;
/**
* input array
*/
protected ArrayList<Input> inputs;
/**
* output array
*/
protected ArrayList<Output> outputs;
/**
* parameters that control the functionality of the unit
*/
protected ArrayList<Parameter> parameters;
/**
* Size the component will be displayed on the workspace.
*/
protected Size compontentSize;
private String iconPath;
/**
* @param unitName
* @param unitsImageJSyntax
*/
public UnitElement (
final String unitName,
final String unitsImageJSyntax) {
super(new Point(30,30), new MacroElement(unitsImageJSyntax));
setDimension(new Dimension(100,100));
init(unitName);
}
/**
* @param origin
* @param unitName
* @param unitsImageJSyntax
*/
public UnitElement (
final Point origin,
final String unitName,
final String unitsImageJSyntax) {
super(origin, new MacroElement(unitsImageJSyntax));
setDimension(new Dimension(100,100));
init(unitName);
}
/**
* @param origin
* @param unitName
* @param macroElement
*/
public UnitElement (
final Point origin,
final String unitName,
final MacroElement macroElement) {
super(origin, macroElement);
setDimension(new Dimension(100,100));
init(unitName);
}
/**
* @param unitName
*/
private void init(final String unitName) {
this.label = unitName;
this.unitName = unitName;
this.inputs = new ArrayList<Input>();
this.outputs = new ArrayList<Output>();
this.parameters = new ArrayList<Parameter>();
this.unitComponentIcon= new NodeIcon(this);
this.unitIcon = unitComponentIcon.getImage();
setCompontentSize(Size.BIG);
}
/*
* Outputs
*/
/**
* Gets the pinTolerance of this {@link UnitElement}
*/
public int getPinTolerance() {
return pinTolerance;
}
/**
* Sets the pinTolerance of this {@link UnitElement}
* @param pinTolerance Pixel radius of PinTolerance
*/
public void setPinTolerance(int pinTolerance) {
this.pinTolerance = pinTolerance;
}
/**
* Adds an Output-Object to the unit.
* @param newOutput
* @return
*/
public boolean addOutput(final Output newOutput) {
notifyModelListeners();
return this.outputs.add(newOutput);
}
/**
* Get one {@link Output} at the index. Indices start with 0;
* @param index
* @return
*/
public Output getOutput(final int index) {
return getOutputs().get(index);
}
/**
* Returns a list of all {@link Output}s
* @return
*/
public ArrayList<Output> getOutputs() {
return this.outputs;
}
/**
* Number of {@link Output}s.
* @return
*/
public int getOutputsCount() {
return this.outputs.size();
}
/**
* Returns true if an output is marked.
* @return
*/
public boolean hasMarkedOutput() {
for (Output output : this.outputs) {
// output is actually connected
if(output.isConnected()) {
int mark = ((UnitElement)output.getParent()).getMark();
// if mark is set to anything
if(mark != 0) {
return true;
}
}
}
return false;
}
/**
* Returns true, if this UnitElement has {@link Output}-Pins.
* @return
*/
public boolean hasOutputs() {
return !this.outputs.isEmpty();
}
/**
* Is true as soon as one connected {@link Output} is found.
* @return
*/
public boolean hasOutputsConnected() {
for (final Output output : outputs) {
if(output.isConnected())
return true;
}
return false;
}
/*
* Inputs
*/
/**
* Returns the {@link Input} at the given index. Indecies start with 0.
* @param index
* @return
*/
public Input getInput(final int index) {
return this.inputs.get(index);
}
/**
* List of all attached {@link Input}s
* @return
*/
public ArrayList<Input> getInputs() {
return this.inputs;
}
/**
* Returns if this unit has inputs attached to this unit.
* @return
*/
public boolean hasInputs() {
return (this.inputs.size() > 0);
}
/**
* Add new Input by Object.
* @param input
* @return
*/
public boolean addInput(final Input input) {
notifyModelListeners();
return this.inputs.add(input);
}
/**
* How many {@link Input}s are actually registered.
* @return
*/
public int getInputsCount() {
return this.inputs.size();
}
/**
* Is true as soon as one connected {@link Input} is found.
* @return
*/
public boolean hasInputsConnected() {
for (final Input input : inputs) {
if(input.isConnected())
return true;
}
return false;
}
/**
* Returns true if this unit has all required inputs connected.
* @return
*/
public boolean hasRequiredInputsConnected() {
for (final Input input : inputs) {
if(input.isRequired() && !input.isConnected()) {
return false;
}
}
return true;
}
/**
* Returns the typename of this unit. This is different to the
* name displayed in the workflow.
* @return the unitName
*/
public String getUnitName() {
return this.unitName;
}
@Override
public void drag(int dx, int dy) {
super.drag(dx, dy);
notifyModelListeners();
}
/*
* Parameters
*/
/**
* Add a Parameter to the unit
* @param parameter
* @return
*/
public boolean addParameter(final Parameter parameter){
final int parameterNumber = parameters.size()+1;
parameter.setParameterNumber(parameterNumber);
boolean add = this.parameters.add(parameter);
notifyModelListeners();
return add;
}
/**
* Returns how many assigned {@link AbstractParameter}s this unit actually has.
* @return
*/
public int getParametersCount() {
return this.parameters.size();
}
/**
* List of all {@link AbstractParameter}s available for this unit
* @return
*/
public ArrayList<Parameter> getParameters() {
return this.parameters;
}
/**
* Parameter on index i.
* @param i
* @return
*/
public Parameter getParameter(final int i) {
return this.parameters.get(i);
}
/**
* Displays a Popup-Window with the properties, that can be edited for this UnitElement.
* @param point leave null to center on screen
*/
public void showProperties(Point point) {
// PropertiesDialog not yet instantiated
if (this.propertiesDialog == null) {
this.propertiesDialog = new UnitPropertiesDialog(this, point);
}
// PropertiesDialog already exists, but is invisible
else if (!propertiesDialog.isVisible()) {
this.propertiesDialog.setVisible(true);
}
// PropertiesDialog already displayed, but has no focus
else {
this.propertiesDialog.toFront();
}
}
/**
* Hook for adding Custom Widgets.
* @param gd
*/
public void addCustomWidgets(final PropertiesDialog dialog) { }
/**
* Returns whether this unit has parameters or not.
* This doesn't concern the unit label.
* @return
*/
public boolean hasParameters() {
return !parameters.isEmpty();
}
/**
* Returns the what type this is.
* SOURCE - only {@link Output}
* SINK - only {@link Input}
* FILTER - {@link Input} and {@link Output}
* @return
*/
public Type getUnitType() {
if(this.inputs.size() > 0 && this.outputs.size() == 0) {
return Type.SINK;
} else if ( this.inputs.size() == 0 && this.outputs.size() > 0) {
return Type.SOURCE;
} else {
return Type.FILTER;
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return super.toString() + " Name:"+this.label + " Type:" + this.getUnitType() + " ID:" + this.nodeID;
}
/**
* Returns the Icon of the filter.
* @return the icon
*/
public Image getIcon() {
return this.preview;
}
/**
* Returns the path to the icon.
* @return
*/
public String getIconPath() {
if(this.iconPath !=null)
return this.iconPath;
else
return "";
}
/**
* Returns the {@link URL} of this unit's icon.
* @return
*/
public URL getIconURL() {
return this.iconURL;
}
public void setIconURL(URL iconURL) {
this.iconURL = iconURL;
//notifyModelListeners(); no notify, since this is not save worthy
}
public void setIconScaled(final Image icon) {
setIcon(scaleThumbnail(ICON_SIZE, ICON_SIZE, icon));
}
/**
* @param image the icon to set
*/
public void setIcon(Image image) {
this.preview = image;
this.unitComponentIcon.setIcon(image);
notifyModelListeners();
}
/**
* Updates the units representational {@link NodeIcon} on the workspace.
*/
public void updateUnitIcon() {
this.unitIcon = new NodeIcon(this).getImage();
}
/**
* Checks if there is an {@link Input} or an {@link Output} at this mouse coordinates.
*/
@Override
public Object contains(final int x, final int y) {
if ((x >= origin.x - pinTolerance)
&&(x < origin.x + pinTolerance)) {
int inputsMaxCount = getInputsCount();
int lower_y;
for (int i = 0; i < inputsMaxCount; i++) {
lower_y = PaintUtil.alignY(inputsMaxCount, i, getDimension().height, NodeIcon.pinSize)+origin.y;
if ((y >= lower_y)&&(y <= lower_y + pinTolerance*2)) {
return getInput(i);
}
}
}
if ((x >= origin.x + getDimension().width - pinTolerance)
&&(x < origin.x + getDimension().width + pinTolerance)) {
int outputsCount = getOutputsCount();
int lower_y;
for (int i = 0; i < outputsCount; i++) {
lower_y = PaintUtil.alignY(outputsCount, i, getDimension().height, NodeIcon.pinSize)+origin.y;
if ((y >= lower_y)&&(y <= lower_y + pinTolerance*2)) {
return getOutput(i);
}
}
}
return super.contains(x,y);
}
@Override
public Dimension getDimension() {
return NodeIcon.getDimensionFromSize(this.compontentSize);
}
/* (non-Javadoc)
* @see graph.Node#paint(java.awt.Graphics, java.awt.image.ImageObserver)
*/
@Override
public Rectangle paint(Graphics g, ImageObserver io) {
//TODO move from model to view
if (unitIcon == null) {
// obj != null, icon == null
g.setColor(selected ? Color.red : new Color(250, 220, 100));
g.fillRect(origin.x, origin.y, getDimension().width, getDimension().height);
g.setColor(Color.black);
g.drawRect(origin.x, origin.y, getDimension().width-1, getDimension().height-1);
} else {
if (selected) {
g.setColor(new Color(0,0,255,40));
g.fillRoundRect(origin.x-2, origin.y-2, getDimension().width+4, getDimension().height+4,
unitComponentIcon.arc, unitComponentIcon.arc);
g.setColor(new Color(0,0,0,44));
g.drawRoundRect(origin.x-2, origin.y-2, getDimension().width+4, getDimension().height+4,
unitComponentIcon.arc, unitComponentIcon.arc);
}
Image unitIcon = unitComponentIcon.getImage(this.compontentSize);
g.drawImage(unitIcon, this.origin.x, this.origin.y, null);
// unitComponentIcon.paintBigIcon((Graphics2D) g);
// unitComponentIcon.paintMediumIcon((Graphics2D) g);
// unitComponentIcon.paintSmallIcon((Graphics2D) g);
}
//draw inputs
int numberInputs = getInputsCount(), x, y;
for (int i = 0; i < numberInputs; i++) {
if(getInput(i).isRequired())
g.setColor(Color.BLACK);
else
g.setColor(new Color(0,0,0,60));
y = PaintUtil.alignY(numberInputs, i, unitComponentIcon.getHeight(), NodeIcon.pinSize);
g.fillRect(origin.x, origin.y+y, NodeIcon.pinSize, NodeIcon.pinSize);
if(getInput(i).isLocked())
g.fillRect(origin.x-20, origin.y+y, NodeIcon.pinSize, NodeIcon.pinSize);
}
//draw outputs
int numberOutputs = getOutputsCount();
Polygon po;
for (int i = 0; i < numberOutputs; i++) {
g.setColor(Color.BLACK);
x = (unitComponentIcon.getWidth() - 8) + origin.x;
y = PaintUtil.alignY(numberOutputs, i, unitComponentIcon.getHeight(), NodeIcon.pinSize)+origin.y;
if(getOutput(i).isLocked())
g.fillRect(x+20, y, NodeIcon.pinSize, NodeIcon.pinSize);
po = new Polygon();
po.addPoint(x, y); //top
po.addPoint(x + NodeIcon.pinSize, y + (NodeIcon.pinSize/2)); //pointy
po.addPoint(x, y+NodeIcon.pinSize); //bottom
g.fillPolygon(po);
g.drawPolygon(po);
}
// during draggin
if (dragging != null) {
/*g.setColor(Color.black);
g.drawRect(dragging.x, dragging.y, dragging.width-1, dragging.height-1);*/
g.setColor(new Color(0,0,255, 40));
g.fillRoundRect(dragging.x+5, dragging.y+5, getDimension().width-10, getDimension().height-10,
unitComponentIcon.arc, unitComponentIcon.arc);
g.setColor(new Color(0,0,0));
g.drawRoundRect(dragging.x+5, dragging.y+5, getDimension().width-10, getDimension().height-10,
unitComponentIcon.arc, unitComponentIcon.arc);
}
return new Rectangle(origin, getDimension());
}
public NodeIcon getUnitComponentIcon() {
return unitComponentIcon;
}
protected BufferedImage scaleThumbnail(int width, int height, Image image) {
BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D) thumbnail.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
g2.drawImage(image, 0, 0, width, height, null);
g2.dispose();
return thumbnail;
}
/*
* Object
*/
/* (non-Javadoc)
* @see graph.NodeAbstract#setObject(java.lang.Object)
*/
@Override
public void setObject(Object obj) {
this.obj = obj;
}
/* (non-Javadoc)
* @see graph.Node#clone()
*/
@Override
public UnitElement clone() {
// clone the object
String macroSyntax;
try {
macroSyntax = (String) cloneNonClonableObject(this.obj);
} catch (CloneNotSupportedException e) {
macroSyntax = ((MacroElement)this.obj).getImageJSyntax();
}
UnitElement clone = new UnitElement(new Point(origin.x+15, origin.y+15),
this.label,
macroSyntax);
clone.setOriginalUnit(this);
for (int j = 0; j < getInputsCount(); j++) {
cloneInput(clone, j);
}
for (int i = 0; i < getOutputsCount(); i++) {
cloneOutput(clone, i);
}
for (Parameter parameter : parameters) {
cloneParameter(clone, parameter);
}
clone.setDisplay(isDisplay());
clone.setDisplaySilent(isDisplaySilent());
clone.setColor(this.color);
clone.setIcon(this.preview);
clone.setHelpString(this.infoText);
clone.setCompontentSize(this.getCompontentSize());
return clone;
}
void setOriginalUnit(UnitElement unitElement) {
this.originalNode = unitElement;
}
public UnitElement getOriginalUnit() {
return this.originalNode;
}
public boolean hasOriginalUnit() {
return this.originalNode != null;
}
public boolean isClonedUnit() {
return this.originalNode != null;
}
/**
* @param clone
* @param parameter
*/
protected void cloneParameter(final UnitElement clone, final Parameter parameter) {
Parameter clonedParameter;
if(parameter instanceof ChoiceParameter) {
clonedParameter = ParameterFactory.createChoiceParameter(parameter.getDisplayName(),
parameter.getParaType(),
((ChoiceParameter)parameter).getChoices(),
parameter.getHelpString(),
parameter.getOptions());
} else if (parameter instanceof BooleanParameter){
clonedParameter = ParameterFactory.createParameter(parameter.getDisplayName(),
parameter.getParaType(),
parameter.getValue(),
parameter.getHelpString(),
((BooleanParameter)parameter).getTrueString(),
parameter.getOptions());
} else {
clonedParameter = ParameterFactory.createParameter(parameter.getDisplayName(),
parameter.getParaType(),
parameter.getValue(),
parameter.getHelpString(),
parameter.getOptions());
}
clone.addParameter(clonedParameter);
}
/**
* @param clone
* @param i
*/
protected void cloneOutput(final UnitElement clone, final int i) {
Output output = getOutput(i);
Output clonedOutput = new Output(output.getDataType().clone(), clone, i+1);
clonedOutput.setupOutput(output.getName(), output.getShortDisplayName());
clonedOutput.setDoDisplay(output.isDoDisplay());
clonedOutput.setDoDisplaySilent(output.isDoDisplaySilent());
clone.addOutput(clonedOutput);
}
/**
* @param clone
* @param j
*/
protected void cloneInput(final UnitElement clone, final int j) {
Input input = getInput(j);
Input clonedInput = new Input(input.getDataType().clone(), clone, j+1, input.isRequired());
clonedInput.setupInput(input.getName(), input.getShortDisplayName(), input.isNeedToCopyInput());
clone.addInput(clonedInput);
}
/*
* Marking
*/
/**
* Mark this unit by marking its {@link Input}s and {@link Output}s.
* @param mark
*/
public void setMark(int mark) {
for (Input input : inputs) {
input.setMark(mark);
}
for (Output output : outputs) {
output.setMark(mark);
}
notifyModelListeners();
}
/**
* Returns the mark of the attached {@link Output}.
* Doesn't help it if the mark is different on any pin
* @return
*/
public int getMark() {
return outputs.get(0).getMark();
// doesn't help it if the mark is different on any pin
}
/**
* Checks a unit, if its inputs have already been registered in the algorithm.
* It's marked, when it's not 0. The int is conjecture to the order.
* TODO: this method name may be confusing: it doesn't check if all inputs of this unit are marked
* @param unit
* @return
*/
public boolean hasAllInputsMarked() {
if(hasInputs()) {
// check each input, if it's parent has been registered
for (Input input : getInputs()) {
if( (input.isConnected()
&& !input.getFromOutput().isMarked())
|| !input.isConnected() && input.isRequired() ) {
// this connected output hasn't been registered and is missing a mark,
// so the whole unit isn't ready set.
return false;
// otherwise mark is already set, so this output is fine
}
}
}
// if there are no inputs, it's true
return true;
}
/**
* Returns the base color of the units representation.
* @return
*/
public Color getColor() {
return color;
}
/**
* Set the color of the unit.
* @param color
*/
public void setColor(Color color) {
this.color = color;
notifyModelListeners();
}
/**
* HelpString is a short description for example to use in Tooltips.
* @return the infoText
*/
public String getHelpString() {
return this.infoText;
}
/**
* Sets a new help descriptions message for this unit.
* @param helpString
*/
public void setHelpString(final String helpString) {
this.infoText = helpString;
}
/**
* @return the compontentSize
*/
public Size getCompontentSize() {
return compontentSize;
}
/**
* @param compontentSize the compontentSize to set
*/
public void setCompontentSize(Size compontentSize) {
this.compontentSize = compontentSize;
this.unitComponentIcon.setDimension(compontentSize);
notifyModelListeners();
}
/**
* Returns true, if the unit in question is positioned north of this unit.
* @param node
* @return
*/
public boolean isNorthOfUnit(Node node) {
return (node.getOrigin().y > this.getOrigin().y);
}
/**
* Returns true, if the unit in question is positioned north of this unit.
* @param node
* @return
*/
public boolean isEastOfUnit(Node node) {
return (node.getOrigin().x < this.getOrigin().x);
}
/**
* Returns true, if the unit in question is positioned north of this unit.
* @param node
* @return
*/
public boolean isSouthOfUnit(Node node) {
return (node.getOrigin().y < this.getOrigin().y);
}
/**
* Returns true, if the unit in question is positioned north of this unit.
* @param node
* @return
*/
public boolean isWestOfUnit(Node node) {
return (node.getOrigin().x > this.getOrigin().x);
}
/*
* Displayable
*/
/**
* boolean indicating if this unit is a display unit
* The result of DisplayUnits will be shown after executing the workflow.
*/
private boolean display = false;
private boolean displaySilent = false;
public void setDisplay(final boolean isDisplay) {
this.display = isDisplay;
for (Output output : getOutputs()) {
output.setDoDisplay(isDisplay);
}
notifyModelListeners();
}
public void setDisplaySilent(final boolean isDisplay) {
this.displaySilent = isDisplay;
for (Output output : getOutputs()) {
output.setDoDisplaySilent(isDisplay);
}
notifyModelListeners();
}
public boolean isDisplay() {
return this.display;
}
/**
* Returns true if the unit is either displayed or silently displayed.
* @return
*/
public boolean isDisplayAny() {
return this.display || this.displaySilent;
}
public boolean isDisplaySilent() {
return this.displaySilent;
}
public void toggleDisplay() {
setDisplay(!isDisplay());
}
public void toggleDisplaySilent() {
setDisplaySilent(!isDisplaySilent());
}
/**
* Returns true if any {@link Output} connects to a unit that is set as displayable.
* @return
*/
public boolean hasDisplayBranch() {
if(isDisplayAny())
return true;
for (Output output : getOutputs()) {
if(output.isConnected()) {
for (Connection connection : output.getConnections()) {
UnitElement next = (UnitElement)connection.getToUnit();
if(next.hasDisplayBranch()) {
return true;
}
}
}
}
return false;
}
private JComponent widget;
private JComponent previewWidget;
protected PropertiesDialog propertiesDialog;
public void setWidget(JComponent component) {
this.widget = component;
}
public void setPreviewWidget(JComponent component) {
this.previewWidget = component;
}
public JComponent getWidget() {
return this.widget;
}
public boolean hasWidget() {
return this.widget != null;
}
public JComponent getPreviewWidget() {
return this.previewWidget;
}
public boolean hasPreviewWidget() {
return this.previewWidget != null;
}
public void addParamChangeListerToAllParameters(ParamChangeListener listener) {
for (Parameter parameter : getParameters()) {
parameter.addParamChangeListener(listener);
}
}
}