runway-visualiser-tool / code / src / Main.java
Main.java
Raw
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.awt.event.KeyEvent;
import java.beans.EventHandler;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Scanner;


public class Main extends Application {

    private ConfigPanel configPanel; // config module
    private RunwayGraphics runwayGraphics; // graphics module
    private AffectedRunway currentRunway; // calculation module
    private HistoryPanel historyPanel; // history module
    private Stage stage;

    public static void main(String[] args) {
        System.out.println("If you're reading this I compiled and ran");
        launch(args);
    }

    @Override
    public void start(Stage stage) { // initialise window
        this.stage = stage;
        stage.setTitle("Runway Re-declaration");
        VBox root = setUpMainGUI();
        setupConfigButtons();
        currentRunway = configPanel.getAffectedRunway();
        stage.setScene(new Scene(root, 1000, 600));
        stage.setMaximized(true);
        stage.show();


        /** keyboard shortcuts
         * v - iterate through view modes
         * e - export menu
         * i - import menu
         * l - clear all presets
         */
        stage.getScene().setOnKeyPressed(e -> {
            if (e.getCode() == KeyCode.V) {
                int currentView = runwayGraphics.viewSelect.getSelectionModel().getSelectedIndex();
                switch (currentView) {
                    case 0:
                        runwayGraphics.viewSelect.setValue("Side View");
                        break;
                    case 1:
                        runwayGraphics.viewSelect.setValue("Split View");
                        break;
                    case 3:
                        runwayGraphics.viewSelect.setValue("Top View");
                        break;
                }
            }
            if (e.getCode() == KeyCode.I) {
                importNewXML();
            }
            if (e.getCode() == KeyCode.E) {
                exportToXML();
            }
            if (e.getCode() == KeyCode.L) {
                Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Are you sure you want to clear all presets?",
                        ButtonType.YES, ButtonType.NO);
                alert.showAndWait();

                if (alert.getResult() == ButtonType.YES) {
                    clearPresets();
                }
            }
        });
    }

    // add functionality to buttons in config panel to update the graphical view
    private void setupConfigButtons() {
        Button runwayApply = configPanel.getApplyRunway();
        runwayApply.setOnMouseClicked((event) -> {
            Runway runway = configPanel.getRunway();
            if (runway == null) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setTitle("Error");
                alert.setHeaderText("Configuration Error");
                alert.setContentText("One of the parameters in the runway or obstruction was invalid: " + configPanel.getInvalidText());
                alert.showAndWait();
            } else {
                if(currentRunway != null) {
                    historyPanel.addHistoryEntry(compareChanges(runway));
                }
                Obstruction obstruction = configPanel.getObstruction();
                currentRunway = runway.recalculate(obstruction);
                System.out.println(currentRunway.getOriginalRunway());
                runwayGraphics.draw(currentRunway);
            }
        });

        Button obsApply = configPanel.getApplyObst();
        obsApply.setOnMouseClicked((event) -> {
            Obstruction obstruction = configPanel.getObstruction();
            if (obstruction == null) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setTitle("Error");
                alert.setHeaderText("Configuration Error");
                alert.setContentText("One of the parameters in the runway or obstruction was invalid: "  + configPanel.getInvalidText());
                alert.showAndWait();
            } else {
                historyPanel.addHistoryEntry(compareChanges(obstruction));
                currentRunway.recalculate(obstruction);
                System.out.println(currentRunway.getOriginalRunway());
                runwayGraphics.draw(currentRunway);
            }
        });
    }

    private String compareChanges(Runway newRunway) {
        Runway oldRunway = currentRunway.getOriginalRunway();
        if(oldRunway == null) {
            return "RUNWAY: ";
        }
        ArrayList<String> changes = new ArrayList<>();
        if(!oldRunway.getName().equals(newRunway.getName())) {
            changes.add("Name changed to " + newRunway.getName());
        }

        if(!oldRunway.getAirport().equals(newRunway.getAirport())) {
            changes.add("Airport changed to " + newRunway.getAirport());
        }
        if(oldRunway.getTORA() != (newRunway.getTORA())) {
            changes.add("TORA changed to " + newRunway.getTORA());
        }
        if(oldRunway.getTODA() != (newRunway.getTODA())) {
            changes.add("TODA changed to " + newRunway.getTODA());
        }
        if(oldRunway.getASDA() != (newRunway.getASDA())) {
            changes.add("ASDA changed to " + newRunway.getASDA());
        }
        if(oldRunway.getLDA() != (newRunway.getLDA())) {
            changes.add("LDA changed to " + newRunway.getLDA());
        }
        if(oldRunway.getDisplacedThreshold() != newRunway.getDisplacedThreshold()) {
            changes.add("Displaced Threshold changed to " + newRunway.getDisplacedThreshold());
        }
        if(oldRunway.getStripEnd() != newRunway.getStripEnd()) {
            changes.add("Strip End changed to " + newRunway.getStripEnd());
        }

        return "RUNWAY: " + String.join(", ", changes);
    }

    public String compareChanges(Obstruction obst) {
        Obstruction oldObst = currentRunway.getObstruction();
        if(oldObst == null) {
            return "OBSTRUCTION: ";
        }
        ArrayList<String> changes = new ArrayList<>();
        if(!oldObst.getName().equals(obst.getName())) {
            changes.add("Name changed to " + obst.getName());
        }

        if(oldObst.getHeight() != obst.getHeight()) {
            changes.add("Height changed to " + obst.getHeight());
        }

        if(oldObst.getLength() != obst.getLength()) {
            changes.add("Length changed to " + obst.getLength());
        }

        if(oldObst.getDistanceFromThreshold() != obst.getDistanceFromThreshold()) {
            changes.add("Distance from threshold changed to " + obst.getDistanceFromThreshold());
        }

        if(oldObst.getDistanceFromCentre() != obst.getDistanceFromCentre()) {
            changes.add("Distance from centre changed to " + obst.getDistanceFromCentre());
        }

        return "OBSTRUCTION: " + String.join(", ", changes);

    }

    // create the main vbox and gridpane that the modules will run in
    private VBox setUpMainGUI() {
        MenuBar menuBar = new MenuBar();
        setupMenus(menuBar);
        VBox root = new VBox(menuBar);

        GridPane main = new GridPane();
        ColumnConstraints col1 = new ColumnConstraints();
        ColumnConstraints col2 = new ColumnConstraints();
        ColumnConstraints col3 = new ColumnConstraints();
        col1.setPercentWidth(33.3);
        col2.setPercentWidth(33.3);
        col3.setPercentWidth(33.4);
        main.getColumnConstraints().addAll(col1, col2, col3);

        RowConstraints row1 = new RowConstraints();
        RowConstraints row2 = new RowConstraints();
        RowConstraints row3 = new RowConstraints();
        RowConstraints row4 = new RowConstraints();
        row1.setPercentHeight(25);
        row2.setPercentHeight(25);
        row3.setPercentHeight(25);
        row4.setPercentHeight(25);
        main.getRowConstraints().addAll(row1, row2, row3, row4);


        historyPanel = new HistoryPanel();
        historyPanel.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, CornerRadii.EMPTY, Insets.EMPTY)));
        configPanel = new ConfigPanel(historyPanel);
        configPanel.setBackground(new Background(new BackgroundFill(Color.LIGHTGRAY, CornerRadii.EMPTY, Insets.EMPTY)));
        currentRunway = configPanel.getAffectedRunway();
        main.add(configPanel, 2, 0, 1, 3);

        runwayGraphics = new RunwayGraphics(stage, new Theme(getLastTheme()));
        if(currentRunway != null)
            runwayGraphics.draw(currentRunway);
        main.add(runwayGraphics.getRunwayGraphics(), 0, 0, 2, 4);


        main.add(historyPanel, 2, 3);

