diff --git a/visualiser/src/main/java/seng302/Controllers/RaceController.java b/visualiser/src/main/java/seng302/Controllers/RaceController.java index 88915830..58d8de0c 100644 --- a/visualiser/src/main/java/seng302/Controllers/RaceController.java +++ b/visualiser/src/main/java/seng302/Controllers/RaceController.java @@ -6,81 +6,44 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.chart.LineChart; import javafx.scene.control.*; +import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; import javafx.scene.layout.Pane; import javafx.scene.layout.StackPane; -import javafx.scene.paint.Color; import seng302.Mock.StreamedRace; import seng302.Model.*; import seng302.VisualiserInput; import java.net.URL; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; import java.util.ResourceBundle; /** * Created by fwy13 on 15/03/2017. */ public class RaceController extends Controller { - @FXML GridPane canvasBase; - - //user saved data for annotation display - private ArrayList presetAnno; - private Map importantAnno; - private Map annoShownBeforeHide; - private int buttonChecked;//button currently checked allows the checkboxes to know whether or not to put it's state in history (if not hidden then store) - private int prevBtnChecked;//button to keep track of previous pressed button incase we want to check a checkbox straight from hidden we do not wish for all previous to come on. - private boolean radioBtnChecked; - private boolean selectShow = false; //button to make it so that show doesn't run the listener - - private static String nameCheckAnno = "name"; - private static String abbrevCheckAnno = "abbrev"; - private static String speedCheckAnno = "speed"; - private static String pathCheckAnno = "path"; - private static String timeCheckAnno = "time"; - private static String estTimeCheckAnno = "est time"; - - private static int noBtn = 0; - private static int hideBtn = 1; - private static int showBtn = 2; - private static int partialBtn = 3; - private static int importantBtn = 4; - - private int legNum; - private Sparkline sparkline; - private ResizableRaceCanvas raceMap; private ResizableRaceMap raceBoundaries; - private ToggleGroup annotationGroup; private RaceClock raceClock; + private Sparkline sparkline; + + private int legNum; + @FXML GridPane canvasBase; @FXML Pane arrow; @FXML SplitPane race; @FXML StackPane arrowPane; - @FXML CheckBox showFPS; - @FXML CheckBox showBoatPath; - @FXML CheckBox showName; - @FXML CheckBox showAbbrev; - @FXML CheckBox showSpeed; - @FXML CheckBox showTime; - @FXML CheckBox showEstTime; @FXML Label timer; @FXML Label FPS; @FXML Label timeZone; - @FXML Button saveAnno; + @FXML CheckBox showFPS; @FXML TableView boatInfoTable; @FXML TableColumn boatPlacingColumn; @FXML TableColumn boatTeamColumn; @FXML TableColumn boatMarkColumn; @FXML TableColumn boatSpeedColumn; - @FXML RadioButton hideAnnoRBTN; - @FXML RadioButton showAnnoRBTN; - @FXML RadioButton partialAnnoRBTN; - @FXML RadioButton importantAnnoRBTN; @FXML LineChart sparklineChart; + @FXML AnchorPane annotationPane; /** * Updates the ResizableRaceCanvas (raceMap) with most recent data @@ -123,12 +86,6 @@ public class RaceController extends Controller { FPS.setVisible(false); } }); - //adds all radios buttons for annotations to a group - annotationGroup = new ToggleGroup(); - hideAnnoRBTN.setToggleGroup(annotationGroup); - showAnnoRBTN.setToggleGroup(annotationGroup); - partialAnnoRBTN.setToggleGroup(annotationGroup); - importantAnnoRBTN.setToggleGroup(annotationGroup); } /** @@ -156,7 +113,6 @@ public class RaceController extends Controller { */ public void startRace(VisualiserInput visualiserInput, RaceClock raceClock) { - //newRace.initialiseBoats(); legNum = visualiserInput.getCourse().getLegs().size()-1; makeArrow(); @@ -165,7 +121,6 @@ public class RaceController extends Controller { raceMap.setMouseTransparent(true); raceMap.widthProperty().bind(canvasBase.widthProperty()); raceMap.heightProperty().bind(canvasBase.heightProperty()); - //raceMap.setBoats(newRace.getStartingBoats()); raceMap.draw(); raceMap.setVisible(true); raceMap.setArrow(arrow.getChildren().get(0)); @@ -183,7 +138,6 @@ public class RaceController extends Controller { race.setVisible(true); - //Initialize save annotation array, fps listener, and annotation listeners timeZone.setText(raceClock.getTimeZone()); //RaceClock.duration isn't necessarily being changed in the javaFX thread, so we need to runlater the update. @@ -197,8 +151,11 @@ public class RaceController extends Controller { raceMap.setRaceClock(raceClock); StreamedRace newRace = new StreamedRace(visualiserInput, this); + initializeFPS(); - initializeAnnotations(); + + // set up annotation displays + Annotations annotations = new Annotations(annotationPane, raceMap); new Thread((newRace)).start(); } @@ -235,188 +192,6 @@ public class RaceController extends Controller { }); } - private void storeCurrentAnnotationState(String dictionaryAnnotationKey, boolean selected){ - if (buttonChecked != hideBtn) { - //if we are checking the box straight out of hide instead of using the radio buttons - annoShownBeforeHide.put(dictionaryAnnotationKey, selected); - if (prevBtnChecked == hideBtn && buttonChecked == noBtn){ - storeCurrentAnnotationDictionary(); - } - if (buttonChecked == noBtn) { - selectShow = false; - annotationGroup.selectToggle(showAnnoRBTN); - } - } - } - - private void storeCurrentAnnotationDictionary(){ - annoShownBeforeHide.put(nameCheckAnno, showName.isSelected()); - annoShownBeforeHide.put(abbrevCheckAnno, showAbbrev.isSelected()); - annoShownBeforeHide.put(pathCheckAnno, showBoatPath.isSelected()); - annoShownBeforeHide.put(speedCheckAnno, showSpeed.isSelected()); - annoShownBeforeHide.put(timeCheckAnno, showTime.isSelected()); - annoShownBeforeHide.put(estTimeCheckAnno, showEstTime.isSelected()); - } - - /** - * Set up boat annotations - */ - private void initializeAnnotations() { - presetAnno = new ArrayList<>(); - - importantAnno = new HashMap<>(); - importantAnno.put(nameCheckAnno, false); - importantAnno.put(abbrevCheckAnno, false); - importantAnno.put(pathCheckAnno, false); - importantAnno.put(speedCheckAnno, false); - importantAnno.put(timeCheckAnno, false); - importantAnno.put(estTimeCheckAnno, false); - - annoShownBeforeHide = new HashMap<>(); - annoShownBeforeHide.put(nameCheckAnno, true); - annoShownBeforeHide.put(abbrevCheckAnno, true); - annoShownBeforeHide.put(pathCheckAnno, true); - annoShownBeforeHide.put(speedCheckAnno, true); - annoShownBeforeHide.put(timeCheckAnno, true); - annoShownBeforeHide.put(estTimeCheckAnno, true); - //listener for show name in annotation - showName.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleAnnoName(); - radioBtnChecked = false; - storeCurrentAnnotationState(nameCheckAnno, new_val); - raceMap.update(); - } - }); - //listener for show abbreviation for annotation - showAbbrev.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleAnnoAbbrev(); - radioBtnChecked = false; - storeCurrentAnnotationState(abbrevCheckAnno, new_val); - raceMap.update(); - } - }); - - //listener for show boat path for annotation - showBoatPath.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleBoatPath(); - radioBtnChecked = false; - storeCurrentAnnotationState(pathCheckAnno, new_val); - raceMap.update(); - } - }); - //listener to show speed for annotation - showSpeed.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleAnnoSpeed(); - radioBtnChecked = false; - storeCurrentAnnotationState(speedCheckAnno, new_val); - raceMap.update(); - } - }); - showTime.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleAnnoTime(); - radioBtnChecked = false; - storeCurrentAnnotationState(timeCheckAnno, new_val); - raceMap.update(); - } - }); - showEstTime.selectedProperty().addListener((ov, old_val, new_val) -> { - if (old_val != new_val) { - raceMap.toggleAnnoEstTime(); - radioBtnChecked = false; - storeCurrentAnnotationState(estTimeCheckAnno, new_val); - raceMap.update(); - } - }); - //listener to save currently selected annotation - saveAnno.setOnAction(event -> { - presetAnno.clear(); - presetAnno.add(showName.isSelected()); - presetAnno.add(showAbbrev.isSelected()); - presetAnno.add(showSpeed.isSelected()); - presetAnno.add(showBoatPath.isSelected()); - presetAnno.add(showTime.isSelected()); - presetAnno.add(showEstTime.isSelected()); - }); - //listener for hiding - hideAnnoRBTN.setOnAction((e)->{ - buttonChecked = hideBtn; - selectShow = false; - showName.setSelected(false); - showAbbrev.setSelected(false); - showBoatPath.setSelected(false); - showSpeed.setSelected(false); - showTime.setSelected(false); - showEstTime.setSelected(false); - raceMap.update(); - buttonChecked = noBtn; - prevBtnChecked = hideBtn; - selectShow = true; - }); - //listener for showing all annotations - showAnnoRBTN.setOnAction((e)->{ - if (selectShow) { - buttonChecked = showBtn; - showName.setSelected(annoShownBeforeHide.get(nameCheckAnno)); - showAbbrev.setSelected(annoShownBeforeHide.get(abbrevCheckAnno)); - showBoatPath.setSelected(annoShownBeforeHide.get(pathCheckAnno)); - showSpeed.setSelected(annoShownBeforeHide.get(speedCheckAnno)); - showTime.setSelected(annoShownBeforeHide.get(timeCheckAnno)); - showEstTime.setSelected(annoShownBeforeHide.get(estTimeCheckAnno)); - raceMap.update(); - buttonChecked = noBtn; - prevBtnChecked = showBtn; - } - selectShow = true; - }); - //listener for showing all important - partialAnnoRBTN.setOnAction((e)->{ - selectShow = false; - buttonChecked = partialBtn; - showName.setSelected(false); - showAbbrev.setSelected(true); - showSpeed.setSelected(true); - showBoatPath.setSelected(false); - showTime.setSelected(false); - showEstTime.setSelected(false); - - raceMap.update(); - buttonChecked = noBtn; - prevBtnChecked = partialBtn; - selectShow = true; - }); - //listener for showing all important - importantAnnoRBTN.setOnAction((e) ->{ - selectShow = false; - buttonChecked = importantBtn; - if (presetAnno.size() > 0) { - showName.setSelected(presetAnno.get(0)); - showAbbrev.setSelected(presetAnno.get(1)); - showSpeed.setSelected(presetAnno.get(2)); - showBoatPath.setSelected(presetAnno.get(3)); - showTime.setSelected(presetAnno.get(4)); - showEstTime.setSelected(presetAnno.get(5)); - storeCurrentAnnotationDictionary(); - raceMap.update(); - } - buttonChecked = noBtn; - prevBtnChecked = importantBtn; - selectShow = true; - }); - annotationGroup.selectToggle(showAnnoRBTN); - } - - private String colourToHex(Color color) { - return String.format( "#%02X%02X%02X", - (int)( color.getRed() * 255 ), - (int)( color.getGreen() * 255 ), - (int)( color.getBlue() * 255 ) ); - } - private void makeArrow() { arrowPane.getChildren().add(arrow); } diff --git a/visualiser/src/main/java/seng302/Model/Annotations.java b/visualiser/src/main/java/seng302/Model/Annotations.java new file mode 100644 index 00000000..de4be5c2 --- /dev/null +++ b/visualiser/src/main/java/seng302/Model/Annotations.java @@ -0,0 +1,277 @@ +package seng302.Model; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.RadioButton; +import javafx.scene.control.Toggle; +import javafx.scene.layout.AnchorPane; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class that processes user selected annotation visibility options to + * display the requested information on the race map. + */ +public class Annotations { + private ResizableRaceCanvas raceMap; + + // checkable objects in the anchor pane + private Map checkBoxes = new HashMap<>(); + private Map annoToggles = new HashMap<>(); + + // maps of selected and saved annotations + private Map importantAnno = new HashMap<>(); + private Map annoShownBeforeHide = new HashMap<>(); + + // string values match the fx:id value of check boxes + private static String nameCheckAnno = "showName"; + private static String abbrevCheckAnno = "showAbbrev"; + private static String speedCheckAnno = "showSpeed"; + private static String pathCheckAnno = "showBoatPath"; + private static String timeCheckAnno = "showTime"; + private static String estTimeCheckAnno = "showEstTime"; + + // string values match the fx:id value of radio buttons + private static String noBtn = "noBtn"; + private static String hideBtn = "hideAnnoRBtn"; + private static String showBtn = "showAnnoRBtn"; + private static String partialBtn = "partialAnnoRBtn"; + private static String importantBtn = "importantAnnoRBtn"; + + private Boolean selectShow = false; + private String buttonChecked; + private String prevBtnChecked; + @FXML Button saveAnnoBtn; + + /** + * Constructor to set up and display initial annotations + * @param annotationPane javaFX pane containing annotation options + * @param raceMap the canvas to update annotation displays + */ + public Annotations(AnchorPane annotationPane, ResizableRaceCanvas raceMap){ + this.raceMap = raceMap; + + for (Node child : annotationPane.getChildren()) { + // collect all check boxes into a map + if (child.getClass()==CheckBox.class){ + checkBoxes.put(child.getId(), (CheckBox)child); + } + // collect annotation toggle radio buttons into a map + else if (child.getClass()== RadioButton.class){ + //annotationGroup.getToggles().add((RadioButton)child); + annoToggles.put(child.getId(), (RadioButton)child); + } + else if (child.getClass() == Button.class){ + saveAnnoBtn = (Button)child; + } + } + initializeAnnotations(); + } + + + /** + * Set up initial boat annotations and shows all data. + * Defines partial annotations. + * Creates listeners for when the user selects a different annotation + * visibility. + */ + public void initializeAnnotations() { + for (Map.Entry checkBox : checkBoxes.entrySet()) + { annoShownBeforeHide.put(checkBox.getKey(), true); } + + addCheckBoxListeners(); + addSaveAnnoListener(); + addAnnoToggleListeners(); + + annoToggles.get(showBtn).setSelected(true); + } + + /** + * Creates listeners for each checkbox so the annotation display is + * updated when a user selects a different level of annotation visibility. + */ + private void addCheckBoxListeners(){ + //listener for show name in annotation + checkBoxes.get(nameCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleAnnoName(); + storeCurrentAnnotationState(nameCheckAnno, new_val); + raceMap.update(); + } + }); + + //listener for show abbreviation for annotation + checkBoxes.get(abbrevCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleAnnoAbbrev(); + storeCurrentAnnotationState(abbrevCheckAnno, new_val); + raceMap.update(); + } + }); + + //listener for show boat path for annotation + checkBoxes.get(pathCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleBoatPath(); + storeCurrentAnnotationState(pathCheckAnno, new_val); + raceMap.update(); + } + }); + + //listener to show speed for annotation + checkBoxes.get(speedCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleAnnoSpeed(); + storeCurrentAnnotationState(speedCheckAnno, new_val); + raceMap.update(); + } + }); + + //listener to show time for annotation + checkBoxes.get(timeCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleAnnoTime(); + storeCurrentAnnotationState(timeCheckAnno, new_val); + raceMap.update(); + } + }); + + //listener to show estimated time for annotation + checkBoxes.get(estTimeCheckAnno).selectedProperty() + .addListener((ov, old_val, new_val) -> { + if (old_val != new_val) { + raceMap.toggleAnnoEstTime(); + storeCurrentAnnotationState(estTimeCheckAnno, new_val); + raceMap.update(); + } + }); + } + + /** + * Creates a listener so the system knows when to save a users currently + * selected annotation options as important for future use. + */ + private void addSaveAnnoListener(){ + //listener to save currently selected annotations as important + saveAnnoBtn.setOnAction(event -> { + importantAnno.clear(); + for (Map.Entry checkBox : checkBoxes.entrySet()){ + importantAnno.put(checkBox.getKey(), + checkBox.getValue().isSelected()); + } + }); + } + + /** + * Creates listeners for each visibility option so that the annotation + * display is updated when a user selects a different level of annotation + * visibility. + */ + private void addAnnoToggleListeners(){ + //listener for hiding all annotations + RadioButton hideAnnoRBtn = (RadioButton)annoToggles.get(hideBtn); + hideAnnoRBtn.setOnAction((e)->{ + buttonChecked = hideBtn; + selectShow = false; + for (Map.Entry checkBox : checkBoxes.entrySet()){ + checkBox.getValue().setSelected(false); + } + raceMap.update(); + buttonChecked = noBtn; + prevBtnChecked = hideBtn; + selectShow = true; + }); + + //listener for showing previously visible annotations + RadioButton showAnnoRBTN = (RadioButton)annoToggles.get(showBtn); + showAnnoRBTN.setOnAction((e)->{ + if (selectShow) { + buttonChecked = showBtn; + for (Map.Entry checkBox : checkBoxes.entrySet()){ + checkBox.getValue().setSelected( + annoShownBeforeHide.get(checkBox.getKey())); + } + raceMap.update(); + buttonChecked = noBtn; + prevBtnChecked = showBtn; + } + selectShow = true; + }); + + //listener for showing a predetermined subset of annotations + RadioButton partialAnnoRBTN = (RadioButton)annoToggles.get(partialBtn); + partialAnnoRBTN.setOnAction((e)->{ + selectShow = false; + buttonChecked = partialBtn; + for (Map.Entry checkBox : checkBoxes.entrySet()){ + // the checkbox defaults for partial annotations + if (checkBox.getKey().equals(abbrevCheckAnno) + || checkBox.getKey().equals(speedCheckAnno)){ + checkBox.getValue().setSelected(true); + } + else { checkBox.getValue().setSelected(false); } + } + raceMap.update(); + buttonChecked = noBtn; + prevBtnChecked = partialBtn; + selectShow = true; + }); + + //listener for showing all saved important annotations + RadioButton importantAnnoRBTN = (RadioButton)annoToggles.get(importantBtn); + importantAnnoRBTN.setOnAction((e) ->{ + selectShow = false; + buttonChecked = importantBtn; + if (importantAnno.size()>0){ + for (Map.Entry checkBox : checkBoxes.entrySet()){ + checkBox.getValue().setSelected + (importantAnno.get(checkBox.getKey())); + } + } + buttonChecked = noBtn; + prevBtnChecked = importantBtn; + selectShow = true; + }); + } + + /** + * Updates the current state of an annotation so that when a user + * deselects the hidden visibility option, the previous annotations will + * be displayed again. + * @param dictionaryAnnotationKey annotation checkbox + * @param selected boolean value representing the state of the checkbox + */ + private void storeCurrentAnnotationState(String dictionaryAnnotationKey, boolean selected){ + if (buttonChecked != hideBtn) { + //if we are checking the box straight out of hide instead of using the radio buttons + annoShownBeforeHide.put(dictionaryAnnotationKey, selected); + if (prevBtnChecked == hideBtn && buttonChecked == noBtn){ + storeCurrentAnnotationDictionary(); + } + if (buttonChecked == noBtn) { + selectShow = false; + annoToggles.get(showBtn).setSelected(true); + } + } + } + + /** + * Stores all current annotation states so that when a user + * deselects the hidden visibility option, the previous annotations will + * be displayed again. + */ + private void storeCurrentAnnotationDictionary(){ + for (Map.Entry checkBox : checkBoxes.entrySet()){ + annoShownBeforeHide.put(checkBox.getKey(), + checkBox.getValue().isSelected()); + } + } +} diff --git a/visualiser/src/main/resources/scenes/race.fxml b/visualiser/src/main/resources/scenes/race.fxml index 5f11b477..97168ac1 100644 --- a/visualiser/src/main/resources/scenes/race.fxml +++ b/visualiser/src/main/resources/scenes/race.fxml @@ -3,25 +3,10 @@ - - - - - - - - - - - - - - - - + + - - + @@ -38,7 +23,7 @@ - + @@ -48,11 +33,14 @@