import javafx.geometry.HPos; import javafx.geometry.Insets; import javafx.scene.control.*; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; import java.util.ArrayList; import java.util.Arrays; public class ConfigPanel extends ScrollPane { private ComboBox runwayPresetCombo; private ComboBox obstPresetCombo; private Label runwayConfigText, runwayText, nameText, airportText, TORAText, TODAText, ASDAText, LDAText, displacedText, stripEndText, egrText, resaText, blastText, stopwayText, clearwayText, obstConfigText, obstText, obstName, obstHeightText, obstLengthText, distThreshText, distCentreText; private TextField nameTextField, airportTextField, TORATextField, TODATextField, ASDATextField, LDATextField, displacedTextField, stripEndTextField, egrTextField, resaTextField, blastTextField, stopwayTextField, clearwayTextField, obstNameTextField, obstHeightTextField, obstLengthTextField, distThreshTextField, distCentreTextField; private VBox configWindow; private Button saveRunwayPreset, removeRunwayPreset, applyRunway, saveObstPreset, removeObstPreset, applyObst; private ArrayList runwayIntTextFields, runwayStringTextFields, obstIntTextFields, optionalFields; private ArrayList presetRunways; private ArrayList presetObstructions; private HistoryPanel historyPanel; private String invalidText; ConfigPanel(HistoryPanel historyPanel) { this.historyPanel = historyPanel; populateDefaults(); setupScrollPane(); createLabels(); createTextFields(); createComboBoxes(); createButtons(); addFieldsToConfig(); runwayIntTextFields = new ArrayList<>(Arrays.asList(TORATextField, TODATextField, ASDATextField, LDATextField, displacedTextField, stripEndTextField, egrTextField, resaTextField, blastTextField, stopwayTextField, clearwayTextField)); runwayStringTextFields = new ArrayList<>(Arrays.asList(nameTextField, airportTextField)); obstIntTextFields = new ArrayList<>(Arrays.asList(obstHeightTextField, obstLengthTextField, distThreshTextField, distCentreTextField)); optionalFields = new ArrayList<>(Arrays.asList(stripEndTextField, egrTextField, resaTextField, blastTextField, stopwayTextField, clearwayTextField)); } // return AffectedRunway object of the current configuration public AffectedRunway getAffectedRunway() { Runway runway = getRunway(); Obstruction obstruction = getObstruction(); if(runway == null || obstruction == null) { return null; } else { return runway.recalculate(obstruction); } } // return currently outlined obstruction object public Obstruction getObstruction() { if(!areFieldsValid()) { return null; } ArrayList obstValues = new ArrayList<>(); for (TextField textField : obstIntTextFields) { String val = textField.textProperty().getValue(); obstValues.add(Integer.parseInt(val)); } String name = obstNameTextField.textProperty().getValue(); return new Obstruction(name, obstValues.get(0), obstValues.get(1), obstValues.get(2), obstValues.get(3)); } // return currently outlined runway object // TODO add proper support for defaults/optional fields (if blank use defaults) public Runway getRunway() { if(!areFieldsValid()) { return null; } ArrayList runwayValues = new ArrayList<>(); for (TextField textField : runwayIntTextFields) { String val = textField.textProperty().getValue(); runwayValues.add(Integer.parseInt(val)); } String name = nameTextField.textProperty().getValue(); String airport = airportTextField.textProperty().getValue(); return new Runway(name, airport, runwayValues.get(0), runwayValues.get(1), runwayValues.get(2), runwayValues.get(3), runwayValues.get(4), runwayValues.get(5), runwayValues.get(6), runwayValues.get(7), runwayValues.get(8), runwayValues.get(9), runwayValues.get(10)); } // check if the config currently has any invalid fields public Boolean areFieldsValid() { try { //Rules for input int tora = Integer.parseInt(runwayIntTextFields.get(0).textProperty().getValue()); int toda = Integer.parseInt(runwayIntTextFields.get(1).textProperty().getValue()); int asda = Integer.parseInt(runwayIntTextFields.get(2).textProperty().getValue()); int stopway = Integer.parseInt(runwayIntTextFields.get(9).textProperty().getValue()); int clearway = Integer.parseInt(runwayIntTextFields.get(10).textProperty().getValue()); if (tora < 400) { invalidText = "TORA below minimum value"; return false; } if (tora > 5000) { invalidText = "TORA above maximum value"; return false; } /** //TODA = TORA + Clearway if (toda != tora + clearway) { invalidText = "TODA not equal to TORA + Clearway"; return false; } //ASDA = TORA + Stopway if (asda != tora + stopway) { invalidText = "ASDA not equal to TORA + Stopway"; return false; } **/ for (TextField textField : obstIntTextFields) { String val = textField.textProperty().getValue(); if (textField == obstHeightTextField || textField == obstLengthTextField) { int value = Integer.parseInt(val); if (value <= 0 || value >= 999999) { invalidText = "Object height or length cannot be negative"; return false; } } else { int value = Integer.parseInt(val); if (value >= 999999) { invalidText = "Distance from threshold or centre line above maximum allowed value"; return false; } } } for (TextField textField : runwayIntTextFields) { String val = textField.textProperty().getValue(); if (!(val.equals("") && (optionalFields.contains(textField)))) { int value = Integer.parseInt(val); if (value < 0 || value >= 999999) { invalidText = "One of the runway configuration values is negative or above the maximum allowed value"; return false; } } } for (TextField textField : runwayStringTextFields) { String val = textField.textProperty().getValue(); if (val.length() <= 0 || val.length() >= 32) { // arbitrary limit to name size invalidText = "Invalid Airport name"; return false; } } String runName = nameTextField.textProperty().getValue(); try { int deg = Integer.parseInt(runName + "0"); if(deg < 0 || deg > 360) { invalidText = "Invalid Runway name"; return false; } } catch (NumberFormatException e) { try { int deg = Integer.parseInt(runName.substring(0, 2) + "0"); if(deg < 0 || deg > 360) { invalidText = "Invalid Runway name"; return false; } if (runName.length() > 3) { invalidText = "Invalid Runway name"; return false; } String letter = runName.substring(2); if (runName.length() == 3 && !(letter.equals("L") || letter.equals("R") || letter.equals("C"))) { invalidText = "Invalid Runway name"; return false; } } catch (NumberFormatException e2) { invalidText = "Invalid Runway name"; return false; } } String obstName = obstNameTextField.textProperty().getValue(); return obstName.length() > 0 && obstName.length() < 64; } catch (NumberFormatException e) { invalidText = "non integer input in a field requiring an integer"; return false; } } // setup actual panel private void setupScrollPane() { this.setFitToWidth(true); configWindow = new VBox(); this.setContent(configWindow); this.setBorder(new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); this.setPadding(new Insets(5)); } // initialise all the labels for fields private void createLabels() { runwayConfigText = new Label("Runway Configuration"); Font defaultFont = Font.getDefault(); runwayConfigText.setFont(Font.font(defaultFont.getFamily(), FontWeight.BOLD, 15)); runwayConfigText.setPadding(new Insets(10)); runwayText = new Label("Runway: "); nameText = new Label("Name: "); airportText = new Label("Airport: "); TORAText = new Label("TORA: "); TODAText = new Label("TODA: "); ASDAText = new Label("ASDA: "); LDAText = new Label("LDA: "); displacedText = new Label("Displaced Threshold: "); stripEndText = new Label("Strip End: "); egrText = new Label("EGR: "); resaText = new Label("RESA: "); blastText = new Label("Blast Allowance: "); stopwayText = new Label("Stopway: "); clearwayText = new Label("Clearway: "); obstConfigText = new Label("Obstacle Configuration"); obstConfigText.setFont(Font.font(defaultFont.getFamily(), FontWeight.BOLD, 15)); obstConfigText.setPadding(new Insets(10)); obstText = new Label("Obstacle: "); obstName = new Label("Name: "); obstHeightText = new Label("Height: "); obstLengthText = new Label("Length: "); distThreshText = new Label("Distance from Threshold: "); distCentreText = new Label("Distance from Centreline: "); } // initialise all the textfields private void createTextFields() { nameTextField = new TextField(); airportTextField = new TextField(); TORATextField = new TextField(); TODATextField = new TextField(); ASDATextField = new TextField(); LDATextField = new TextField(); displacedTextField = new TextField(); stripEndTextField = new TextField(); egrTextField = new TextField(); resaTextField = new TextField(); blastTextField = new TextField(); stopwayTextField = new TextField(); clearwayTextField = new TextField(); obstNameTextField = new TextField(); obstHeightTextField = new TextField(); obstLengthTextField = new TextField(); distThreshTextField = new TextField(); distCentreTextField = new TextField(); } // initialise buttons private void createButtons() { saveRunwayPreset = new Button("Save Runway as Preset"); saveRunwayPreset.setOnMouseClicked((event) -> { Runway runway = getRunway(); if(runway == null) { configError("One of the parameters in the runway or obstruction was invalid: " + invalidText); } else { presetRunways.add(runway); runwayPresetCombo.getItems().add(runway); runwayPresetCombo.setValue(runway); populateRunwayFields(runway); try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); } catch (Exception e) { configError("The file could not be exported.\n" + e); } historyPanel.addHistoryEntry("PRESETS: added " + runway.getName() + " - " + runway.getAirport() + " to presets"); } }); removeRunwayPreset = new Button("Delete Current Runway Preset"); removeRunwayPreset.setOnMouseClicked((event) -> { Runway runway = getRunway(); if(runway == null) { configError("One of the parameters in the runway or obstruction was invalid: " + invalidText); } else { Runway toRemove = getRunwayFromName(runway.getName(), runway.getAirport()); if (toRemove == null) { configError("This runway is not saved!"); } else { presetRunways.remove(toRemove); runwayPresetCombo.getItems().remove(toRemove); runwayPresetCombo.setValue(runwayPresetCombo.getItems().get(0)); populateRunwayFields(runwayPresetCombo.getValue()); try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); historyPanel.addHistoryEntry("PRESETS: " + runway.getName() + " - " + runway.getAirport() + " removed from presets"); } catch (Exception e) { configError("The file could not be exported.\n" + e); } } } }); applyRunway = new Button("Render"); saveObstPreset = new Button("Save Obstacle as Preset"); saveObstPreset.setOnMouseClicked((event) -> { Obstruction obstruction = getObstruction(); if(obstruction == null) { configError("One of the parameters in the runway or obstruction was invalid: " + invalidText); } else { presetObstructions.add(obstruction); obstPresetCombo.getItems().add(obstruction); obstPresetCombo.setValue(obstruction); populateObstFields(obstruction); try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); historyPanel.addHistoryEntry("PRESETS: " + obstruction.getName() + " added to presets"); } catch (Exception e) { configError("The file could not be exported.\n" + e); } } }); removeObstPreset = new Button("Delete Current Obstacle Preset"); removeObstPreset.setOnMouseClicked((event) -> { Obstruction obstruction = getObstruction(); if(obstruction == null) { configError("One of the parameters in the runway or obstruction was invalid: " + invalidText); } else { Obstruction toRemove = getObstructionFromName(obstruction.getName()); if (toRemove == null) { configError("This obstruction is not saved!"); } else { presetObstructions.remove(toRemove); obstPresetCombo.getItems().remove(toRemove); obstPresetCombo.setValue(obstPresetCombo.getItems().get(0)); populateObstFields(obstPresetCombo.getValue()); try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); historyPanel.addHistoryEntry("PRESETS: " + obstruction.getName() + " removed from presets"); } catch (Exception e) { configError("The file could not be exported.\n" + e); } } } }); applyObst = new Button("Render"); } private Runway getRunwayFromName(String name, String airport) { for(Runway runway : presetRunways) { if (runway.getName().equals(name) && runway.getAirport().equals(airport)) { return runway; } } return null; } private Obstruction getObstructionFromName(String name) { for(Obstruction obstruction : presetObstructions) { if (obstruction.getName().equals(name)) { return obstruction; } } return null; } public void addRunwaysFromXML(ArrayList runways) { for(Runway runway : runways) { Runway exists = getRunwayFromName(runway.getName(), runway.getAirport()); if(exists == null) { presetRunways.add(runway); runwayPresetCombo.getItems().add(runway); } } try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); } catch (Exception e) { configError("The file could not be exported.\n" + e); } runwayPresetCombo.setValue(presetRunways.get(0)); populateRunwayFields(runwayPresetCombo.getValue()); } public void addObstructionsFromXML(ArrayList obstructions) { for(Obstruction obstruction : obstructions) { Obstruction exists = getObstructionFromName(obstruction.getName()); if(exists == null) { presetObstructions.add(obstruction); obstPresetCombo.getItems().add(obstruction); } } try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); } catch (Exception e) { configError("The file could not be exported.\n" + e); } obstPresetCombo.setValue(presetObstructions.get(0)); populateObstFields(obstPresetCombo.getValue()); } public void clearPresets() { runwayPresetCombo.getItems().removeAll(presetRunways); obstPresetCombo.getItems().removeAll(presetObstructions); presetObstructions = new ArrayList<>(); presetRunways = new ArrayList<>(); try { exportXML.exportBothToXML("src/xml/presets.xml", presetObstructions, presetRunways); } catch (Exception e) { configError("The file could not be exported.\n" + e); } clearTextFields(); } private void clearTextFields() { nameTextField.textProperty().setValue(""); airportTextField.textProperty().setValue(""); TORATextField.textProperty().setValue(""); TODATextField.textProperty().setValue(""); ASDATextField.textProperty().setValue(""); LDATextField.textProperty().setValue(""); displacedTextField.textProperty().setValue(""); stripEndTextField.textProperty().setValue(""); egrTextField.textProperty().setValue(""); resaTextField.textProperty().setValue(""); blastTextField.textProperty().setValue(""); stopwayTextField.textProperty().setValue(""); clearwayTextField.textProperty().setValue(""); obstNameTextField.textProperty().setValue(""); obstHeightTextField.textProperty().setValue(""); obstLengthTextField.textProperty().setValue(""); distThreshTextField.textProperty().setValue(""); distCentreTextField.textProperty().setValue(""); } // initialise comboboxes private void createComboBoxes() { runwayPresetCombo = new ComboBox<>(); runwayPresetCombo.setPrefWidth(400); for (Runway runway : presetRunways) { runwayPresetCombo.getItems().add(runway); } runwayPresetCombo.setOnAction((event) -> { Runway runway = runwayPresetCombo.getValue(); try { populateRunwayFields(runway); } catch (NullPointerException e) { System.out.println("No presets available"); } }); if(presetRunways.size() > 0) { runwayPresetCombo.setValue(presetRunways.get(0)); populateRunwayFields(runwayPresetCombo.getValue()); } obstPresetCombo = new ComboBox<>(); obstPresetCombo.setPrefWidth(400); for (Obstruction obstruction : presetObstructions) { obstPresetCombo.getItems().add(obstruction); } obstPresetCombo.setOnAction((event) -> { Obstruction obstruction = obstPresetCombo.getValue(); try { populateObstFields(obstruction); } catch (NullPointerException e) { System.out.println("No presets available"); } }); if(presetObstructions.size() > 0) { obstPresetCombo.setValue(presetObstructions.get(0)); populateObstFields(obstPresetCombo.getValue()); } } // fill in textfields with specific values from selected runway private void populateRunwayFields(Runway runway) { nameTextField.textProperty().setValue(runway.getName()); airportTextField.textProperty().setValue(runway.getAirport()); TORATextField.textProperty().setValue(Integer.toString(runway.getTORA())); TODATextField.textProperty().setValue(Integer.toString(runway.getTODA())); ASDATextField.textProperty().setValue(Integer.toString(runway.getASDA())); LDATextField.textProperty().setValue(Integer.toString(runway.getLDA())); displacedTextField.textProperty().setValue(Integer.toString(runway.getDisplacedThreshold())); stripEndTextField.textProperty().setValue(Integer.toString(runway.getStripEnd())); egrTextField.textProperty().setValue(Integer.toString(runway.getEGR())); resaTextField.textProperty().setValue(Integer.toString(runway.getRESA())); blastTextField.textProperty().setValue(Integer.toString(runway.getBlastAllowance())); stopwayTextField.textProperty().setValue(Integer.toString(runway.getStopway())); clearwayTextField.textProperty().setValue(Integer.toString(runway.getClearway())); } // fill in textfields with values of a specific obstruction private void populateObstFields(Obstruction obstruction) { obstNameTextField.textProperty().setValue(obstruction.getName()); obstHeightTextField.textProperty().setValue(Integer.toString(obstruction.getHeight())); obstLengthTextField.textProperty().setValue(Integer.toString(obstruction.getLength())); distThreshTextField.textProperty().setValue(Integer.toString(obstruction.getDistanceFromThreshold())); distCentreTextField.textProperty().setValue(Integer.toString(obstruction.getDistanceFromCentre())); } // initialise config panel by adding all the fields private void addFieldsToConfig() { GridPane runwayConfigPane = new GridPane(); runwayConfigPane.setPadding(new Insets(10)); runwayConfigPane.setBorder(new Border(new BorderStroke(Color.SKYBLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); runwayConfigPane.setVgap(20); runwayConfigPane.setHgap(10); ColumnConstraints col1 = new ColumnConstraints(); ColumnConstraints col2 = new ColumnConstraints(); ColumnConstraints col3 = new ColumnConstraints(); ColumnConstraints col4 = new ColumnConstraints(); col1.setPercentWidth(25); col1.setHalignment(HPos.RIGHT); col2.setPercentWidth(25); col3.setPercentWidth(25); col3.setHalignment(HPos.RIGHT); col4.setPercentWidth(25); runwayConfigPane.getColumnConstraints().addAll(col1, col2, col3, col4); runwayConfigPane.add(runwayText, 0, 0); runwayConfigPane.add(runwayPresetCombo, 1, 0, 3, 1); runwayConfigPane.add(nameText, 0, 1); runwayConfigPane.add(nameTextField, 1, 1); runwayConfigPane.add(airportText, 2, 1); runwayConfigPane.add(airportTextField, 3, 1); runwayConfigPane.add(TORAText, 0,2); runwayConfigPane.add(TORATextField, 1, 2); runwayConfigPane.add(TODAText, 2, 2); runwayConfigPane.add(TODATextField, 3, 2); runwayConfigPane.add(ASDAText, 0, 3); runwayConfigPane.add(ASDATextField, 1, 3); runwayConfigPane.add(LDAText, 2, 3); runwayConfigPane.add(LDATextField, 3, 3); runwayConfigPane.add(displacedText, 0, 4); runwayConfigPane.add(displacedTextField, 1, 4); GridPane optionalGridPane = new GridPane(); optionalGridPane.getColumnConstraints().addAll(col1, col2, col3, col4); optionalGridPane.setPadding(new Insets(10)); optionalGridPane.setVgap(20); optionalGridPane.setHgap(10); optionalGridPane.add(stripEndText, 0, 0); optionalGridPane.add(stripEndTextField, 1, 0); optionalGridPane.add(egrText, 2, 0); optionalGridPane.add(egrTextField, 3, 0); optionalGridPane.add(resaText, 0, 1); optionalGridPane.add(resaTextField, 1, 1); optionalGridPane.add(blastText, 2, 1); optionalGridPane.add(blastTextField, 3, 1); optionalGridPane.add(stopwayText, 0, 2); optionalGridPane.add(stopwayTextField, 1, 2); optionalGridPane.add(clearwayText, 2, 2); optionalGridPane.add(clearwayTextField, 3, 2); TitledPane optionals = new TitledPane("Extra Parameters", optionalGridPane); runwayConfigPane.add(optionals, 0, 5, 4, 1); runwayConfigPane.add(saveRunwayPreset, 0, 6); runwayConfigPane.add(removeRunwayPreset, 1, 6, 2, 1); runwayConfigPane.add(applyRunway, 3, 6); optionals.setExpanded(false); GridPane obstConfigPane = new GridPane(); obstConfigPane.setPadding(new Insets(10)); obstConfigPane.setBorder(new Border(new BorderStroke(Color.SKYBLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, BorderWidths.DEFAULT))); obstConfigPane.setVgap(20); obstConfigPane.setHgap(10); obstConfigPane.getColumnConstraints().addAll(col1, col2, col3, col4); obstConfigPane.add(obstText, 0, 0); obstConfigPane.add(obstPresetCombo, 1, 0, 3, 1); obstConfigPane.add(obstName, 0, 1); obstConfigPane.add(obstNameTextField, 1, 1, 3, 1); obstConfigPane.add(obstHeightText, 0, 2); obstConfigPane.add(obstHeightTextField, 1, 2); obstConfigPane.add(obstLengthText, 2, 2); obstConfigPane.add(obstLengthTextField, 3, 2); obstConfigPane.add(distThreshText, 0, 3); obstConfigPane.add(distThreshTextField, 1, 3); obstConfigPane.add(distCentreText, 2, 3); obstConfigPane.add(distCentreTextField, 3, 3); obstConfigPane.add(saveObstPreset, 0, 4); obstConfigPane.add(removeObstPreset, 1, 4, 2, 1); obstConfigPane.add(applyObst, 3, 4); configWindow.getChildren().addAll(runwayConfigText, runwayConfigPane, obstConfigText, obstConfigPane); } // temp function, fill arraylists of presets private void populateDefaults() { try { importXML importXML = new importXML("src/xml/presets.xml"); presetRunways = importXML.importRunwaysFromXML(); presetObstructions = importXML.importObstructionsFromXML(); } catch (Exception e) { System.out.println(e); presetRunways = new ArrayList<>(); presetObstructions = new ArrayList<>(); } } private void configError(String errorMessage) { Alert alert = new Alert(Alert.AlertType.ERROR); alert.setTitle("Error"); alert.setHeaderText("Configuration Error"); alert.setContentText(errorMessage); alert.showAndWait(); } public Button getSaveRunwayPreset() { return saveRunwayPreset; } public Button getApplyRunway() { return applyRunway; } public Button getSaveObstPreset() { return saveObstPreset; } public Button getApplyObst() { return applyObst; } public ArrayList getPresetObstructions() { return presetObstructions; } public ArrayList getPresetRunways() { return presetRunways; } public ArrayList getRunwayIntTextFields() { return runwayIntTextFields; } public ArrayList getRunwayStringTextFields() { return runwayStringTextFields; } public ArrayList getObstIntTextFields() { return obstIntTextFields; } public ArrayList getOptionalFields() { return optionalFields; } public String getInvalidText() { return invalidText; } }