diff --git a/pom.xml b/pom.xml
index 7ccda65..933dab3 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
4.0.0
de.srsoftware
web4rail
- 1.3.6
+ 1.3.7
Web4Rail
jar
Java Model Railway Control
@@ -38,7 +38,7 @@
de.srsoftware
tools
- 1.1.9
+ 1.1.11
compile
diff --git a/resources/css/style.css b/resources/css/style.css
index 4388852..a1444d6 100644
--- a/resources/css/style.css
+++ b/resources/css/style.css
@@ -393,4 +393,12 @@ svg.Block text{
#aspect-form td{
text-align: center;
+}
+
+.directory{
+ color: blue;
+}
+
+.plan-file{
+ color: green;
}
\ No newline at end of file
diff --git a/resources/js/plan.js b/resources/js/plan.js
index cb99954..fb60c7f 100644
--- a/resources/js/plan.js
+++ b/resources/js/plan.js
@@ -157,9 +157,7 @@ function moveTile(x,y){
}
function place(data){
- var tag = $(data);
- $('#'+tag.attr('id')).remove();
- $('#scroll').append(tag);
+ $('#'+$(data).attr('id')).replaceWith(data);
return false;
}
@@ -227,8 +225,6 @@ function runAction(ev){
window.open("https://api.qrserver.com/v1/create-qr-code/?data="+window.location.href,'_blank');
} else if (clicked.id == 'fullscreen'){
toggleFullscreen();
- } else if (clicked.id == 'save'){
- alert('save');
} else return request({action:ev.target.id,realm:realm}); // TODO: ask for name
return false;
}
diff --git a/resources/translations/Application.de.translation b/resources/translations/Application.de.translation
index fdb91ab..48c572a 100644
--- a/resources/translations/Application.de.translation
+++ b/resources/translations/Application.de.translation
@@ -30,6 +30,7 @@ Analyze may overwrite these routes! : Durch die Analyse können diese Fahrstraß
Analyzing plan... : Plan wird analysiert...
and : und
AndCondition : Und-Bedingung
+Application will load "{}" on next launch and will now quit! : Anwendung wird beim nächsten Start „{}“ laden und wird jetzt beendet.
Apply : Übernehmen
Aspect : Signalbild
Aspects : Signalbilder
@@ -109,6 +110,7 @@ Edit json : JSON bearbeiten
Effect : Effekt
Emergency : Notfall
enable {} : {} aktivieren
+Enter new name for plan : Neuen Namen für den Plan eingeben
export : exportieren
Faster (10 {}) : 10 {} schneller
Final speed after breaking, before halting : Endgeschwindigkeit nach Bremsvorgang, vor dem Anhalten
@@ -179,6 +181,8 @@ On : An
One of : eine von
One way : Richtung
Online Documentation : Online-Dokumentation
+open other plan : anderen Plan öffnen
+Open plan... : Plan öffnen...
Operating System : Betriebssystem
or : oder
OrCondition : Oder-Bedingung
@@ -200,6 +204,8 @@ Relay : Relais
Relay/Signal/Turnout : Relais/Signal/Weiche
Remove tag "{}" from train : Markierung "{}" von Zug entfernen
Removed {} : {} gelöscht
+rename : umbenennen
+Rename plan : Plan umbenennen
Report Issue : Problem melden
reverse : wenden
Reversed {}. : {} umgedreht.
@@ -209,6 +215,7 @@ Route properties : Routen-Eigenschaften
Routes : Fahrstraßen
Routes using this tile : Fahrstraßen, die diesen Abschnitt verwenden
Route will only be available, if all conditions are fulfilled. : Route ist nur verfügbar, wenn alle Bedingungen erfüllt sind.
+Save "{}" : „{}“ speichern
Save : speichern
SavePlan : Plan speichern
Select block : Block auswählen
diff --git a/src/main/java/de/srsoftware/web4rail/Application.java b/src/main/java/de/srsoftware/web4rail/Application.java
index f3d92e3..21e3871 100644
--- a/src/main/java/de/srsoftware/web4rail/Application.java
+++ b/src/main/java/de/srsoftware/web4rail/Application.java
@@ -3,7 +3,6 @@ package de.srsoftware.web4rail;
import java.awt.Desktop;
import java.io.File;
import java.io.FileInputStream;
-import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
@@ -13,8 +12,8 @@ import java.net.InetSocketAddress;
import java.net.URI;
import java.net.URLDecoder;
import java.nio.file.Files;
-import java.nio.file.NoSuchFileException;
import java.util.HashMap;
+import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@@ -43,6 +42,8 @@ public class Application extends BaseClass{
private static final Logger LOG = LoggerFactory.getLogger(Application.class);
private static final String START_TRAINS = "--start-trains";
public static final ExecutorService threadPool = Executors.newCachedThreadPool();
+ private static final String FILENAME = "filename";
+ private static Configuration config;
/**
* entry point for the application:
@@ -57,10 +58,11 @@ public class Application extends BaseClass{
* @throws NoSuchMethodException
* @throws SecurityException
*/
- public static void main(String[] args) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
- Configuration config = new Configuration(Configuration.dir("Web4Rail")+"/app.config");
+ public static void main(String[] args) throws IOException {
+ config = new Configuration(Configuration.dir("Web4Rail")+"/app.config");
LOG.debug("config: {}",config);
InetSocketAddress addr = new InetSocketAddress(config.getOrAdd(PORT, 8080));
+ String planName = config.getOrAdd(Plan.NAME, Plan.DEFAULT_NAME);
HttpServer server = HttpServer.create(addr, 0);
server.createContext("/plan", client -> sendPlan(client));
server.createContext("/css" , client -> sendFile(client));
@@ -69,10 +71,17 @@ public class Application extends BaseClass{
server.setExecutor(threadPool);
server.start();
try {
- Plan.load(Plan.DEFAULT_NAME);
- } catch (FileNotFoundException|NoSuchFileException e) {
+ Plan.load(planName);
+ } catch (IOException e) {
plan = new Plan();
}
+ plan.setAppConfig(config);
+ try {
+ Desktop.getDesktop().browse(URI.create("http://"+InetAddress.getLocalHost().getHostName()+":"+config.getInt(PORT)+"/plan"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
for (String arg : args) {
switch (arg) {
case START_TRAINS:
@@ -80,9 +89,8 @@ public class Application extends BaseClass{
break;
}
}
- Desktop.getDesktop().browse(URI.create("http://"+InetAddress.getLocalHost().getHostName()+":"+config.getInt(PORT)+"/plan"));
}
-
+
/**
* handles request from clients by delegating them to respective classes
* @param params
@@ -99,10 +107,12 @@ public class Application extends BaseClass{
private static Object handle(HashMap params) throws IOException, ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
LOG.debug("Application.handle({})",params);
String realm = params.get(REALM);
- if (realm == null) throw new NullPointerException(REALM+" should not be null!");
+ if (isNull(realm)) throw new NullPointerException(REALM+" should not be null!");
String action = params.get(ACTION);
- if (action == null) throw new NullPointerException(ACTION+" should not be null!");
+ if (isNull(action)) throw new NullPointerException(ACTION+" should not be null!");
+
+ if (action.equals(ACTION_OPEN)) return open(params);
switch (realm) {
case REALM_ACTIONS:
@@ -168,6 +178,49 @@ public class Application extends BaseClass{
return Files.probeContentType(file.toPath());
}
+ private static Object open(HashMap params) {
+ Window win = new Window("open-plan", t("Open plan..."));
+ String filename = params.get(FILENAME);
+ if (isNull(filename)) {
+ filename = ".";
+ } else if (filename.startsWith("."+File.separator)) {
+ filename = filename.substring(2);
+ }
+ File file = new File(filename);
+ if (file.isDirectory()) {
+ Tag ul = null;
+ File[] children = file.listFiles();
+ for (File child : children) {
+ if (child.isDirectory()) {
+ if (isNull(ul)) ul = new Tag("ul").addTo(win);
+ Plan.link("li", child.getName(), Map.of(REALM,REALM_APP,ACTION,ACTION_OPEN,FILENAME,filename+File.separator+child.getName())).clazz("directory").addTo(ul);
+ }
+ }
+ for (File child : children) {
+ if (!child.isDirectory() && child.getName().endsWith(".plan")) {
+ if (isNull(ul)) ul = new Tag("ul").addTo(win);
+ Plan.link("li", child.getName(), Map.of(REALM,REALM_APP,ACTION,ACTION_OPEN,FILENAME,filename+File.separator+child.getName())).clazz("plan-file").addTo(ul);
+ }
+ }
+ } else {
+ if (file.getName().endsWith(".plan")) {
+ String name = file.getPath();
+ config.put(NAME,name.substring(0,name.length()-5));
+ try {
+ config.save();
+ plan.controlUnit().set(false);
+ plan.stream(t("Application will load \"{}\" on next launch and will now quit!",file));
+ plan.controlUnit().end();
+ System.exit(0);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ return win;
+ }
+
+
/**
* sends a response generated from the application to a given client
* @param client
diff --git a/src/main/java/de/srsoftware/web4rail/BaseClass.java b/src/main/java/de/srsoftware/web4rail/BaseClass.java
index f6d3286..ed62bce 100644
--- a/src/main/java/de/srsoftware/web4rail/BaseClass.java
+++ b/src/main/java/de/srsoftware/web4rail/BaseClass.java
@@ -497,6 +497,12 @@ public abstract class BaseClass implements Constants{
}
protected void removeChild(BaseClass child) {}
+
+ public static void resetRegistry() {
+ registry = new HashMap();
+ customFieldNames = new HashMap, Set>();
+ }
+
protected static String t(String txt, Object...fills) {
if (isSet(fills)) for (int i=0; i Tile.load(object, plan));
if (json.has(LENGTH_UNIT)) lengthUnit = json.getString(LENGTH_UNIT);
@@ -506,17 +515,17 @@ public class Plan extends BaseClass{
if (json.has(FREE_BEHIND_TRAIN)) Route.freeBehindTrain = json.getBoolean(FREE_BEHIND_TRAIN);
try {
- Train.loadAll(filename+".trains",plan);
+ Train.loadAll(name+".trains",plan);
} catch (Exception e) {
LOG.warn("Was not able to load trains!",e);
}
try {
- Route.loadAll(filename+".routes",plan);
+ Route.loadAll(name+".routes",plan);
} catch (Exception e) {
LOG.warn("Was not able to load routes!",e);
}
try {
- plan.controlUnit.load(filename+".cu");
+ plan.controlUnit.load(name+".cu");
} catch (Exception e) {
LOG.warn("Was not able to load control unit settings!",e);
}
@@ -642,11 +651,13 @@ public class Plan extends BaseClass{
private Tag planMenu() throws IOException {
Tag actionMenu = new Tag("div").clazz("actions").content(t("Plan"));
Tag actions = new Tag("div").clazz("list").content("");
- new Div(ACTION_SAVE).clazz(REALM_PLAN).content(t("Save")).addTo(actions);
+ saveButton().addTo(actions);
new Div(ACTION_ANALYZE).clazz(REALM_PLAN).content(t("Analyze")).addTo(actions);
new Div(ACTION_QR).clazz(REALM_PLAN).content(t("QR-Code")).addTo(actions);
new Div(FULLSCREEN).clazz(REALM_PLAN).content(t("Fullscreen")).addTo(actions);
new Div(ACTION_PROPS).clazz(REALM_PLAN).content(t("Properties")).addTo(actions);
+ new Div(RENAME).clazz(REALM_PLAN).content(t("rename")).addTo(actions);
+ new Div(ACTION_OPEN).clazz(REALM_APP).content(t("open other plan")).addTo(actions);
return actions.addTo(actionMenu);
}
@@ -727,8 +738,7 @@ public class Plan extends BaseClass{
if (device.address() % 4 == 1) table.children().lastElement().clazz("group");
}
- table.clazz("turnouts").addTo(fieldset);
- return fieldset;
+ return table.clazz("turnouts").addTo(fieldset);
}
@Override
@@ -736,6 +746,35 @@ public class Plan extends BaseClass{
if (child instanceof Tile) drop((Tile) child);
super.removeChild(child);
}
+
+ private Object rename(HashMap params) {
+ String newName = params.get(NAME);
+ Window win = new Window("rename-plan", t("Rename plan"));
+ if (isSet(newName)) {
+ newName = newName.trim();
+ if (!newName.isEmpty() && !newName.equals(name)) {
+ String old = name;
+ name = newName;
+ try {
+ String saved = save();
+ appConfig.put(NAME, name);
+ appConfig.save();
+ stream("place "+saveButton());
+ return saved;
+ } catch (IOException e) {
+ new Tag("div").content(t("Was not able to save plan as \"{}\".",newName));
+ name = old;
+ }
+ }
+ }
+ Form form = new Form("rename-form");
+ new Input(REALM, REALM_PLAN).hideIn(form);
+ new Input(ACTION, RENAME).hideIn(form);
+ new Input(NAME,name).addTo(new Label(t("Enter new name for plan")+":"+NBSP)).addTo(form);
+ new Button(t("Save"), form).addTo(form);
+ return form.addTo(win);
+ }
+
private Tag routeProperties() {
Fieldset fieldset = new Fieldset(t("Routes"));
@@ -754,12 +793,7 @@ public class Plan extends BaseClass{
actions);
if (route.isDisabled()) row.clazz("disabled");
}
- table.clazz("turnouts").addTo(fieldset);
- return fieldset;
- }
-
- public void save() throws IOException {
- plan.stream(plan.saveTo("default"));
+ return table.clazz("turnouts").addTo(fieldset);
}
/**
@@ -768,8 +802,8 @@ public class Plan extends BaseClass{
* @return
* @throws IOException
*/
- private String saveTo(String name) throws IOException {
- if (name == null || name.isEmpty()) throw new NullPointerException("Name must not be empty!");
+ public String save() throws IOException {
+ if (isNull(name) || name.isEmpty()) throw new NullPointerException("Name must not be empty!");
Car.saveAll(name+".cars");
Tile.saveAll(name+".plan");
@@ -783,6 +817,10 @@ public class Plan extends BaseClass{
return t("Plan saved as \"{}\".",name);
}
+
+ private Tag saveButton() {
+ return new Div(ACTION_SAVE).clazz(REALM_PLAN).content(t("Save \"{}\"",name));
+ }
public void sensor(int addr, boolean active) {
Contact contact = Contact.get(addr);
@@ -800,6 +838,11 @@ public class Plan extends BaseClass{
if (isSet(contact)) contact.activate(active);
}
+ public void setAppConfig(Configuration config) {
+ appConfig = config;
+ }
+
+
private Object simplifyRouteName(HashMap params) {
String routeId = params.get(ROUTE);
if (isSet(routeId)) {
@@ -926,8 +969,7 @@ public class Plan extends BaseClass{
if (params.containsKey(FINAL_SPEED)) Route.endSpeed = Integer.parseInt(params.get(FINAL_SPEED));
Route.freeBehindTrain = "on".equalsIgnoreCase(params.get(FREE_BEHIND_TRAIN));
- return t("Plan updated.");
-
+ return t("Plan updated.");
}
private Object updateTimes(HashMap params) throws IOException {
diff --git a/src/main/java/de/srsoftware/web4rail/actions/SavePlan.java b/src/main/java/de/srsoftware/web4rail/actions/SavePlan.java
index c865ce0..ad6b5aa 100644
--- a/src/main/java/de/srsoftware/web4rail/actions/SavePlan.java
+++ b/src/main/java/de/srsoftware/web4rail/actions/SavePlan.java
@@ -13,7 +13,7 @@ public class SavePlan extends Action{
@Override
public boolean fire(Context context) {
try {
- plan.save();
+ plan.stream(plan.save());
} catch (IOException e) {
return false;
}
diff --git a/src/main/java/de/srsoftware/web4rail/moving/Car.java b/src/main/java/de/srsoftware/web4rail/moving/Car.java
index 8034022..1e5a776 100644
--- a/src/main/java/de/srsoftware/web4rail/moving/Car.java
+++ b/src/main/java/de/srsoftware/web4rail/moving/Car.java
@@ -35,7 +35,6 @@ import de.srsoftware.web4rail.tags.Table;
public class Car extends BaseClass implements Comparable{
protected static final Logger LOG = LoggerFactory.getLogger(Car.class);
- public static final String NAME = "name";
public static boolean FORWARD = true;
public static boolean REVERSE = false;
private static final String LENGTH = "length";