From 0b4f165ea09f6f1b40586f8e6b84599da1369242 Mon Sep 17 00:00:00 2001 From: _Trizion_ Date: Tue, 19 Mar 2024 21:35:56 +0100 Subject: [PATCH] Add checks for version of geometry_generator of opened file --- src/main/java/fabulator/FABulator.java | 14 +++ src/main/java/fabulator/language/Text.java | 9 +- src/main/java/fabulator/object/Version.java | 67 ++++++++++ .../java/fabulator/parse/GeometryParser.java | 6 +- src/main/java/fabulator/settings/Config.java | 12 +- .../fabulator/ui/builder/ButtonBuilder.java | 5 + .../fabulator/ui/builder/LabelBuilder.java | 5 + .../fabulator/ui/window/ChoiceDialog.java | 88 ++++++++++++++ .../fabulator/ui/window/FABulatorWindow.java | 6 + .../fabulator/ui/window/LoadingWindow.java | 2 +- src/main/java/fabulator/util/FileUtils.java | 115 ++++++++++++------ src/main/resources/default.properties | 1 + src/main/resources/language/english.txt | 8 +- src/main/resources/language/german.txt | 8 +- src/main/resources/style/style-dark.css | 48 +++++++- 15 files changed, 347 insertions(+), 47 deletions(-) create mode 100644 src/main/java/fabulator/object/Version.java create mode 100644 src/main/java/fabulator/ui/window/ChoiceDialog.java diff --git a/src/main/java/fabulator/FABulator.java b/src/main/java/fabulator/FABulator.java index af3c22b..cfea472 100644 --- a/src/main/java/fabulator/FABulator.java +++ b/src/main/java/fabulator/FABulator.java @@ -23,6 +23,7 @@ import javafx.application.Application; import javafx.scene.Scene; import javafx.stage.Stage; +import javafx.stage.WindowEvent; import lombok.Getter; import java.util.ArrayList; @@ -38,6 +39,7 @@ public class FABulator extends Application { private Stage stage; private MainView mainView; + private List closedRequestListeners = new ArrayList<>(); private List closedListeners = new ArrayList<>(); public static void main(String[] args) { @@ -49,8 +51,10 @@ public void start(Stage stage) { application = this; this.stage = stage; + this.stage.setOnCloseRequest(this::closeRequested); this.stage.setTitle(APP_NAME); this.stage.getIcons().add(ImageIcon.FABULOUS.getImage()); + this.mainView = new MainView(); Scene scene = new Scene( @@ -75,6 +79,16 @@ public void stop() { } } + private void closeRequested(WindowEvent event) { + for (Runnable closedRequestListener : this.closedRequestListeners) { + closedRequestListener.run(); + } + } + + public void addClosedRequestListener(Runnable runnable) { + this.closedRequestListeners.add(runnable); + } + public void addClosedListener(Runnable runnable) { this.closedListeners.add(runnable); } diff --git a/src/main/java/fabulator/language/Text.java b/src/main/java/fabulator/language/Text.java index 98057d1..7102d59 100644 --- a/src/main/java/fabulator/language/Text.java +++ b/src/main/java/fabulator/language/Text.java @@ -4,6 +4,8 @@ import javafx.beans.property.StringProperty; public enum Text { + YES, + NO, LEFT, RIGHT, @@ -96,7 +98,12 @@ public enum Text { EXPLORER_HINT_2, ERASE_FASM, - CLEAR_SELECTION; + CLEAR_SELECTION, + + OUTDATED_VERSION, + OUTDATED_INFO_1, + OUTDATED_INFO_2, + OPEN_ANYWAYS; private StringProperty stringProperty; diff --git a/src/main/java/fabulator/object/Version.java b/src/main/java/fabulator/object/Version.java new file mode 100644 index 0000000..19eef94 --- /dev/null +++ b/src/main/java/fabulator/object/Version.java @@ -0,0 +1,67 @@ +package fabulator.object; + +import lombok.Getter; + +@Getter +public class Version { // TODO: tests + + private int major; + private int minor; + private int patch; + + public Version(String versionString) throws NumberFormatException { + this.parseString(versionString); + } + + private void parseString(String versionString) throws NumberFormatException { + if (versionString != null && !versionString.isEmpty()) { + String[] parts = versionString.split("\\."); + + if (parts.length == 3) { + this.major = Integer.parseInt(parts[0]); + this.minor = Integer.parseInt(parts[1]); + this.patch = Integer.parseInt(parts[2]); + } else { + throw new NumberFormatException(); + } + } else { + throw new NumberFormatException(); + } + } + + public static boolean outdated(Version minVersion, Version version) { + boolean outdated = true; + + if (version != null) { + outdated = !minVersion.lessEqual(version); + } + return outdated; + } + + public boolean lessEqual(Version version) { + boolean lessEqual = false; + + if (version.getMajor() > this.major) { + lessEqual = true; + + } else if (version.getMajor() == this.major) { + if (version.getMinor() > this.minor) { + lessEqual = true; + + } else if (version.getMinor() == this.minor) { + lessEqual = version.getPatch() >= this.patch; + } + } + return lessEqual; + } + + @Override + public String toString() { + return String.format( + "%d.%d.%d", + this.major, + this.minor, + this.patch + ); + } +} diff --git a/src/main/java/fabulator/parse/GeometryParser.java b/src/main/java/fabulator/parse/GeometryParser.java index 7f6ccfc..719af31 100644 --- a/src/main/java/fabulator/parse/GeometryParser.java +++ b/src/main/java/fabulator/parse/GeometryParser.java @@ -2,6 +2,7 @@ import fabulator.geometry.*; import fabulator.object.Location; +import fabulator.object.Version; import fabulator.util.StringUtils; import lombok.Getter; @@ -37,7 +38,8 @@ enum ParsingMode { } private ParsingMode parsingMode; - private String generatorVersion; + private Version generatorVersion; + private String name; private int numberOfRows; private int numberOfColumns; @@ -136,7 +138,7 @@ private void parseAsParams(String[] tokens, String attribute) { assert tokens.length == 2; switch (attribute) { - case "GeneratorVersion" -> this.generatorVersion = tokens[1]; + case "GeneratorVersion" -> this.generatorVersion = new Version(tokens[1]); case "Name" -> this.name = tokens[1]; case "Rows" -> this.numberOfRows = Integer.parseInt(tokens[1]); case "Columns" -> this.numberOfColumns = Integer.parseInt(tokens[1]); diff --git a/src/main/java/fabulator/settings/Config.java b/src/main/java/fabulator/settings/Config.java index 005227f..3e308e9 100644 --- a/src/main/java/fabulator/settings/Config.java +++ b/src/main/java/fabulator/settings/Config.java @@ -3,6 +3,7 @@ import fabulator.language.Language; import fabulator.logging.LogManager; import fabulator.logging.Logger; +import fabulator.object.Version; import fabulator.util.StringUtils; import javafx.beans.property.*; import javafx.scene.paint.Color; @@ -22,6 +23,8 @@ public class Config { private Properties properties; + private Version minGeneratorVersion; + private StringProperty openedFabricFileName; private StringProperty openedFasmFileName; private StringProperty openedHdlFileName; @@ -63,6 +66,7 @@ private void initialize() { Properties defaultProperties = this.loadDefaultProperties(); this.properties = this.loadProperties(defaultProperties); + this.buildNonPropertyObjs(); this.buildPropertyObjects(); } catch (IOException e) { @@ -98,6 +102,12 @@ private boolean tryCreateProps() throws IOException { return success; } + private void buildNonPropertyObjs() { + this.minGeneratorVersion = new Version( + this.properties.getProperty("minGeneratorVersion") + ); + } + private void buildPropertyObjects() { this.openedFabricFileName = this.buildStringSetting("openedFabricFileName"); this.openedFasmFileName = this.buildStringSetting("openedFasmFileName"); @@ -115,7 +125,7 @@ private void buildPropertyObjects() { this.belPortColor = this.buildColorSetting("belPortColor"); this.smConnInColor = this.buildColorSetting("smConnInColor"); this.smConnOutColor = this.buildColorSetting("smConnOutColor"); - this.smConnJumpColor = this.buildColorSetting("smConnJumpColor"); + this.smConnJumpColor = this.buildColorSetting("smConnJumpColor"); this.userDesignColor = this.buildColorSetting("userDesignColor"); this.userDesignMarkedColor = this.buildColorSetting("userDesignMarkedColor"); diff --git a/src/main/java/fabulator/ui/builder/ButtonBuilder.java b/src/main/java/fabulator/ui/builder/ButtonBuilder.java index 3b2af5d..5660b15 100644 --- a/src/main/java/fabulator/ui/builder/ButtonBuilder.java +++ b/src/main/java/fabulator/ui/builder/ButtonBuilder.java @@ -36,6 +36,11 @@ public ButtonBuilder setColor(UiColor color) { return this; } + public ButtonBuilder setId(String id) { + this.button.setId(id); + return this; + } + public ButtonBuilder setTooltip(Text text) { Tooltip tooltip = new Tooltip(); tooltip.textProperty().bind(text.stringProperty()); diff --git a/src/main/java/fabulator/ui/builder/LabelBuilder.java b/src/main/java/fabulator/ui/builder/LabelBuilder.java index 8a4567f..909b1bf 100644 --- a/src/main/java/fabulator/ui/builder/LabelBuilder.java +++ b/src/main/java/fabulator/ui/builder/LabelBuilder.java @@ -39,6 +39,11 @@ public LabelBuilder setText(Text text) { return this; } + public LabelBuilder setWrapText(boolean wrapText) { + this.label.setWrapText(wrapText); + return this; + } + public LabelBuilder setTranslateX(double translateX) { this.label.setTranslateX(translateX); return this; diff --git a/src/main/java/fabulator/ui/window/ChoiceDialog.java b/src/main/java/fabulator/ui/window/ChoiceDialog.java new file mode 100644 index 0000000..64e67fa --- /dev/null +++ b/src/main/java/fabulator/ui/window/ChoiceDialog.java @@ -0,0 +1,88 @@ +package fabulator.ui.window; + +import fabulator.language.Text; +import fabulator.ui.builder.ButtonBuilder; +import fabulator.ui.builder.LabelBuilder; +import fabulator.util.LayoutUtils; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import lombok.Setter; + +public class ChoiceDialog extends FABulatorWindow { + + private Text[] dialogText; + private Button yesButton; + private Button noButton; + + @Setter + private Runnable yesRunnable = () -> {}; + + @Setter + private Runnable noRunnable = () -> {}; + + public ChoiceDialog(int width, int height, Text title, Text... dialogText) { + super(width, height, title); + + this.dialogText = dialogText; + + this.initialize(); + this.setup(); + } + + private void initialize() { + this.yesButton = new ButtonBuilder() + .setId("yesButton") + .setText(Text.YES) + .setTooltip(Text.YES) + .setOnAction(this::yesClicked) + .build(); + + this.noButton = new ButtonBuilder() + .setId("noButton") + .setText(Text.CANCEL) + .setTooltip(Text.CANCEL) + .setOnAction(this::noClicked) + .build(); + } + + private void setup() { + VBox root = new VBox(); + root.getStyleClass().add("choice-dialog"); + + ObservableList rootChildren = root.getChildren(); + + for (Text text : this.dialogText) { + Label textLabel = new LabelBuilder() + .setText(text) + .setWrapText(true) + .build(); + rootChildren.add(textLabel); + } + + rootChildren.addAll( + LayoutUtils.vSpacer(), + new HBox( + LayoutUtils.hSpacer(), + this.yesButton, + this.noButton + ) + ); + + this.setRoot(root); + } + + private void yesClicked(ActionEvent event) { + this.close(); + this.yesRunnable.run(); + } + + private void noClicked(ActionEvent event) { + this.close(); + this.noRunnable.run(); + } +} diff --git a/src/main/java/fabulator/ui/window/FABulatorWindow.java b/src/main/java/fabulator/ui/window/FABulatorWindow.java index 77eeecc..7b61218 100644 --- a/src/main/java/fabulator/ui/window/FABulatorWindow.java +++ b/src/main/java/fabulator/ui/window/FABulatorWindow.java @@ -1,5 +1,6 @@ package fabulator.ui.window; +import fabulator.FABulator; import fabulator.language.Text; import fabulator.ui.icon.ImageIcon; import fabulator.ui.style.Style; @@ -27,6 +28,7 @@ public FABulatorWindow(int width, int height, Text title) { this.setHeight(height); this.setIcon(); this.initScene(); + this.initListeners(); this.setStyle(); } @@ -59,6 +61,10 @@ private void initScene() { this.setScene(this.scene); } + private void initListeners() { + FABulator.getApplication().addClosedRequestListener(this::close); + } + public void setRoot(Pane root) { this.root = root; this.scene.setRoot(root); diff --git a/src/main/java/fabulator/ui/window/LoadingWindow.java b/src/main/java/fabulator/ui/window/LoadingWindow.java index 6df2a6b..708c882 100644 --- a/src/main/java/fabulator/ui/window/LoadingWindow.java +++ b/src/main/java/fabulator/ui/window/LoadingWindow.java @@ -46,7 +46,7 @@ private void buildSelf() { ); root.setAlignment(Pos.CENTER); root.setSpacing(20); - root.getStyleClass().add("loading-pane"); + root.getStyleClass().add("loading-window"); // TODO: Add to StyleClass this.setRoot(root); } diff --git a/src/main/java/fabulator/util/FileUtils.java b/src/main/java/fabulator/util/FileUtils.java index 898b817..17a6f9e 100644 --- a/src/main/java/fabulator/util/FileUtils.java +++ b/src/main/java/fabulator/util/FileUtils.java @@ -7,10 +7,12 @@ import fabulator.logging.LogManager; import fabulator.logging.Logger; import fabulator.lookup.BitstreamConfiguration; +import fabulator.object.Version; import fabulator.parse.FasmParser; import fabulator.parse.GeometryParser; import fabulator.settings.Config; import fabulator.ui.fabric.Fabric; +import fabulator.ui.window.ChoiceDialog; import fabulator.ui.window.ErrorMessageDialog; import fabulator.ui.window.LoadingWindow; import javafx.application.Platform; @@ -146,26 +148,7 @@ public static void openFabricAsync(File file) { Logger logger = LogManager.getLogger(); logger.info("Asynchronously opening fabric file " + file.getName()); - LoadingWindow.getInstance().show(); - - FABulator.getApplication() - .getMainView() - .dropReferences(); - - new Thread(() -> { - FileChangedManager.getInstance().setFile(file); - - GeometryParser parser = new GeometryParser(file); - FabricGeometry geometry = parser.getGeometry(); - Fabric fabric = new Fabric(geometry); - - Platform.runLater(() -> { - FABulator.getApplication() - .getMainView() - .setNewFabric(fabric); - Platform.runLater(() -> LoadingWindow.getInstance().hide()); - }); - }).start(); + openFabricWithVersionCheck(file, true); } else { Logger logger = LogManager.getLogger(); @@ -185,21 +168,7 @@ public static void openFabricSync(File file) { Logger logger = LogManager.getLogger(); logger.info("Synchronously opening fabric file " + file.getName()); - FABulator.getApplication() - .getMainView() - .dropReferences(); - - FileChangedManager.getInstance().setFile(file); - - GeometryParser parser = new GeometryParser(file); - FabricGeometry geometry = parser.getGeometry(); - Fabric fabric = new Fabric(geometry); - - Platform.runLater(() -> { - FABulator.getApplication() - .getMainView() - .setNewFabric(fabric); - }); + openFabricWithVersionCheck(file, false); } else { Logger logger = LogManager.getLogger(); @@ -209,6 +178,82 @@ public static void openFabricSync(File file) { } } + /** + * Opens a fabric file and does a version check to + * ensure that the generatorVersion of the file is + * supported. + * + * @param file the file to open + * @param async open the fabric asynchronously + */ + private static void openFabricWithVersionCheck(File file, boolean async) { + GeometryParser parser = new GeometryParser(file); + + Config config = Config.getInstance(); + Version minGeneratorVersion = config.getMinGeneratorVersion(); + Version generatorVersion = parser.getGeneratorVersion(); + + if (!Version.outdated(minGeneratorVersion, generatorVersion)) { + openGeomOf(parser, async); + } else { + LoadingWindow.getInstance().hide(); + + ChoiceDialog dialog = new ChoiceDialog( + 400, + 260, + Text.OUTDATED_VERSION, + Text.OUTDATED_INFO_1, + Text.OUTDATED_INFO_2, + Text.OPEN_ANYWAYS + ); + dialog.setYesRunnable(() -> openGeomOf(parser, async)); + dialog.show(); + } + } + + /** + * Opens a fabric from a {@link GeometryParser} object. + * + * @param parser the {@link GeometryParser} object + * @param async open the fabric asynchronously + */ + private static void openGeomOf(GeometryParser parser, boolean async) { + if (async) { + LoadingWindow.getInstance().show(); + } + + FABulator.getApplication() + .getMainView() + .dropReferences(); + + FabricGeometry geometry = parser.getGeometry(); + + if (async) { + Thread openGeomThread = new Thread(() -> { + openGeom(geometry, parser.getFile()); + }); + openGeomThread.start(); + } else { + openGeom(geometry, parser.getFile()); + } + } + + private static void openGeom(FabricGeometry geometry, File file) { + Fabric fabric = new Fabric(geometry); + + Platform.runLater(() -> { + FABulator.getApplication() + .getMainView() + .setNewFabric(fabric); + + FileChangedManager.getInstance().setFile(file); + + Platform.runLater(() -> { + LoadingWindow.getInstance().hide(); + }); + }); + } + /** * Opens a dialog for choosing a folder to open. */ diff --git a/src/main/resources/default.properties b/src/main/resources/default.properties index dfe52bb..d98cc39 100644 --- a/src/main/resources/default.properties +++ b/src/main/resources/default.properties @@ -1,3 +1,4 @@ +minGeneratorVersion=1.0.0 openedFabricFileName=null openedFasmFileName=null openedHdlFileName=null diff --git a/src/main/resources/language/english.txt b/src/main/resources/language/english.txt index 42d08af..e0e12d5 100644 --- a/src/main/resources/language/english.txt +++ b/src/main/resources/language/english.txt @@ -1,3 +1,5 @@ +YES: Yes +NO: No LEFT: Left RIGHT: Right FILE: File @@ -77,4 +79,8 @@ COLLAPSE_ALL: Collapse All EXPLORER_HINT_1: To open a folder: File > Open Folder EXPLORER_HINT_2: Click a file to open it in the editor ERASE_FASM: Erase fasm -CLEAR_SELECTION: Clear Selection \ No newline at end of file +CLEAR_SELECTION: Clear Selection +OUTDATED_VERSION: Outdated Version +OUTDATED_INFO_1: The selected file was created with an outdated geometry_generator version. +OUTDATED_INFO_2: Geometry files which were created with an outdated version can be faulty or cause compatibility issues. +OPEN_ANYWAYS: Open anyways? \ No newline at end of file diff --git a/src/main/resources/language/german.txt b/src/main/resources/language/german.txt index f893a61..3736caa 100644 --- a/src/main/resources/language/german.txt +++ b/src/main/resources/language/german.txt @@ -1,3 +1,5 @@ +YES: Ja +NO: Nein LEFT: Links RIGHT: Rechts FILE: Datei @@ -77,4 +79,8 @@ COLLAPSE_ALL: Alle einklappen EXPLORER_HINT_1: Ordner öffnen: Datei > Ordner öffnen EXPLORER_HINT_2: Anzuzeigende Datei anklicken ERASE_FASM: Fasm entfernen -CLEAR_SELECTION: Auswahl aufheben \ No newline at end of file +CLEAR_SELECTION: Auswahl aufheben +OUTDATED_VERSION: Veraltete Version +OUTDATED_INFO_1: Die gewählte Datei wurde mit einer veralteten geometry_generator Version erzeugt. +OUTDATED_INFO_2: Mit veralteten Versionen generierte geometry-Dateien können fehlerhaft sein oder Kompatibilitätsprobleme auslösen. +OPEN_ANYWAYS: Trotzdem öffnen? \ No newline at end of file diff --git a/src/main/resources/style/style-dark.css b/src/main/resources/style/style-dark.css index 953a419..119cadb 100644 --- a/src/main/resources/style/style-dark.css +++ b/src/main/resources/style/style-dark.css @@ -15,17 +15,41 @@ } -/* ---- FABulatorWindow Style ---- */ -.loading-pane { +/* ---- LoadingWindow Style ---- */ +.loading-window { -fx-background-color: secondary; -fx-background-radius: 12; } -.loading-pane .label { +.loading-window .label { -fx-text-fill: -fx-icon-fill; -fx-font-size: 16; } +/* ---- Choice-Dialog Style ---- */ +.choice-dialog { + -fx-background-color: secondary; + -fx-padding: 16; +} +.choice-dialog, +.choice-dialog HBox { + -fx-spacing: 8; +} +.choice-dialog .label { + -fx-text-fill: -fx-icon-fill; + -fx-font-size: 14; +} +.choice-dialog .button { + -fx-background-color: tertiary; + -fx-background-radius: 8; + -fx-text-fill: -fx-icon-fill; + -fx-font-size: 14; +} +.choice-dialog .button:hover { + -fx-background-color: tertiary-hover; +} + + /* ---- TextField Style ---- */ .text-field { -fx-focus-color: transparent; @@ -565,10 +589,24 @@ -fx-text-fill: -fx-icon-fill; } -/* TODO */ +/* TODO: extra css file for ids */ #highlight-on { -fx-background-color: -fx-icon-fill; } #highlight-off { -fx-background-color: primary; -} \ No newline at end of file +} + +#yesButton { + -fx-background-color: secondary; +} +#yesButton:hover { + -fx-background-color: #315443; +} + +#noButton { + -fx-background-color: secondary; +} +#noButton:hover { + -fx-background-color: secondary-hover; +}