Commit dd1ce7fe83362fb902f85183684bac14c6d6ed9c
1 parent
78935bd622
Exists in
master
and in
1 other branch
add button to switch between real time and static mode
Showing 9 changed files with 70 additions and 25 deletions Inline Diff
- src/main/kotlin/application/controller/MapPanelController.kt
- src/main/kotlin/application/controller/MenuBarController.kt
- src/main/kotlin/application/controller/TimePanel.kt
- src/main/kotlin/application/controller/VesselListPanelController.kt
- src/main/kotlin/application/model/ObservableReplayState.kt
- src/main/kotlin/application/model/ObservableSelectedVessel.kt
- src/main/kotlin/application/model/ObservableVessel.kt
- src/main/kotlin/map/MapDisplayer.kt
- src/main/resources/gui/menuBar.fxml
src/main/kotlin/application/controller/MapPanelController.kt
View file @
dd1ce7f
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.* | 3 | 3 | import application.model.* | |
| import application.model.MapState.* | 4 | 4 | import application.model.MapState.* | |
| import javafx.fxml.FXML | 5 | 5 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 6 | 6 | import javafx.fxml.Initializable | |
| import javafx.scene.layout.StackPane | 7 | 7 | import javafx.scene.layout.StackPane | |
| import map.* | 8 | 8 | import map.* | |
| import java.net.URL | 9 | 9 | import java.net.URL | |
| import java.util.* | 10 | 10 | import java.util.* | |
| 11 | 11 | |||
| class MapPanelController : Initializable { | 12 | 12 | class MapPanelController : Initializable { | |
| 13 | 13 | |||
| @FXML | 14 | 14 | @FXML | |
| private lateinit var map: StackPane | 15 | 15 | private lateinit var map: StackPane | |
| 16 | 16 | |||
| private val mapView = LeafletMapView() | 17 | 17 | private val mapView = LeafletMapView() | |
| 18 | 18 | |||
| 19 | ||||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 20 | 19 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| mapView.displayMap(MapConfig()) | 21 | 20 | mapView.displayMap(MapConfig()) | |
| setObservableVesselListener() | 22 | 21 | setObservableVesselListener() | |
| setObservableSelectedVesselListener() | 23 | 22 | setObservableSelectedVesselListener() | |
| setStateListener() | 24 | 23 | setStateListener() | |
| 24 | setObservableIsReplayState() | |||
| observableCurrentTime() | 25 | 25 | observableCurrentTime() | |
| 26 | 26 | |||
| map.children.add(mapView) | 27 | 27 | map.children.add(mapView) | |
| map.children | 28 | 28 | map.children | |
| } | 29 | 29 | } | |
| 30 | 30 | |||
| private fun setStateListener() { | 31 | 31 | private fun setStateListener() { | |
| observableMapState.listeners.add(object : StateListener { | 32 | 32 | observableMapState.listeners.add(object : StateListener { | |
| override fun onValueChanged(newValue: MapState) { | 33 | 33 | override fun onValueChanged(newValue: MapState) { | |
| if (observableSelectedVessel.vessel.mmsi != null) { | 34 | 34 | if (observableSelectedVessel.value.mmsi != null) { | |
| updateMap(observableSelectedVessel.vessel.mmsi!!) | 35 | 35 | updateMap(observableSelectedVessel.value.mmsi!!) | |
| } else { | 36 | 36 | } else { | |
| updateMap() | 37 | 37 | updateMap() | |
| } | 38 | 38 | } | |
| } | 39 | 39 | } | |
| }) | 40 | 40 | }) | |
| } | 41 | 41 | } | |
| 42 | 42 | |||
| private fun observableCurrentTime() { | 43 | 43 | private fun observableCurrentTime() { | |
| observableCurrentTime.listeners.add(object : CurrentTime { | 44 | 44 | observableCurrentTime.listeners.add(object : CurrentTime { | |
| override fun onValueChanged(newValue: Int) { | 45 | 45 | override fun onValueChanged(newValue: Int) { | |
| if (observableSelectedVessel.vessel.mmsi != null) { | 46 | 46 | if (observableSelectedVessel.value.mmsi != null) { | |
| updateMap(observableSelectedVessel.vessel.mmsi!!) | 47 | 47 | updateMap(observableSelectedVessel.value.mmsi!!) | |
| } else { | 48 | 48 | } else { | |
| updateMap() | 49 | 49 | updateMap() | |
| } | 50 | 50 | } | |
| } | 51 | 51 | } | |
| }) | 52 | 52 | }) | |
| } | 53 | 53 | } | |
| 54 | 54 | |||
| private fun updateMap() { | 55 | 55 | private fun updateMap() { | |
| if (observableIsReplayState.value) { | 56 | 56 | if (observableIsReplayState.value) { | |
| when (observableMapState.state) { | 57 | 57 | when (observableMapState.state) { | |
| ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView) | 58 | 58 | ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView) | |
| CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView) | 59 | 59 | CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView) | |
| HEAT_MAP -> displayTimedHeatMapOnMap(mapView) | 60 | 60 | HEAT_MAP -> displayTimedHeatMapOnMap(mapView) | |
| } | 61 | 61 | } | |
| } else { | 62 | 62 | } else { | |
| when (observableMapState.state) { | 63 | 63 | when (observableMapState.state) { | |
| ALL_MESSAGES -> displayAllMessageOnMap(mapView) | 64 | 64 | ALL_MESSAGES -> displayAllMessageOnMap(mapView) | |
| CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) | 65 | 65 | CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) | |
| HEAT_MAP -> displayHeatMapOnMap(mapView) | 66 | 66 | HEAT_MAP -> displayHeatMapOnMap(mapView) | |
| } | 67 | 67 | } | |
| } | 68 | 68 | } | |
| } | 69 | 69 | } | |
| 70 | 70 | |||
| private fun updateMap(selectedMMSI: String) { | 71 | 71 | private fun updateMap(selectedMMSI: String) { | |
| if (observableIsReplayState.value) { | 72 | 72 | if (observableIsReplayState.value) { | |
| when (observableMapState.state) { | 73 | 73 | when (observableMapState.state) { | |
| ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView, selectedMMSI) | 74 | 74 | ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView, selectedMMSI) | |
| CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView, selectedMMSI) | 75 | 75 | CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView, selectedMMSI) | |
| HEAT_MAP -> displayTimedHeatMapOnMap(mapView, selectedMMSI) | 76 | 76 | HEAT_MAP -> displayTimedHeatMapOnMap(mapView, selectedMMSI) | |
| } | 77 | 77 | } | |
| } else { | 78 | 78 | } else { | |
| when (observableMapState.state) { | 79 | 79 | when (observableMapState.state) { | |
| ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) | 80 | 80 | ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) | |
| CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) | 81 | 81 | CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) | |
| HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) | 82 | 82 | HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) | |
| } | 83 | 83 | } | |
| } | 84 | 84 | } | |
| } | 85 | 85 | } | |
| 86 | 86 | |||
| private fun setObservableVesselListener() { | 87 | 87 | private fun setObservableVesselListener() { | |
| observableVessel.listeners.add(object : MessageListener { | 88 | 88 | observableVessel.listeners.add(object : MessageListener { | |
| override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 89 | 89 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
| updateMap() | 90 | 90 | updateMap() | |
| 91 | } | |||
| 92 | }) | |||
| 93 | } | |||
| 94 | ||||
| 95 | private fun setObservableIsReplayState() { | |||
| 96 | observableIsReplayState.listeners.add(object: ReplayState { | |||
| 97 | override fun onValueChanged(newValue: Boolean) { | |||
| 98 | if (observableSelectedVessel.value.mmsi != null) { | |||
| 99 | updateMap(observableSelectedVessel.value.mmsi!!) | |||
| 100 | } else { | |||
| 101 | updateMap() | |||
| 102 | } | |||
| } | 91 | 103 | } | |
| }) | 92 | 104 | }) | |
| } | 93 | 105 | } | |
| 94 | 106 | |||
| private fun setObservableSelectedVesselListener() { | 95 | 107 | private fun setObservableSelectedVesselListener() { | |
| observableSelectedVessel.listeners.add(object : SelectedVesselListener { | 96 | 108 | observableSelectedVessel.listeners.add(object : SelectedVesselListener { | |
| override fun onValueChanged(newValue: Vessel) { | 97 | 109 | override fun onValueChanged(newValue: Vessel) { |
src/main/kotlin/application/controller/MenuBarController.kt
View file @
dd1ce7f
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.MapState.* | 3 | 3 | import application.model.MapState.* | |
| import application.model.createVesselCollection | 4 | 4 | import application.model.createVesselCollection | |
| 5 | import application.model.observableIsReplayState | |||
| import application.model.observableMapState | 5 | 6 | import application.model.observableMapState | |
| import application.model.observableVessel | 6 | 7 | import application.model.observableVessel | |
| import javafx.event.EventHandler | 7 | 8 | import javafx.event.EventHandler | |
| import javafx.fxml.FXML | 8 | 9 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 9 | 10 | import javafx.fxml.Initializable | |
| import javafx.scene.control.Alert | 10 | 11 | import javafx.scene.control.* | |
| import javafx.scene.control.CheckMenuItem | 11 | |||
| import javafx.scene.control.MenuBar | 12 | |||
| import javafx.scene.control.MenuItem | 13 | |||
| import javafx.stage.FileChooser | 14 | 12 | import javafx.stage.FileChooser | |
| import java.net.URL | 15 | 13 | import java.net.URL | |
| import java.util.* | 16 | 14 | import java.util.* | |
| 17 | 15 | |||
| class MenuBarController : Initializable { | 18 | 16 | class MenuBarController : Initializable { | |
| 19 | 17 | |||
| @FXML | 20 | 18 | @FXML | |
| var menuBar: MenuBar = MenuBar() | 21 | 19 | var menuBar: MenuBar = MenuBar() | |
| 22 | 20 | |||
| @FXML | 23 | 21 | @FXML | |
| var import: MenuItem = MenuItem() | 24 | 22 | var import: MenuItem = MenuItem() | |
| 25 | 23 | |||
| @FXML | 26 | 24 | @FXML | |
| var allMessages: CheckMenuItem = CheckMenuItem() | 27 | 25 | var allMessages: CheckMenuItem = CheckMenuItem() | |
| 28 | 26 | |||
| @FXML | 29 | 27 | @FXML | |
| var clusteredMessage: CheckMenuItem = CheckMenuItem() | 30 | 28 | var clusteredMessage: CheckMenuItem = CheckMenuItem() | |
| 31 | 29 | |||
| @FXML | 32 | 30 | @FXML | |
| var heatMap: CheckMenuItem = CheckMenuItem() | 33 | 31 | var heatMap: CheckMenuItem = CheckMenuItem() | |
| 34 | 32 | |||
| 33 | @FXML | |||
| 34 | var activateReplayButton: RadioMenuItem = RadioMenuItem() | |||
| 35 | ||||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 35 | 36 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| 36 | 37 | |||
| setOnActionImportButton() | 37 | 38 | setOnActionImportButton() | |
| 38 | 39 | |||
| setOnActionAllMessageButton() | 39 | 40 | setOnActionAllMessageButton() | |
| setOnActionClusteredMessageButton() | 40 | 41 | setOnActionClusteredMessageButton() | |
| setOnActionHeatMapButton() | 41 | 42 | setOnActionHeatMapButton() | |
| 43 | setOnActionActivateReplayButton() | |||
| observableMapState.state = CLUSTERED_MESSAGES | 42 | 44 | observableMapState.state = CLUSTERED_MESSAGES | |
| allMessages.isSelected = false | 43 | 45 | allMessages.isSelected = false | |
| clusteredMessage.isSelected = true | 44 | 46 | clusteredMessage.isSelected = true | |
| heatMap.isSelected = false | 45 | 47 | heatMap.isSelected = false | |
| 46 | 48 | |||
| 49 | } | |||
| 50 | ||||
| 51 | private fun setOnActionActivateReplayButton() { | |||
| 52 | activateReplayButton.onAction = EventHandler { | |||
| 53 | observableIsReplayState.value = activateReplayButton.isSelected | |||
| 54 | } | |||
| } | 47 | 55 | } | |
| 48 | 56 | |||
| private fun setOnActionImportButton() { | 49 | 57 | private fun setOnActionImportButton() { | |
| import.onAction = EventHandler { | 50 | 58 | import.onAction = EventHandler { | |
| val fileChooser = FileChooser() | 51 | 59 | val fileChooser = FileChooser() | |
| fileChooser.title = "Choose a file to import" | 52 | 60 | fileChooser.title = "Choose a file to import" | |
| val window = menuBar.scene.window | 53 | 61 | val window = menuBar.scene.window | |
| val file = fileChooser.showOpenDialog(window) | 54 | 62 | val file = fileChooser.showOpenDialog(window) | |
| try { | 55 | 63 | try { | |
| if (file.extension != "csv") { | 56 | 64 | if (file.extension != "csv") { | |
| val alert = Alert(Alert.AlertType.WARNING) | 57 | 65 | val alert = Alert(Alert.AlertType.WARNING) | |
| alert.title = "Warning Alert" | 58 | 66 | alert.title = "Warning Alert" | |
| alert.headerText = "Wrong file format." | 59 | 67 | alert.headerText = "Wrong file format." | |
| alert.contentText = "Please choose à .csv file." | 60 | 68 | alert.contentText = "Please choose à .csv file." | |
| alert.showAndWait() | 61 | 69 | alert.showAndWait() | |
| } | 62 | 70 | } | |
| val vessels = createVesselCollection(file) | 63 | 71 | val vessels = createVesselCollection(file) | |
| observableVessel.vessels.clear() | 64 | 72 | observableVessel.vessels.clear() | |
| observableVessel.vessels = vessels | 65 | 73 | observableVessel.vessels = vessels | |
| } catch (ignore: IllegalStateException) { | 66 | 74 | } catch (ignore: IllegalStateException) { | |
| 67 | 75 | |||
| } | 68 | 76 | } | |
| } | 69 | 77 | } | |
| } | 70 | 78 | } | |
| 71 | 79 | |||
| private fun setOnActionAllMessageButton() { | 72 | 80 | private fun setOnActionAllMessageButton() { | |
| allMessages.onAction = EventHandler { | 73 | 81 | allMessages.onAction = EventHandler { | |
| observableMapState.state = ALL_MESSAGES | 74 | 82 | observableMapState.state = ALL_MESSAGES | |
| allMessages.isSelected = true | 75 | 83 | allMessages.isSelected = true | |
| clusteredMessage.isSelected = false | 76 | 84 | clusteredMessage.isSelected = false | |
| heatMap.isSelected = false | 77 | 85 | heatMap.isSelected = false | |
| } | 78 | 86 | } | |
| } | 79 | 87 | } | |
| 80 | 88 | |||
| private fun setOnActionClusteredMessageButton() { | 81 | 89 | private fun setOnActionClusteredMessageButton() { | |
| clusteredMessage.onAction = EventHandler { | 82 | 90 | clusteredMessage.onAction = EventHandler { | |
| observableMapState.state = CLUSTERED_MESSAGES | 83 | 91 | observableMapState.state = CLUSTERED_MESSAGES |
src/main/kotlin/application/controller/TimePanel.kt
View file @
dd1ce7f
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.* | 3 | 3 | import application.model.* | |
| import javafx.fxml.FXML | 4 | 4 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 5 | 5 | import javafx.fxml.Initializable | |
| import javafx.scene.control.Button | 6 | 6 | import javafx.scene.control.Button | |
| import javafx.scene.control.Slider | 7 | 7 | import javafx.scene.control.Slider | |
| import java.net.URL | 8 | 8 | import java.net.URL | |
| import java.util.* | 9 | 9 | import java.util.* | |
| 10 | 10 | |||
| 11 | 11 | |||
| class TimePanel : Initializable { | 12 | 12 | class TimePanel : Initializable { | |
| 13 | 13 | |||
| @FXML | 14 | 14 | @FXML | |
| var timeSlider = Slider() | 15 | 15 | var timeSlider = Slider() | |
| 16 | 16 | |||
| @FXML | 17 | 17 | @FXML | |
| var timeStop = Button() | 18 | 18 | var timeStop = Button() | |
| 19 | 19 | |||
| @FXML | 20 | 20 | @FXML | |
| var timePlay = Button() | 21 | 21 | var timePlay = Button() | |
| 22 | 22 | |||
| 23 | 23 | |||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 24 | 24 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| setSliderMinMax() | 25 | 25 | setSliderMinMax() | |
| setSliderListener() | 26 | 26 | setSliderListener() | |
| 27 | 27 | disableAllButton() | ||
| 28 | 28 | setDisable() | ||
| } | 29 | 29 | } | |
| 30 | 30 | |||
| private fun setSliderMinMax() { | 31 | 31 | private fun setSliderMinMax() { | |
| timeSlider.min = 0.0 | 32 | 32 | timeSlider.min = 0.0 | |
| timeSlider.max = 0.0 | 33 | 33 | timeSlider.max = 0.0 | |
| observableVessel.listeners.add(object : MessageListener{ | 34 | 34 | observableVessel.listeners.add(object : MessageListener { | |
| override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 35 | 35 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
| timeSlider.max = Vessel.maxTime.toDouble() | 36 | 36 | timeSlider.max = Vessel.maxTime.toDouble() | |
| timeSlider.min = Vessel.minTime.toDouble() | 37 | 37 | timeSlider.min = Vessel.minTime.toDouble() | |
| } | 38 | 38 | } | |
| }) | 39 | 39 | }) | |
| } | 40 | 40 | } | |
| 41 | 41 | |||
| private fun setSliderListener() { | 42 | 42 | private fun setSliderListener() { | |
| timeSlider.valueProperty().addListener { _, _, newValue -> | 43 | 43 | timeSlider.valueProperty().addListener { _, _, newValue -> | |
| observableCurrentTime.value = newValue.toInt() | 44 | 44 | observableCurrentTime.value = newValue.toInt() | |
| } | 45 | 45 | } | |
| 46 | } | |||
| 47 | ||||
| 48 | private fun disableAllButton() { | |||
| 49 | timeStop.isDisable = true | |||
| 50 | timeSlider.isDisable = true | |||
| 51 | timePlay.isDisable = true | |||
| 52 | } | |||
| 53 | ||||
| 54 | private fun unableAllButton() { | |||
| 55 | timeStop.isDisable = false | |||
| 56 | timeSlider.isDisable = false | |||
| 57 | timePlay.isDisable = false | |||
| 58 | } | |||
| 59 | ||||
| 60 | private fun setDisable() { | |||
| 61 | observableIsReplayState.listeners.add(object : ReplayState { | |||
| 62 | override fun onValueChanged(newValue: Boolean) { |
src/main/kotlin/application/controller/VesselListPanelController.kt
View file @
dd1ce7f
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.MessageListener | 3 | 3 | import application.model.MessageListener | |
| import application.model.Vessel | 4 | 4 | import application.model.Vessel | |
| import application.model.observableSelectedVessel | 5 | 5 | import application.model.observableSelectedVessel | |
| import application.model.observableVessel | 6 | 6 | import application.model.observableVessel | |
| import javafx.collections.FXCollections | 7 | 7 | import javafx.collections.FXCollections | |
| import javafx.collections.ObservableList | 8 | 8 | import javafx.collections.ObservableList | |
| import javafx.fxml.FXML | 9 | 9 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 10 | 10 | import javafx.fxml.Initializable | |
| import javafx.scene.control.ListCell | 11 | 11 | import javafx.scene.control.ListCell | |
| import javafx.scene.control.ListView | 12 | 12 | import javafx.scene.control.ListView | |
| import javafx.scene.control.MultipleSelectionModel | 13 | 13 | import javafx.scene.control.MultipleSelectionModel | |
| import javafx.scene.control.SelectionMode | 14 | 14 | import javafx.scene.control.SelectionMode | |
| import javafx.scene.input.MouseEvent | 15 | 15 | import javafx.scene.input.MouseEvent | |
| import java.net.URL | 16 | 16 | import java.net.URL | |
| import java.util.* | 17 | 17 | import java.util.* | |
| 18 | 18 | |||
| 19 | 19 | |||
| class VesselListPanelController : Initializable, MessageListener { | 20 | 20 | class VesselListPanelController : Initializable, MessageListener { | |
| @FXML | 21 | 21 | @FXML | |
| var shipListView: ListView<String?> = ListView() | 22 | 22 | var shipListView: ListView<String?> = ListView() | |
| 23 | 23 | |||
| private var shipList: ObservableList<String?> = FXCollections.observableArrayList() | 24 | 24 | private var shipList: ObservableList<String?> = FXCollections.observableArrayList() | |
| 25 | 25 | |||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 26 | 26 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| 27 | 27 | |||
| 28 | 28 | |||
| shipListView.items = shipList | 29 | 29 | shipListView.items = shipList | |
| observableVessel.listeners.add(this) | 30 | 30 | observableVessel.listeners.add(this) | |
| shipListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | 31 | 31 | shipListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | |
| if (newValue == null) { | 32 | 32 | if (newValue == null) { | |
| observableSelectedVessel.vessel = Vessel(null) | 33 | 33 | observableSelectedVessel.value = Vessel(null) | |
| } else { | 34 | 34 | } else { | |
| observableSelectedVessel.vessel = observableVessel.vessels[newValue]!! | 35 | 35 | observableSelectedVessel.value = observableVessel.vessels[newValue]!! | |
| } | 36 | 36 | } | |
| } | 37 | 37 | } | |
| setCellFactory() | 38 | 38 | setCellFactory() | |
| } | 39 | 39 | } | |
| 40 | 40 | |||
| override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 41 | 41 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
| shipList.clear() | 42 | 42 | shipList.clear() | |
| shipList.addAll(newValue.keys) | 43 | 43 | shipList.addAll(newValue.keys) | |
| } | 44 | 44 | } | |
| 45 | 45 | |||
| private fun setCellFactory() { | 46 | 46 | private fun setCellFactory() { | |
| val selectionModel: MultipleSelectionModel<String?>? = shipListView.selectionModel | 47 | 47 | val selectionModel: MultipleSelectionModel<String?>? = shipListView.selectionModel | |
| selectionModel?.selectionMode = SelectionMode.SINGLE | 48 | 48 | selectionModel?.selectionMode = SelectionMode.SINGLE | |
| shipListView.setCellFactory { | 49 | 49 | shipListView.setCellFactory { | |
| val cell = ListCell<String?>() | 50 | 50 | val cell = ListCell<String?>() | |
| cell.textProperty().bind(cell.itemProperty()) | 51 | 51 | cell.textProperty().bind(cell.itemProperty()) | |
| cell.addEventFilter(MouseEvent.MOUSE_PRESSED) { event: MouseEvent -> | 52 | 52 | cell.addEventFilter(MouseEvent.MOUSE_PRESSED) { event: MouseEvent -> | |
| shipListView.requestFocus() | 53 | 53 | shipListView.requestFocus() | |
| if (!cell.isEmpty) { | 54 | 54 | if (!cell.isEmpty) { | |
| val index = cell.index | 55 | 55 | val index = cell.index | |
| if (selectionModel!!.selectedIndices.contains(index)) { | 56 | 56 | if (selectionModel!!.selectedIndices.contains(index)) { | |
| selectionModel.clearSelection() | 57 | 57 | selectionModel.clearSelection() | |
| } else { | 58 | 58 | } else { | |
| selectionModel.select(index) | 59 | 59 | selectionModel.select(index) | |
| } | 60 | 60 | } |
src/main/kotlin/application/model/ObservableReplayState.kt
View file @
dd1ce7f
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| import kotlin.properties.Delegates | 3 | 3 | import kotlin.properties.Delegates | |
| 4 | 4 | |||
| class ObservableReplayState { | 5 | 5 | class ObservableReplayState { | |
| 6 | 6 | |||
| val listeners: MutableList<ReplayState> = mutableListOf() | 7 | 7 | val listeners: MutableList<ReplayState> = mutableListOf() | |
| 8 | 8 | |||
| var value: Boolean by Delegates.observable( | 9 | 9 | var value: Boolean by Delegates.observable( | |
| initialValue = true, | 10 | 10 | initialValue = false, | |
| onChange = { _, _, new -> | 11 | 11 | onChange = { _, _, new -> | |
| run { | 12 | 12 | run { | |
| listeners.forEach { | 13 | 13 | listeners.forEach { | |
| it.onValueChanged(new) | 14 | 14 | it.onValueChanged(new) | |
| } | 15 | 15 | } | |
| } | 16 | 16 | } |
src/main/kotlin/application/model/ObservableSelectedVessel.kt
View file @
dd1ce7f
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| import kotlin.properties.Delegates | 3 | 3 | import kotlin.properties.Delegates | |
| 4 | 4 | |||
| class ObservableSelectedVessel { | 5 | 5 | class ObservableSelectedVessel { | |
| val listeners: MutableList<SelectedVesselListener> = mutableListOf() | 6 | 6 | val listeners: MutableList<SelectedVesselListener> = mutableListOf() | |
| 7 | 7 | |||
| var vessel: Vessel by Delegates.observable( | 8 | 8 | var value: Vessel by Delegates.observable( | |
| initialValue = Vessel(null), | 9 | 9 | initialValue = Vessel(null), | |
| onChange = { _, _, new -> | 10 | 10 | onChange = { _, _, new -> | |
| run { | 11 | 11 | run { | |
| listeners.forEach { | 12 | 12 | listeners.forEach { | |
| it.onValueChanged(new) | 13 | 13 | it.onValueChanged(new) | |
| } | 14 | 14 | } | |
| } | 15 | 15 | } | |
| } | 16 | 16 | } |
src/main/kotlin/application/model/ObservableVessel.kt
View file @
dd1ce7f
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| import kotlin.properties.Delegates | 3 | 3 | import kotlin.properties.Delegates | |
| 4 | 4 | |||
| class ObservableVessel { | 5 | 5 | class ObservableVessel { | |
| val listeners: MutableList<MessageListener> = mutableListOf() | 6 | 6 | val listeners: MutableList<MessageListener> = mutableListOf() | |
| 7 | 7 | |||
| var vessels: MutableMap<String?, Vessel> by Delegates.observable( | 8 | 8 | var vessels: MutableMap<String?, Vessel> by Delegates.observable( | |
| initialValue = mutableMapOf(), | 9 | 9 | initialValue = mutableMapOf(), | |
| onChange = { _, _, new -> | 10 | 10 | onChange = { _, _, new -> | |
| run { | 11 | 11 | run { | |
| observableSelectedVessel.vessel = Vessel(null) | 12 | 12 | observableSelectedVessel.value = Vessel(null) | |
| listeners.forEach { | 13 | 13 | listeners.forEach { | |
| it.onValueChanged(new) | 14 | 14 | it.onValueChanged(new) | |
| } | 15 | 15 | } | |
| } | 16 | 16 | } | |
| } | 17 | 17 | } |
src/main/kotlin/map/MapDisplayer.kt
View file @
dd1ce7f
| package map | 1 | 1 | package map | |
| 2 | 2 | |||
| import application.model.observableVessel | 3 | 3 | import application.model.observableVessel | |
| 4 | 4 | |||
| fun clearMap(map: LeafletMapView) { | 5 | 5 | fun clearMap(map: LeafletMapView) { | |
| clearMapCanvas(map) | 6 | 6 | clearMapCanvas(map) | |
| clearMapCluster(map) | 7 | 7 | clearMapCluster(map) | |
| clearHeatMap(map) | 8 | 8 | clearHeatMap(map) | |
| clearMarker(map) | 9 | 9 | clearMarker(map) | |
| } | 10 | 10 | } | |
| 11 | 11 | |||
| fun clearMapCluster(map: LeafletMapView) { | 12 | 12 | fun clearMapCluster(map: LeafletMapView) { | |
| map.execScript( | 13 | 13 | map.execScript( | |
| """ | 14 | 14 | """ | |
| |myMap.removeLayer(markerClusters); | 15 | 15 | |myMap.removeLayer(markerClusters); | |
| |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | 16 | 16 | |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | |
| """.trimMargin() | 17 | 17 | """.trimMargin() | |
| ) | 18 | 18 | ) | |
| } | 19 | 19 | } | |
| 20 | 20 | |||
| fun clearMapCanvas(map: LeafletMapView) { | 21 | 21 | fun clearMapCanvas(map: LeafletMapView) { | |
| map.execScript( | 22 | 22 | map.execScript( | |
| """ | 23 | 23 | """ | |
| |myRenderer.removeFrom(myMap); | 24 | 24 | |myRenderer.removeFrom(myMap); | |
| |var myRenderer = L.canvas({ padding: 0.5 }); | 25 | 25 | |var myRenderer = L.canvas({ padding: 0.5 }); | |
| """.trimMargin() | 26 | 26 | """.trimMargin() | |
| ) | 27 | 27 | ) | |
| } | 28 | 28 | } | |
| 29 | 29 | |||
| fun clearHeatMap(map: LeafletMapView) { | 30 | 30 | fun clearHeatMap(map: LeafletMapView) { | |
| map.execScript( | 31 | 31 | map.execScript( | |
| """ | 32 | 32 | """ | |
| |heatLayer.removeFrom(myMap); | 33 | 33 | |heatLayer.removeFrom(myMap); | |
| |var heatLayer = L.heatLayer([]).addTo(myMap); | 34 | 34 | |var heatLayer = L.heatLayer([]).addTo(myMap); | |
| """.trimMargin() | 35 | 35 | """.trimMargin() | |
| ) | 36 | 36 | ) | |
| } | 37 | 37 | } | |
| 38 | 38 | |||
| fun clearMarker(map: LeafletMapView) { | 39 | 39 | fun clearMarker(map: LeafletMapView) { | |
| map.execScript( | 40 | 40 | map.execScript( | |
| """ | 41 | 41 | """ | |
| |for(var i = 0; i < markers.length; i++){ | 42 | 42 | |for(var i = 0; i < markers.length; i++){ | |
| |myMap.removeLayer(markers[i]); | 43 | 43 | |myMap.removeLayer(markers[i]); | |
| |} | 44 | 44 | |} | |
| |markers = [] | 45 | 45 | |markers = [] | |
| """.trimMargin() | 46 | 46 | """.trimMargin() | |
| ) | 47 | 47 | ) | |
| } | 48 | 48 | } | |
| 49 | 49 | |||
| fun displayAllMessageOnMap(map: LeafletMapView) { | 50 | 50 | fun displayAllMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 51 | 51 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 52 | 52 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 53 | 53 | value.messages.forEach { (_, message) -> | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | 54 | 54 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | |
| } | 55 | 55 | } | |
| } | 56 | 56 | } | |
| } | 57 | 57 | } | |
| 58 | 58 | |||
| fun displayTimedAllMessageOnMap(map: LeafletMapView) { | 59 | 59 | fun displayTimedAllMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 60 | 60 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 61 | 61 | observableVessel.vessels.forEach { (_, value) -> | |
| val message = value.messageToDisplay ?: return@forEach | 62 | 62 | val message = value.messageToDisplay ?: return@forEach | |
| map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))") | 63 | 63 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))") | |
| } | 64 | 64 | } | |
| } | 65 | 65 | } | |
| 66 | 66 | |||
| fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 67 | 67 | fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 68 | 68 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 69 | 69 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 70 | 70 | value.messages.forEach { (_, message) -> | |
| if (selectedMMSI == message.mmsi.value) { | 71 | 71 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)") | 72 | 72 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap))") | |
| } else { | 73 | 73 | } else { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | 74 | 74 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))") | |
| } | 75 | 75 | } | |
| } | 76 | 76 | } | |
| } | 77 | 77 | } | |
| } | 78 | 78 | } | |
| 79 | 79 | |||
| fun displayTimedAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 80 | 80 | fun displayTimedAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 81 | 81 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 82 | 82 | observableVessel.vessels.forEach { (_, value) -> | |
| val message = value.messageToDisplay ?: return@forEach | 83 | 83 | val message = value.messageToDisplay ?: return@forEach | |
| if (selectedMMSI == message.mmsi.value) { | 84 | 84 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap))") | 85 | 85 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap))") | |
| } else { | 86 | 86 | } else { | |
| map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))") | 87 | 87 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))") | |
| } | 88 | 88 | } | |
| } | 89 | 89 | } | |
| } | 90 | 90 | } | |
| 91 | 91 | |||
| fun displayClusterMessageOnMap(map: LeafletMapView) { | 92 | 92 | fun displayClusterMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 93 | 93 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 94 | 94 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 95 | 95 | value.messages.forEach { (_, message) -> | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 96 | 96 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 97 | 97 | } | |
| } | 98 | 98 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 99 | 99 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 100 | 100 | } | |
| 101 | 101 | |||
| fun displayTimedClusterMessageOnMap(map: LeafletMapView) { | 102 | 102 | fun displayTimedClusterMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 103 | 103 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 104 | 104 | observableVessel.vessels.forEach { (_, value) -> | |
| val message = value.messageToDisplay ?: return@forEach | 105 | 105 | val message = value.messageToDisplay ?: return@forEach | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 106 | 106 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 107 | 107 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 108 | 108 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 109 | 109 | } | |
| 110 | 110 | |||
| fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 111 | 111 | fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 112 | 112 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 113 | 113 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 114 | 114 | value.messages.forEach { (_, message) -> | |
| if (selectedMMSI == message.mmsi.value) { | 115 | 115 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | 116 | 116 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | |
| } else { | 117 | 117 | } else { | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 118 | 118 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 119 | 119 | } | |
| } | 120 | 120 | } | |
| } | 121 | 121 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 122 | 122 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 123 | 123 | } | |
| 124 | 124 | |||
| fun displayTimedClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 125 | 125 | fun displayTimedClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 126 | 126 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 127 | 127 | observableVessel.vessels.forEach { (_, value) -> | |
| val message = value.messageToDisplay ?: return@forEach | 128 | 128 | val message = value.messageToDisplay ?: return@forEach | |
| if (selectedMMSI == message.mmsi.value) { | 129 | 129 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | 130 | 130 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | |
| } else { | 131 | 131 | } else { | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 132 | 132 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 133 | 133 | } | |
| } | 134 | 134 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 135 | 135 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 136 | 136 | } | |
| 137 | 137 | |||
| fun displayHeatMapOnMap(map: LeafletMapView) { | 138 | 138 | fun displayHeatMapOnMap(map: LeafletMapView) { | |
| clearMap(map) | 139 | 139 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 140 | 140 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 141 | 141 | value.messages.forEach { (_, message) -> | |
| map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 142 | 142 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
| } | 143 | 143 | } | |
| } | 144 | 144 | } | |
| } | 145 | 145 | } | |
| 146 | 146 | |||
| fun displayTimedHeatMapOnMap(map: LeafletMapView) { | 147 | 147 | fun displayTimedHeatMapOnMap(map: LeafletMapView) { | |
| clearMap(map) | 148 | 148 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 149 | 149 | observableVessel.vessels.forEach { (_, value) -> | |
| val message = value.messageToDisplay ?: return@forEach | 150 | 150 | val message = value.messageToDisplay ?: return@forEach | |
| map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 151 | 151 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
| } | 152 | 152 | } | |
| } | 153 | 153 | } | |
| 154 | 154 | |||
| fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | 155 | 155 | fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 156 | 156 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 157 | 157 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 158 | 158 | value.messages.forEach { (_, message) -> | |
| if (selectedMMSI == message.mmsi.value) { | 159 | 159 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | 160 | 160 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | |
| } else { | 161 | 161 | } else { | |
| map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 162 | 162 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
| } | 163 | 163 | } | |
| } | 164 | 164 | } | |
| } | 165 | 165 | } |
src/main/resources/gui/menuBar.fxml
View file @
dd1ce7f
| <?xml version="1.0" encoding="UTF-8"?> | 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | 2 | |||
| <?import javafx.scene.control.*?> | 3 | 3 | <?import javafx.scene.control.*?> | |
| 4 | 4 | |||
| <MenuBar fx:id="menuBar" prefHeight="25.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MenuBarController"> | 5 | 5 | <MenuBar fx:id="menuBar" prefHeight="25.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MenuBarController"> | |
| <menus> | 6 | 6 | <menus> | |
| <Menu mnemonicParsing="false" text="File"> | 7 | 7 | <Menu mnemonicParsing="false" text="File"> | |
| <items> | 8 | 8 | <items> | |
| <MenuItem fx:id="import" mnemonicParsing="false" text="Import" /> | 9 | 9 | <MenuItem fx:id="import" mnemonicParsing="false" text="Import" /> | |
| </items> | 10 | 10 | </items> | |
| </Menu> | 11 | 11 | </Menu> | |
| <Menu mnemonicParsing="false" text="Settings"> | 12 | 12 | <Menu mnemonicParsing="false" text="Settings"> | |
| <items> | 13 | 13 | <items> | |
| <Menu mnemonicParsing="false" text="Map selected"> | 14 | 14 | <Menu mnemonicParsing="false" text="Map selected"> | |
| <items> | 15 | 15 | <items> | |
| <CheckMenuItem mnemonicParsing="false" text="All messages" fx:id="allMessages"/> | 16 | 16 | <CheckMenuItem fx:id="allMessages" mnemonicParsing="false" text="All messages" /> | |
| <CheckMenuItem mnemonicParsing="false" text="Clustered messages" fx:id="clusteredMessage"/> | 17 | 17 | <CheckMenuItem fx:id="clusteredMessage" mnemonicParsing="false" text="Clustered messages" /> | |
| <CheckMenuItem mnemonicParsing="false" text="Heat map" fx:id="heatMap"/> | 18 | 18 | <CheckMenuItem fx:id="heatMap" mnemonicParsing="false" text="Heat map" /> | |
| </items> | 19 | 19 | </items> | |
| </Menu> | 20 | 20 | </Menu> | |
| 21 | <RadioMenuItem fx:id="activateReplayButton" mnemonicParsing="false" text="Unspecified Action" /> | |||
| </items> | 21 | 22 | </items> | |
| </Menu> | 22 | 23 | </Menu> | |
| <Menu mnemonicParsing="false" text="Help"> | 23 | 24 | <Menu mnemonicParsing="false" text="Help"> | |
| <items> | 24 | 25 | <items> | |
| <MenuItem mnemonicParsing="false" text="About" /> | 25 | 26 | <MenuItem mnemonicParsing="false" text="About" /> | |
| </items> | 26 | 27 | </items> | |
| </Menu> | 27 | 28 | </Menu> | |
| </menus> | 28 | 29 | </menus> | |
| </MenuBar> | 29 | 30 | </MenuBar> | |
| 30 | 31 | |||