You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
639 lines
19 KiB
639 lines
19 KiB
package de.srsoftware.web4rail.tiles; |
|
|
|
import java.io.IOException; |
|
import java.util.Collection; |
|
import java.util.HashMap; |
|
import java.util.HashSet; |
|
import java.util.Iterator; |
|
import java.util.LinkedList; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Map.Entry; |
|
import java.util.Vector; |
|
import java.util.stream.Collectors; |
|
|
|
import org.json.JSONArray; |
|
import org.json.JSONException; |
|
import org.json.JSONObject; |
|
import org.slf4j.Logger; |
|
import org.slf4j.LoggerFactory; |
|
|
|
import de.srsoftware.tools.Tag; |
|
import de.srsoftware.web4rail.BaseClass; |
|
import de.srsoftware.web4rail.Connector; |
|
import de.srsoftware.web4rail.LoadCallback; |
|
import de.srsoftware.web4rail.Params; |
|
import de.srsoftware.web4rail.Plan.Direction; |
|
import de.srsoftware.web4rail.Range; |
|
import de.srsoftware.web4rail.Route; |
|
import de.srsoftware.web4rail.moving.Train; |
|
import de.srsoftware.web4rail.tags.Button; |
|
import de.srsoftware.web4rail.tags.Checkbox; |
|
import de.srsoftware.web4rail.tags.Fieldset; |
|
import de.srsoftware.web4rail.tags.Form; |
|
import de.srsoftware.web4rail.tags.Input; |
|
import de.srsoftware.web4rail.tags.Window; |
|
|
|
/** |
|
* Base class for all kinds of Blocks |
|
* @author Stephan Richter, SRSoftware |
|
* |
|
*/ |
|
public abstract class Block extends StretchableTile{ |
|
protected static Logger LOG = LoggerFactory.getLogger(Block.class); |
|
|
|
private class TrainList implements Iterable<Train>{ |
|
|
|
private LinkedList<Train> trains = new LinkedList<Train>(); |
|
|
|
public void add(Train train) { |
|
trains.remove(train); |
|
trains.addFirst(train); |
|
} |
|
|
|
public boolean contains(Train t) { |
|
return trains.contains(t); |
|
} |
|
|
|
public Train first() { |
|
return trains.isEmpty() ? null : trains.getFirst(); |
|
} |
|
|
|
public boolean isEmpty() { |
|
return trains.isEmpty(); |
|
} |
|
|
|
@Override |
|
public Iterator<Train> iterator() { |
|
return trains.iterator(); |
|
} |
|
|
|
public Train last() { |
|
return trains.isEmpty() ? null : trains.getLast(); |
|
} |
|
|
|
public Vector<Train> list() { |
|
return new Vector<>(trains); |
|
} |
|
|
|
public boolean remove(BaseClass b) { |
|
return trains.remove(b); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return trains.stream().map(t -> "["+t+"]→"+t.direction()).reduce((s1,s2) -> s1+", "+s2).orElse(t("empty")); |
|
} |
|
} |
|
private static final String ALLOW_TURN = "allowTurn"; |
|
private static final String NAME = "name"; |
|
private static final String NO_TAG = "[default]"; |
|
private static final String NEW_TAG = "new_tag"; |
|
public static final String TAG = "tag"; |
|
private static final String WAIT_TIMES = "wait_times"; |
|
private static final String RAISE = "raise"; |
|
public static final String ACTION_ADD_CONTACT = "add_contact"; |
|
private static final String PARKED_TRAINS = "parked_trains"; |
|
|
|
public String name = "Block"; |
|
public boolean turnAllowed = false; |
|
private Vector<BlockContact> internalContacts = new Vector<BlockContact>(); |
|
private TrainList parkedTrains = new TrainList(); |
|
|
|
public Block() { |
|
super(); |
|
WaitTime defaultWT = new WaitTime(NO_TAG); |
|
defaultWT.setMin(directionA(), 0); |
|
defaultWT.setMax(directionA(), 10000); |
|
defaultWT.setMin(directionB(), 0); |
|
defaultWT.setMax(directionB(), 10000); |
|
waitTimes.add(defaultWT); |
|
WaitTime learningWt = new WaitTime(t("learn")); |
|
learningWt.setMin(directionA(), 1000); |
|
learningWt.setMax(directionA(), 1000); |
|
learningWt.setMin(directionB(), 1000); |
|
learningWt.setMax(directionB(), 1000); |
|
waitTimes.add(learningWt); |
|
} |
|
|
|
/** |
|
* aggregates all (directional) wait times for one tag |
|
*/ |
|
public class WaitTime{ |
|
public String tag = ""; |
|
private HashMap<Direction,Range> dirs = new HashMap<Direction, Range>(); |
|
|
|
public WaitTime(String tag) { |
|
this.tag = tag; |
|
} |
|
|
|
public Range get(Direction dir) { |
|
Range range = dirs.get(dir); |
|
if (range == null) { |
|
range = new Range(); |
|
dirs.put(dir, range); |
|
} |
|
return range; |
|
} |
|
|
|
public JSONObject json() { |
|
JSONObject json = new JSONObject(); |
|
json.put(TAG, tag); |
|
for (Entry<Direction, Range> entry : dirs.entrySet()) json.put(entry.getKey().toString(), entry.getValue().json()); |
|
return json; |
|
} |
|
|
|
public WaitTime load(JSONObject json) { |
|
for (String key : json.keySet()) { |
|
if (key.equals(TAG)) { |
|
tag = json.getString(key); |
|
} else { |
|
Direction dir = Direction.valueOf(key); |
|
Range range = new Range().load(json.getJSONObject(key)); |
|
dirs.put(dir, range); |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
public WaitTime setMax(Direction dir,int max) { |
|
get(dir).max = max; |
|
return this; |
|
} |
|
|
|
public WaitTime setMin(Direction dir,int min) { |
|
get(dir).min = min; |
|
return this; |
|
} |
|
|
|
public WaitTime setTag(String newTag){ |
|
tag = newTag; |
|
return this; |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return "WaitTime("+tag+", "+dirs+")"; |
|
} |
|
|
|
public void validate() { |
|
for (Entry<Direction, Range> entry: dirs.entrySet()) entry.getValue().validate(); |
|
} |
|
} |
|
|
|
|
|
private Vector<WaitTime> waitTimes = new Vector<WaitTime>(); |
|
|
|
public void addParkedTrain(Train train) { |
|
if (parkedTrains.contains(train)) return; |
|
if (isNull(train)) return; |
|
train.register(); |
|
parkedTrains.add(train); |
|
} |
|
|
|
|
|
public Object addContact() { |
|
BlockContact contact = new BlockContact(this); |
|
plan.learn(contact); |
|
return t("Trigger contact to learn new contact"); |
|
} |
|
|
|
@Override |
|
protected HashSet<String> classes() { |
|
HashSet<String> classes = super.classes(); |
|
if (!parkedTrains.isEmpty()) classes.add(OCCUPIED); |
|
return classes; |
|
} |
|
|
|
@Override |
|
public Object click(boolean shift) throws IOException { |
|
Train train = occupyingTrain(); |
|
return !shift && isSet(train) ? train.properties() : properties(); |
|
} |
|
|
|
public int compareTo(Block other) { |
|
return name.compareTo(other.name); |
|
} |
|
|
|
@Override |
|
public JSONObject config() { |
|
JSONObject config = super.config(); |
|
config.put(NAME, name); |
|
return config; |
|
} |
|
|
|
private Fieldset contactForm() { |
|
Fieldset fieldset = new Fieldset(t("internal contacts")).id("props-contacts"); |
|
this.button(t("new contact"), Map.of(ACTION,ACTION_ADD_CONTACT)).addTo(fieldset); |
|
if (!internalContacts.isEmpty()) { |
|
Tag ul = new Tag("ul"); |
|
for (BlockContact contact : internalContacts) { |
|
Tag li = contact.link("span", contact, contact.id().toString()).content(NBSP).addTo(new Tag("li")); |
|
contact.button(t("learn"),Map.of(ACTION,ACTION_ANALYZE)).addTo(li); |
|
contact.button(t("delete"),Map.of(ACTION,ACTION_DROP)).addTo(li); |
|
li.addTo(ul); |
|
|
|
} |
|
ul.addTo(fieldset); |
|
} |
|
return fieldset; |
|
} |
|
|
|
public Collection<? extends Contact> contacts() { |
|
return internalContacts; |
|
} |
|
|
|
public abstract Direction directionA(); |
|
public abstract Direction directionB(); |
|
|
|
private Tile drop(String tag) { |
|
for (int i=0; i<waitTimes.size(); i++) { |
|
if (waitTimes.get(i).tag.equals(tag)) { |
|
waitTimes.remove(i); |
|
break; |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
public void dropTrain(Train t) { |
|
parkedTrains.remove(t); |
|
plan.place(this); |
|
} |
|
|
|
@Override |
|
public boolean free(Train oldTrain) { |
|
parkedTrains.remove(oldTrain); |
|
if (!super.free(oldTrain)) return false; |
|
return true; |
|
} |
|
|
|
private WaitTime getWaitTime(String tag) { |
|
if (tag == null) return null; |
|
for (WaitTime wt : waitTimes) { |
|
if (wt.tag.equals(tag)) return wt; |
|
} |
|
return null; |
|
} |
|
|
|
public Range getWaitTime(Train train,Direction dir) { |
|
LOG.debug("{}.getWaitTime({},{})",this,train,dir); |
|
for (WaitTime wt : waitTimes) { |
|
LOG.debug("examinig {}",wt); |
|
if (train.tags().contains(wt.tag)) { |
|
LOG.info(t("{} @ {} using rule for \"{}\".",train,this,wt.tag)); |
|
return wt.get(dir); |
|
} |
|
} |
|
LOG.info(t("{} @ {} using rule for \"[default]\".",train,this)); |
|
return getWaitTime(NO_TAG).get(dir); |
|
} |
|
|
|
public int indexOf(BlockContact contact) { |
|
return 1+internalContacts.indexOf(contact); |
|
} |
|
|
|
@Override |
|
public boolean isFreeFor(Context context) { |
|
if (!super.isFreeFor(context)) return false; |
|
Train train = context.train(); |
|
if (isSet(occupyingTrain()) && occupyingTrain() == train) return true; |
|
return parkedTrains.isEmpty() || parkedTrains.contains(train) || train.isShunting(); |
|
} |
|
|
|
@Override |
|
public JSONObject json() { |
|
JSONObject json = super.json(); |
|
json.put(NAME, name); |
|
json.put(ALLOW_TURN, turnAllowed); |
|
JSONArray jWaitTimes = new JSONArray(); |
|
waitTimes.forEach(wt -> jWaitTimes.put(wt.json())); |
|
json.put(WAIT_TIMES, jWaitTimes); |
|
JSONObject jContacts = null; |
|
for (BlockContact contact : internalContacts) { |
|
if (contact.addr() != 0) { |
|
if (isNull(jContacts)) jContacts = new JSONObject(); |
|
jContacts.put(contact.id().toString(), contact.json()); |
|
} |
|
} |
|
if (isSet(jContacts)) json.put(CONTACT, jContacts); |
|
json.remove(REALM_TRAIN); // is set by TRAINS field for blocks |
|
if (!parkedTrains.isEmpty()) { |
|
JSONArray jTrains = new JSONArray(); |
|
for (Train train : parkedTrains) { |
|
JSONObject to = new JSONObject(); |
|
to.put(ID, train.id()); |
|
// Direction dir = parkedTrains.directionOf(train); |
|
// if (isSet(dir)) to.put(DIRECTION, dir.toString()); |
|
jTrains.put(to); |
|
} |
|
json.put(PARKED_TRAINS, jTrains); |
|
} |
|
return json; |
|
} |
|
|
|
public Train lastTrain() { |
|
return parkedTrains.last(); |
|
} |
|
|
|
public List<Route> leavingRoutes() { |
|
return routes().stream().filter(route -> route.startBlock() == Block.this).collect(Collectors.toList()); |
|
} |
|
|
|
|
|
/** |
|
* If arguments are given, the first is taken as content, the second as tag type. |
|
* If no content is supplied, name is set as content. |
|
* If no type is supplied, "span" is preset. |
|
* @param args |
|
* @return |
|
*/ |
|
public Tag link(String...args) { |
|
String tx = args.length<1 ? name+NBSP : args[0]; |
|
String type = args.length<2 ? "span" : args[1]; |
|
return link(type, tx).clazz("link","block"); |
|
} |
|
|
|
@Override |
|
public Tile load(JSONObject json) { |
|
name = json.has(NAME) ? json.getString(NAME) : "Block"; |
|
turnAllowed = json.has(ALLOW_TURN) && json.getBoolean(ALLOW_TURN); |
|
if (json.has(WAIT_TIMES)) { |
|
waitTimes.clear(); |
|
JSONArray wtArr = json.getJSONArray(WAIT_TIMES); |
|
wtArr.forEach(object -> { |
|
if (object instanceof JSONObject) waitTimes.add(new WaitTime(null).load((JSONObject) object)); |
|
}); |
|
} |
|
if (json.has(CONTACT)) { |
|
JSONObject jContact = json.getJSONObject(CONTACT); |
|
for (String key : jContact.keySet()) { |
|
try { |
|
new BlockContact(this).load(jContact.getJSONObject(key)); |
|
} catch (JSONException e) {} |
|
} |
|
} |
|
|
|
new LoadCallback() { |
|
@Override |
|
public void afterLoad() { |
|
if (json.has(PARKED_TRAINS)) { |
|
JSONArray jParkedTrains = json.getJSONArray(PARKED_TRAINS); |
|
for (Object o : jParkedTrains) { |
|
if (o instanceof JSONObject) { |
|
JSONObject trainData = (JSONObject) o; |
|
Train train = BaseClass.get(Id.from(trainData)); |
|
if (isSet(train)) parkedTrains.add(train.set(Block.this)); |
|
} |
|
} |
|
} |
|
} |
|
}; |
|
|
|
return super.load(json); |
|
} |
|
|
|
@Override |
|
public boolean lockFor(Context context, boolean downgrade) { |
|
Train newTrain = context.train(); |
|
LOG.debug("{}.lockFor({})",this,newTrain); |
|
Train train = lockingTrain(); |
|
if (newTrain == train || parkedTrains.isEmpty() || parkedTrains.contains(newTrain) || newTrain.isShunting()) return super.lockFor(context, downgrade); |
|
return false; |
|
} |
|
|
|
@Override |
|
protected Window properties(List<Fieldset> preForm, FormInput formInputs, List<Fieldset> postForm,String...errors) { |
|
formInputs.add(t("Name"),new Input(NAME, name)); |
|
formInputs.add("",new Checkbox(ALLOW_TURN,t("Turn allowed"),turnAllowed)); |
|
Train train = occupyingTrain(); |
|
if (isNull(train)) train = parkedTrains.first(); |
|
formInputs.add(t("Train"),Train.selector(train, null)); |
|
postForm.add(contactForm()); |
|
postForm.add(waitTimeForm()); |
|
if (!parkedTrains.isEmpty()) postForm.add(trainList()); |
|
return super.properties(preForm, formInputs, postForm,errors); |
|
} |
|
|
|
public Tile raise(String tag) { |
|
for (int i=1; i<waitTimes.size(); i++) { |
|
WaitTime wt = waitTimes.get(i); |
|
if (wt.tag.equals(tag)) { |
|
waitTimes.remove(i); |
|
waitTimes.insertElementAt(wt, i-1); |
|
break; |
|
} |
|
} |
|
return this; |
|
} |
|
|
|
@Override |
|
public boolean reserveFor(Context context) { |
|
Train newTrain = context.train(); |
|
LOG.debug("{}.lockFor({})",this,newTrain); |
|
Train train = lockingTrain(); |
|
if (newTrain == train || parkedTrains.isEmpty() || parkedTrains.contains(newTrain) || newTrain.isShunting()) return super.reserveFor(context); |
|
return false; |
|
} |
|
|
|
public BlockContact register(BlockContact contact) { |
|
internalContacts.add(contact); |
|
return contact; |
|
} |
|
|
|
@Override |
|
public void removeChild(BaseClass child) { |
|
super.removeChild(child); |
|
internalContacts.remove(child); |
|
if (parkedTrains.remove(child)) plan.place(this); |
|
} |
|
|
|
public void removeContact(BlockContact blockContact) { |
|
internalContacts.remove(blockContact); |
|
} |
|
|
|
@Override |
|
public boolean setTrain(Train newTrain) { |
|
if (isDisabled()) return false; |
|
if (isNull(newTrain)) return false; |
|
Train occupyingTrain = occupyingTrain(); |
|
if (isSet(occupyingTrain) && newTrain != occupyingTrain && newTrain.isShunting()) parkedTrains.add(occupyingTrain.dropTrace(false)); |
|
return super.setTrain(newTrain); |
|
} |
|
|
|
public abstract List<Connector> startPoints(); |
|
|
|
@Override |
|
public Tag tag(Map<String, Object> replacements) throws IOException { |
|
if (isNull(replacements)) replacements = new HashMap<String, Object>(); |
|
replacements.put("%text%",name); |
|
Vector<String> trainNames = new Vector<String>(); |
|
Train lockingTrain = occupyingTrain(); |
|
if (isSet(lockingTrain)) trainNames.add(lockingTrain.directedName()); |
|
for (Train train: parkedTrains) trainNames.add(train.directedName()); |
|
if (!trainNames.isEmpty())replacements.put("%text%",String.join(" | ", trainNames)); |
|
Tag tag = super.tag(replacements); |
|
tag.clazz(tag.get("class")+" Block"); |
|
return tag; |
|
} |
|
|
|
@Override |
|
public String title() { |
|
StringBuilder sb = new StringBuilder(name); |
|
Train occupyingTrain = occupyingTrain(); |
|
if (isSet(occupyingTrain)) sb.append(title(occupyingTrain)); |
|
if (isSet(parkedTrains)) for (Train parked : parkedTrains.trains) sb.append(title(parked)); |
|
return sb.toString(); |
|
} |
|
|
|
@Override |
|
public String toString() { |
|
return name + " @ ("+x+","+y+")"; |
|
} |
|
|
|
private Fieldset trainList() { |
|
Fieldset fieldset = new Fieldset(t("Trains")); |
|
Tag list = new Tag("ul"); |
|
for (Train t : parkedTrains) { |
|
if (isSet(t)) t.link("li", t, null).addTo(list); |
|
} |
|
list.addTo(fieldset); |
|
return fieldset; |
|
} |
|
|
|
public Vector<Train> trains(){ |
|
return parkedTrains.list(); |
|
} |
|
|
|
@Override |
|
public Tile update(Params params) { |
|
if (params.containsKey(NAME)) name=params.getString(NAME); |
|
if (params.containsKey(Train.class.getSimpleName())) { |
|
Id trainId = Id.from(params,Train.class.getSimpleName()); |
|
if (trainId.equals(0)) { // remove first train |
|
free(occupyingTrain()); |
|
} else { |
|
Train newTrain = Train.get(trainId); |
|
if (isSet(newTrain) && newTrain != occupyingTrain()) { |
|
free(occupyingTrain()); |
|
newTrain.dropTrace(true); |
|
if (connections(newTrain.direction()).isEmpty()) newTrain.heading(null); |
|
newTrain.set(this); |
|
} |
|
if (newTrain.currentBlock() != this) { |
|
newTrain.dropTrace(true); |
|
newTrain.set(this); |
|
} |
|
} |
|
} |
|
turnAllowed = params.containsKey(ALLOW_TURN) && "on".equals(params.get(ALLOW_TURN)); |
|
|
|
return super.update(params); |
|
} |
|
|
|
public Tile updateTimes(Params params) throws IOException { |
|
String tag = params.getString(ACTION_DROP); |
|
if (isSet(tag)) return drop(tag); |
|
tag = params.getString(RAISE); |
|
if (isSet(tag)) return raise(tag); |
|
String newTag = params.getString(NEW_TAG); |
|
if (isSet(newTag)) { |
|
newTag = newTag.replace(" ", "_").trim(); |
|
if (newTag.isEmpty()) newTag = null; |
|
} |
|
|
|
for (Entry<String, Object> entry:params.entrySet()) { |
|
String key = entry.getKey(); |
|
Object val = entry.getValue(); |
|
|
|
if (key.startsWith("max.") || key.startsWith("min.")) { |
|
String[] parts = key.split("\\."); |
|
boolean isMin = parts[0].equals("min"); |
|
tag = parts[1].equals("new_tag") ? newTag : parts[1]; |
|
if (isNull(tag)) continue; |
|
Direction dir = Direction.valueOf(parts[2]); |
|
|
|
WaitTime wt = getWaitTime(tag); |
|
if (wt == null) { |
|
wt = new WaitTime(tag); |
|
waitTimes.add(wt); |
|
} |
|
if (isMin) { |
|
wt.setMin(dir, Integer.parseInt(val.toString())); |
|
} else wt.setMax(dir, Integer.parseInt(val.toString())); |
|
} |
|
} |
|
for (WaitTime wt: waitTimes) wt.validate(); |
|
|
|
return this; |
|
} |
|
|
|
public Fieldset waitTimeForm() { |
|
Fieldset win = new Fieldset(t("Wait times")).id("props-times"); |
|
Form form = new Form("train-wait-form"); |
|
new Tag("h4").content(t("Stop settings")).addTo(win); |
|
new Input(REALM,REALM_PLAN).hideIn(form); |
|
new Input(ID,id()).hideIn(form); |
|
new Input(ACTION,ACTION_TIMES).hideIn(form); |
|
|
|
new Tag("div").content(t("Minimum and maximum times (in Miliseconds) trains with the respective tag have to wait in this block.")).addTo(form); |
|
|
|
Direction dA = directionA(); |
|
Direction dB = directionB(); |
|
|
|
Tag table = new Tag("table"); |
|
Tag row = new Tag("tr"); |
|
new Tag("td").content(t("Direction")).addTo(row); |
|
new Tag("th").content(t("{}bound",dA)).attr("colspan", 2).addTo(row); |
|
new Tag("th").content(t("{}bound",dB)).attr("colspan", 2).addTo(row); |
|
new Tag("td").content("").addTo(row).addTo(table); |
|
|
|
row = new Tag("tr"); |
|
new Tag("th").content(t("Tag")).addTo(row); |
|
new Tag("th").content(t("min")).addTo(row); |
|
new Tag("th").content(t("max")).addTo(row); |
|
new Tag("th").content(t("min")).addTo(row); |
|
new Tag("th").content(t("max")).addTo(row); |
|
new Tag("th").content(t("Actions")).addTo(row).addTo(table); |
|
|
|
int count = 0; |
|
for (WaitTime wt : waitTimes) { |
|
count++; |
|
row = new Tag("tr"); |
|
new Tag("td").content(wt.tag).addTo(row); |
|
new Input("min."+wt.tag+"."+dA,wt.get(dA).min).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("max."+wt.tag+"."+dA,wt.get(dA).max).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("min."+wt.tag+"."+dB,wt.get(dB).min).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("max."+wt.tag+"."+dB,wt.get(dB).max).numeric().addTo(new Tag("td")).addTo(row); |
|
Tag actions = new Tag("td"); |
|
Map<String, String> props = Map.of(REALM,REALM_PLAN,ID,id().toString(),ACTION,ACTION_TIMES); |
|
switch (count) { |
|
case 1: |
|
actions.content(""); break; |
|
case 2: |
|
new Button("-",merged(props,Map.of(ACTION_DROP,wt.tag))).addTo(actions); |
|
break; |
|
default: |
|
new Button("↑",merged(props,Map.of(RAISE,wt.tag))).addTo(actions); |
|
new Button("-",merged(props,Map.of(ACTION_DROP,wt.tag))).addTo(actions); |
|
} |
|
actions.addTo(row).addTo(table); |
|
|
|
} |
|
|
|
WaitTime defaultWT = getWaitTime(NO_TAG); |
|
|
|
row = new Tag("tr"); |
|
new Input(NEW_TAG,"").attr("placeholder", t("new tag")).addTo(new Tag("td")).addTo(row); |
|
new Input("min."+NEW_TAG+"."+dA,defaultWT.get(dA).min).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("max."+NEW_TAG+"."+dA,defaultWT.get(dA).max).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("min."+NEW_TAG+"."+dB,defaultWT.get(dB).min).numeric().addTo(new Tag("td")).addTo(row); |
|
new Input("max."+NEW_TAG+"."+dB,defaultWT.get(dB).max).numeric().addTo(new Tag("td")).addTo(row).addTo(table); |
|
|
|
table.addTo(form); |
|
|
|
new Button(t("Apply"),form).addTo(form).addTo(win); |
|
|
|
return win; |
|
} |
|
}
|
|
|