package nbtool.util; import java.awt.Dimension; import java.awt.GraphicsDevice; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.io.IOException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.HashMap; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; import nbtool.data.OrderedSet; import nbtool.data.ViewProfile; import nbtool.data.json.Json; import nbtool.data.json.Json.JsonValue; import nbtool.data.json.JsonArray; import nbtool.data.json.JsonObject; import nbtool.data.json.JsonParser.JsonParseException; import nbtool.data.json.JsonString; import nbtool.util.Debug.LogLevel; public class UserSettings { public static final Map<String, DisplaySettings> BOUNDS_MAP = new HashMap<>(); public static final Map<String, JsonValue> PREFERENCES = new HashMap<>(); public static int leftSplitLoc = -1; //saved user inputs.. public static OrderedSet<Path> loadPathes = new OrderedSet<>(); /* For list with robot names with and without .local public static OrderedSet<String> addresses = new OrderedSet<> (Robots.sortedHostNames()); */ //List of robot names without .local public static OrderedSet<String> addresses = new OrderedSet<> (Robots.sortedWifiNames()); public static LogLevel logLevel = LogLevel.levelINFO; // public static String venue = "edwards16"; public static String venue = "leipzig16"; private static final String INTERNAL_KEY = "__INTERNAL__"; private static final String BOUNDS_KEY = "__BOUNDS__"; private static final String ADDR_KEY = "__ADDRESSES__"; private static final String LOAD_KEY = "__LOADPATHS__"; private static final String LL_KEY = "__LOGLEVEL__"; public static final String PROFILES_KEY = "__PROFILES__"; public static final String VENUE_KEY = "__VENUE__"; private static void verify(Rectangle bounds) { GraphicsEnvironment ge = GraphicsEnvironment .getLocalGraphicsEnvironment(); GraphicsDevice[] gs = ge.getScreenDevices(); if (gs.length == 1) { Dimension size = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); boolean x_bad = bounds.x < 0 || bounds.x > size.width; boolean y_bad = bounds.y < 0 || bounds.y > size.height; if (x_bad || y_bad) { Debug.error("The tool believes the bounds %s will not display on your screen!" + " It is using defaults.", bounds); bounds.setBounds(ToolSettings.DEFAULT_BOUNDS); } } Dimension size = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); bounds.width = Math.min(size.width, bounds.width); bounds.height = Math.min(size.height, bounds.height); } public static class DisplaySettings { public Rectangle bounds = null; public ViewProfile profile = null; public int splitLocation = -1; public DisplaySettings(Rectangle r, ViewProfile p, int split) { bounds = r; profile = p; splitLocation = split; } public JsonObject serialize() { JsonObject obj = Json.object(); if (bounds == null) { obj.put("bounds", Json.NULL_VALUE); } else { JsonArray array = Json.array(); array.add(bounds.x); array.add(bounds.y); array.add(bounds.width); array.add(bounds.height); obj.put("bounds", array); } obj.put("vp", profile == null ? Json.NULL_VALUE : new JsonString(profile.name)); obj.put("split", splitLocation); return obj; } public DisplaySettings(JsonValue _obj) { JsonObject obj = _obj.asObject(); if (obj.containsKey("bounds") && !Json.isNull(obj.get("bounds"))) { JsonArray array = obj.get("bounds").asArray(); bounds = new Rectangle( array.get(0).asNumber().intValue(), array.get(1).asNumber().intValue(), array.get(2).asNumber().intValue(), array.get(3).asNumber().intValue() ); verify(bounds); } else { bounds = ToolSettings.DEFAULT_BOUNDS; } if (obj.containsKey("vp") && !Json.isNull(obj.get("vp"))) { JsonString vp = obj.get("vp").asString(); profile = ViewProfile.PROFILES.get(vp.value); } if (profile == null) profile = ViewProfile.DEFAULT_PROFILE; if (obj.containsKey("split")) { splitLocation = obj.get("split").asNumber().asInt(); } } @Override public String toString() { return String.format("DisplaySettings{%s}{%s}", bounds, profile); } } /* SAVE PREFERENCES TO FILESYSTEM */ public static void savePreferences() throws IOException { Debug.info("saving preferences to %s", ToolSettings.USER_PREFERENCES_PATH); JsonObject object = Json.object(); object.put("#NBTOOL_USER_PREFS", Json.NULL_VALUE); object.put(SharedConstants.LOG_TOPLEVEL_MAGIC_KEY(), ToolSettings.VERSION); JsonObject internal = Json.object(); JsonObject bounds = Json.object(); JsonArray addr = Json.array(); JsonArray load = Json.array(); internal.put(BOUNDS_KEY, bounds); for (Entry<String, DisplaySettings> entry : BOUNDS_MAP.entrySet()) { bounds.put(entry.getKey(), entry.getValue().serialize()); } internal.put(ADDR_KEY, addr); for (String a : addresses.vector()) { addr.add(a); } internal.put(LOAD_KEY, load); for (Path l : loadPathes.vector()) { load.add(l.toString()); } internal.put(LL_KEY, logLevel.name()); internal.put(VENUE_KEY, venue); internal.put(PROFILES_KEY, ViewProfile.serializeProfiles()); object.put(INTERNAL_KEY, internal); for (Entry<String, JsonValue> entry : PREFERENCES.entrySet()) { object.put(entry.getKey(), entry.getValue()); } byte[] bytes = object.print().getBytes(StandardCharsets.UTF_8); Files.write(ToolSettings.USER_PREFERENCES_PATH, bytes); } /* LOAD PREFERENCES FROM FILESYSTEM */ public static void loadPreferences() throws IOException, ClassNotFoundException, JsonParseException { Debug.print("fetching user preferences from: " + ToolSettings.USER_PREFERENCES_PATH); if (!Files.exists(ToolSettings.USER_PREFERENCES_PATH)) { Debug.error("preferences file not found!"); ViewProfile.setupProfiles(null); return; } String prefText = new String(Files.readAllBytes(ToolSettings.USER_PREFERENCES_PATH), StandardCharsets.UTF_8); prefText = prefText.trim(); if (prefText.startsWith("(")) { Debug.error("not using old SExpr user preferences file (it will be overwritten on exit!)"); ViewProfile.setupProfiles(null); return; } JsonObject all = Json.parse(prefText).asObject(); if (!all.containsKey("#NBTOOL_USER_PREFS") || !(all.get(SharedConstants.LOG_TOPLEVEL_MAGIC_KEY()).asNumber().intValue() == ToolSettings.VERSION)) { Debug.error("cannot use saved preferences at: %s", ToolSettings.USER_PREFERENCES_PATH); Debug.error("using default values."); ViewProfile.setupProfiles(null); return; } Debug.print("preferences format accepted."); JsonObject internal = all.remove(INTERNAL_KEY).asObject(); JsonObject bounds = internal.remove(BOUNDS_KEY).asObject(); JsonArray addr = internal.remove(ADDR_KEY).asArray(); JsonArray load = internal.remove(LOAD_KEY).asArray(); if (internal.containsKey(VENUE_KEY)) { JsonString _venue = internal.remove(VENUE_KEY).asString(); venue = _venue.value; } for (Entry<JsonString, JsonValue> entry : bounds.entrySet()) { BOUNDS_MAP.put(entry.getKey().value, new DisplaySettings(entry.getValue())); } for (ListIterator<JsonValue> i = addr.listIterator(addr.size()); i.hasPrevious(); ) { JsonValue val = i.previous(); addresses.update(val.asString().value); } for (ListIterator<JsonValue> i = load.listIterator(load.size()); i.hasPrevious(); ) { JsonValue val = i.previous(); loadPathes.update(Paths.get(val.asString().value)); } logLevel = Debug.LogLevel.valueOf(internal.get(LL_KEY).asString().value); if (internal.get(PROFILES_KEY).type() == Json.JsonValueType.STRING) { Debug.warn("ha! you used tool8 before it was stable... sorry, your profiles do not transfer."); ViewProfile.setupProfiles(null); } else { ViewProfile.setupProfiles(internal.get(PROFILES_KEY).asObject()); } for (Entry<JsonString, JsonValue> entry : all.entrySet()) { PREFERENCES.put(entry.getKey().value, entry.getValue()); } } }