//        runwayGraphics = new RunwayGraphics();
//        runwayGraphics.setAffectedRunway(currentRunway);
//        main.add(runwayGraphics.getRunwayGraphics(), 0, 0, 2, 4);

        root.getChildren().add(main);
        VBox.setVgrow(main, Priority.ALWAYS);
        return root;
    }

    private void setupMenus(MenuBar menuBar) {
        Menu file = new Menu("File");
        Menu accessibility = new Menu("Accessibility");
        Menu theme = new Menu("Theme");
        menuBar.getMenus().addAll(file, accessibility, theme);
        MenuItem importNewPresets = new MenuItem("Import New Presets");
        importNewPresets.setOnAction((event) -> {
            importNewXML();
        });

        // export all runways and obstructions
        MenuItem exportRunwaysAndObstructions = new MenuItem("Export Config");
        exportRunwaysAndObstructions.setOnAction((event) -> {
            exportToXML();
        });

        MenuItem clearPresets = new MenuItem("Clear All Presets");
        clearPresets.setOnAction((event) -> {
            clearPresets();
        });

//        MenuItem showCalculations = new MenuItem("Show Calculations");
//        showCalculations

//        MenuItem toggleCompass = new MenuItem("Toggle Compass");
//        toggleCompass.setOnAction((event) -> {
//            System.out.println("test");
//            runwayGraphics.drawRotated(currentRunway);
//        });
        file.getItems().addAll(importNewPresets, exportRunwaysAndObstructions, clearPresets);

        MenuItem defaultTheme = new MenuItem("Default Theme");
        MenuItem darkTheme = new MenuItem("Dark Theme");
        MenuItem monochromeTheme = new MenuItem("Monochrome Theme");

        theme.getItems().addAll(defaultTheme, darkTheme, monochromeTheme);
        defaultTheme.setOnAction((event) -> {
            runwayGraphics.changeTheme(new Theme("default"));
            setLastTheme();
        });
        darkTheme.setOnAction((event) -> {
            runwayGraphics.changeTheme(new Theme("dark"));
            setLastTheme();
        });
        monochromeTheme.setOnAction((event) -> {
            runwayGraphics.changeTheme(new Theme("monochrome"));
            setLastTheme();
        });


        MenuItem viewShortcuts = new MenuItem("View shortcuts");
        viewShortcuts.setOnAction((event) -> {
            displayShortcutsWindow();
        });

        accessibility.getItems().addAll(viewShortcuts);
    }

    private String fileChooserGetPath() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter("Runway/Obstruction Files", "*.xml"));
        String currentPath = Paths.get("src/xml").toAbsolutePath().normalize().toString();
        fileChooser.setInitialDirectory(new File(currentPath));
        File selectedFile = fileChooser.showOpenDialog(stage);
        if(selectedFile == null) {
            return null;
        }
        return selectedFile.getAbsolutePath();
    }

    private void importNewXML() {
        String path = fileChooserGetPath();
        if(path == null) {
            Alert alert = new Alert(Alert.AlertType.ERROR);
            alert.setTitle("Error");
            alert.setHeaderText("Import Error");
            alert.setContentText("Please select a file");
            alert.showAndWait();
        } else {
            try {
                importXML importXML = new importXML(path);
                configPanel.addRunwaysFromXML(importXML.importRunwaysFromXML());
                configPanel.addObstructionsFromXML(importXML.importObstructionsFromXML());
                historyPanel.addHistoryEntry("PRESETS: Imported " + path);
            } catch (Exception e) {
                Alert alert = new Alert(Alert.AlertType.ERROR);
                alert.setTitle("Error");
                alert.setHeaderText("Import Error");
                alert.setContentText(e.getMessage());
                alert.showAndWait();
            }
        }
    }

    private void exportToXML() {
        FileChooser fileChooser = new FileChooser();
        fileChooser.setInitialDirectory(new File(Paths.get("src/xml").toAbsolutePath().normalize().toString()));
        fileChooser.getExtensionFilters().setAll(new FileChooser.ExtensionFilter("Runway/Obstruction Files", "*.xml"));
        fileChooser.setTitle("Export runways and obstructions");
        File exporttarget = fileChooser.showSaveDialog(stage);
        if(exporttarget == null) {
            Alert errorMessage = new Alert(Alert.AlertType.ERROR);
            errorMessage.setContentText("Create a file to save config into.");
            errorMessage.show();
        } else {

            String filename = exporttarget.getAbsolutePath();

            // ensure filename has .xml extension
            if (!filename.substring(filename.length() - 4).equals(".xml")) {
                filename = filename.concat(".xml");
            }

            try {
                exportXML.exportBothToXML(filename, configPanel.getPresetObstructions(), configPanel.getPresetRunways());
            } catch (Exception e) {
                Alert errorMessage = new Alert(Alert.AlertType.ERROR);
                errorMessage.setHeaderText("An error occurred exporting the file");
                errorMessage.setContentText(e.getMessage());
                errorMessage.show();
            }
        }
    }

    private void clearPresets() {
        configPanel.clearPresets();
        historyPanel.addHistoryEntry("PRESETS: Cleared all");
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle("INFO");
        alert.setHeaderText("Presets Cleared");
        alert.showAndWait();
        runwayGraphics.clearRunway();
    }

    public void displayShortcutsWindow() {
        Stage shortcutsWindow = new Stage();
        shortcutsWindow.setTitle("Keyboard shortcuts");

        GridPane gp = new GridPane();

        gp.setAlignment(Pos.CENTER);
        gp.setPadding(new Insets(10));

        ColumnConstraints col0 = new ColumnConstraints();
        col0.setMinWidth(2);
        ColumnConstraints col1 = new ColumnConstraints();
        col1.setMinWidth(1);
        ColumnConstraints col2 = new ColumnConstraints();
        col1.setMinWidth(80);

        gp.setMinSize(150, 100);

        gp.getColumnConstraints().addAll(col0, col1, col2);

        gp.add(new Label("E"), 0, 0);
        gp.add(new Label(" "), 1, 0);
        gp.add(new Label("Export menu"), 2, 0);

        gp.add(new Label("I"), 0, 1);
        gp.add(new Label(" "), 1, 1);
        gp.add(new Label("Import menu"), 2, 1);

        gp.add(new Label("L"), 0, 2);
        gp.add(new Label(" "), 1, 2);
        gp.add(new Label("Clear all presets"), 2, 2);

        gp.add(new Label("V"), 0, 3);
        gp.add(new Label(" "), 1, 3);
        gp.add(new Label("Next view"), 2, 3);

        Scene scene = new Scene(gp);
        shortcutsWindow.setScene(scene);

        shortcutsWindow.initModality(Modality.APPLICATION_MODAL);
        shortcutsWindow.show();
    }

    public void setLastTheme() {
        try {
            File configFile = new File("src/bin/config.txt");
            configFile.createNewFile();
        } catch (IOException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
        try {
            FileWriter myWriter = new FileWriter("src/bin/config.txt");
            myWriter.write(runwayGraphics.getTheme().getCurrentTheme());
            myWriter.close();
        } catch (IOException e) {
            System.out.println("An error occurred.");
            e.printStackTrace();
        }
    }

    public String getLastTheme() {
        try {
            File configFile = new File("src/bin/config.txt");
            Scanner myReader = new Scanner(configFile);
            while (myReader.hasNextLine()) {
                String data = myReader.nextLine();
                return data;
            }
            myReader.close();
        } catch (FileNotFoundException e) {
            System.out.println("An error occurred.");
            return "default";
        }

        return "default";
    }
}