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";