/*
* This file is part of NodeBox.
*
* Copyright (C) 2008 Frederik De Bleser (frederik@pandora.be)
*
* NodeBox 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 3 of the License, or
* (at your option) any later version.
*
* NodeBox 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 NodeBox. If not, see .
*/
package nodebox.node;
import nodebox.graphics.Color;
import nodebox.util.StringUtils;
import java.util.*;
import java.util.regex.Pattern;
/**
* A parameter controls the operation of a Node. It provide an interface into the workings of a node and allows a user
* to change its behaviour. Parameters are represented by standard user interface controls, such as sliders for numbers,
* text fields for strings, and checkboxes for booleans.
*
* Parameters implement the observer pattern for expressions. Parameters that are dependent on other parameters because
* of their expressions will observe the parameters they depend on, and marked the node as dirty whenever they receive
* an update event from one of the parameters they depend on.
*/
public class Parameter {
/**
* The primitive type of a parameter. This is different from the control UI that is used to represent this parameter.
*/
public enum Type {
/**
* An integer value
*/
INT,
/**
* A floating-point value
*/
FLOAT,
/**
* A string value
*/
STRING,
/**
* A color
*/
COLOR,
/**
* Executable code
*/
CODE
}
/**
* The UI control for this parameter. This defines how the parameter is represented in the user interface.
*/
public enum Widget {
ANGLE, COLOR, DATA, FILE, FLOAT, FONT, GRADIENT, IMAGE, INT, MENU, SEED, STRING, TEXT, TOGGLE, NODEREF, STAMP_EXPRESSION, CODE
}
/**
* The way in which values will be bound to a minimum and maximum value. Only hard bounding enforces the
* minimum and maximum value.
*/
public enum BoundingMethod {
NONE, SOFT, HARD
}
/**
* The steps where this parameter will be shown. If it is hidden, it will not be shown anywhere.
* If it is in the detail view, it will not show up in the HUD. The HUD is the highest level; this
* means that the control will be shown everywhere.
*/
public enum DisplayLevel {
HIDDEN, DETAIL, HUD
}
public static class MenuItem {
private String key;
private String label;
public MenuItem(String key, String label) {
this.key = key;
this.label = label;
}
public String getKey() {
return key;
}
public String getLabel() {
return label;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MenuItem menuItem = (MenuItem) o;
if (!key.equals(menuItem.key)) return false;
if (!label.equals(menuItem.label)) return false;
return true;
}
@Override
public int hashCode() {
int result = key.hashCode();
result = 31 * result + label.hashCode();
return result;
}
}
public static final HashMap TYPE_MAPPING;
public static final HashMap WIDGET_MAPPING;
public static final HashMap REVERSE_WIDGET_MAPPING;
public static final NodeCode emptyCode = new EmptyCode();
private static final Pattern TIME_DEPENDENT_KEYWORDS = Pattern.compile("FRAME|wave|hold|schedule|timeloop");
private static final Pattern CANVAS_DEPENDENT_KEYWORDS = Pattern.compile("TOP|LEFT|BOTTOM|RIGHT|WIDTH|HEIGHT");
static {
TYPE_MAPPING = new HashMap();
TYPE_MAPPING.put(Type.INT, Integer.class);
TYPE_MAPPING.put(Type.FLOAT, Float.class);
TYPE_MAPPING.put(Type.STRING, String.class);
TYPE_MAPPING.put(Type.COLOR, Color.class);
TYPE_MAPPING.put(Type.CODE, NodeCode.class);
WIDGET_MAPPING = new HashMap();
WIDGET_MAPPING.put(Type.INT, Widget.INT);
WIDGET_MAPPING.put(Type.FLOAT, Widget.FLOAT);
WIDGET_MAPPING.put(Type.STRING, Widget.STRING);
WIDGET_MAPPING.put(Type.COLOR, Widget.COLOR);
WIDGET_MAPPING.put(Type.CODE, Widget.CODE);
REVERSE_WIDGET_MAPPING = new HashMap();
REVERSE_WIDGET_MAPPING.put(Widget.ANGLE, Type.FLOAT);
REVERSE_WIDGET_MAPPING.put(Widget.COLOR, Type.COLOR);
REVERSE_WIDGET_MAPPING.put(Widget.DATA, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.FILE, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.FLOAT, Type.FLOAT);
REVERSE_WIDGET_MAPPING.put(Widget.FONT, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.GRADIENT, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.IMAGE, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.INT, Type.INT);
REVERSE_WIDGET_MAPPING.put(Widget.MENU, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.SEED, Type.INT);
REVERSE_WIDGET_MAPPING.put(Widget.STRING, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.TEXT, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.TOGGLE, Type.INT);
REVERSE_WIDGET_MAPPING.put(Widget.NODEREF, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.STAMP_EXPRESSION, Type.STRING);
REVERSE_WIDGET_MAPPING.put(Widget.CODE, Type.CODE);
}
private Node node;
private String name;
private String label;
private String helpText;
private Type type;
private Widget widget;
private Object value;
private Expression expression;
private BoundingMethod boundingMethod = BoundingMethod.NONE;
private Float minimumValue, maximumValue; // Objects, because they can be null.
private DisplayLevel displayLevel = DisplayLevel.HUD;
private Expression enableExpression;
private List