From 203aaf2804f0418ae8ac7076e7cae45a82b37a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ole=20K=C3=BCck?= Date: Tue, 21 May 2024 13:37:58 +0200 Subject: [PATCH] #27: Implementing Alternative Medications for the View --- .idea/dataSources.xml | 2 +- .../medication/AllMedicationController.java | 4 + .../hitec/nhplus/medication/Medication.java | 7 + .../nhplus/medication/MedicationListCell.java | 129 ++++++++++++++++++ .../medication/MedicationModalController.java | 79 +++++++---- src/main/java/module-info.java | 1 + .../nhplus/medication/AllMedicationView.fxml | 5 + .../nhplus/medication/MedicationModal.fxml | 45 +++++- 8 files changed, 240 insertions(+), 32 deletions(-) create mode 100644 src/main/java/de/hitec/nhplus/medication/MedicationListCell.java diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index fed79a7..98f48a2 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -1,7 +1,7 @@ - + sqlite.xerial true org.sqlite.JDBC diff --git a/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java b/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java index 547d30f..070c582 100644 --- a/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java +++ b/src/main/java/de/hitec/nhplus/medication/AllMedicationController.java @@ -52,6 +52,10 @@ public class AllMedicationController { private final ObservableList medications = FXCollections.observableArrayList(); private MedicationDao dao; + public MedicationDao getDao() { + return dao; + } + /** * Initialization method that is called after the binding of all the fields. */ diff --git a/src/main/java/de/hitec/nhplus/medication/Medication.java b/src/main/java/de/hitec/nhplus/medication/Medication.java index 6320e58..7fdce57 100644 --- a/src/main/java/de/hitec/nhplus/medication/Medication.java +++ b/src/main/java/de/hitec/nhplus/medication/Medication.java @@ -164,4 +164,11 @@ public class Medication { .add("Current Stock: " + this.getCurrentStock()) .toString(); } + + public String getComboBoxString() { + return new StringJoiner(System.lineSeparator()) + .add("Name: " + this.getName()) + .add("Hersteller: " + this.getManufacturer()) + .toString(); + } } diff --git a/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java b/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java new file mode 100644 index 0000000..608267b --- /dev/null +++ b/src/main/java/de/hitec/nhplus/medication/MedicationListCell.java @@ -0,0 +1,129 @@ +package de.hitec.nhplus.medication; + +import java.util.List; + +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.layout.BorderPane; +import javafx.scene.text.Text; +import org.controlsfx.control.SearchableComboBox; + +/** + * A custom implementation of the {@link ListCell} for {@link Ingredient}s. + * This implementation contains an automatic resizing of the parent {@link ListView}. + * + * @author Dominik Säume + */ +public class MedicationListCell extends ListCell { + private final Button deleteButton; + private SearchableComboBox comboBox; + private static final double BUILTIN_BORDER_WIDTH = 1; + private static final double CELL_SPACING = 4; + private static final double CELL_PADDING = 4; + private static final double BUTTON_PADDING_X = 8; + private static final double BUTTON_PADDING_Y = 4; + private final double totalSpacing; + private final List allOtherMedications; + + public MedicationListCell(List allOtherMedications) { + this.allOtherMedications = allOtherMedications; + this.setPadding(new Insets(CELL_PADDING)); + + comboBox = new SearchableComboBox(); + ObservableList list = FXCollections.observableArrayList(); + list.setAll(allOtherMedications); + comboBox.setItems(list); + comboBox.setPromptText("Alternatve Auswählen"); + comboBox.setCellFactory(this::comboBoxFactory); + comboBox.setButtonCell(comboBoxButtonFactory()); + comboBox.valueProperty().addListener(this::onComboBoxChange); + + deleteButton = new Button("-"); + deleteButton.setPadding(new Insets( + BUTTON_PADDING_Y, + BUTTON_PADDING_X, + BUTTON_PADDING_Y, + BUTTON_PADDING_X + )); + deleteButton.setOnAction(event -> getListView().getItems().remove(getItem())); + + // Calculate Delete Button Width + Text textNode = new Text(deleteButton.getText()); + textNode.setFont(deleteButton.getFont()); + double calculatedDeleteButtonWidth = textNode.getLayoutBounds().getWidth() + BUTTON_PADDING_X * 2; + + totalSpacing = BUILTIN_BORDER_WIDTH * 2 // List View + + CELL_PADDING * 2 + + CELL_SPACING + + calculatedDeleteButtonWidth; + } + + private void onComboBoxChange( + ObservableValue observableValue, + Medication oldValue, + Medication newValue + ) { + ListView listView = getListView(); + double max = listView.lookupAll("*") + .stream() + .filter(node -> node instanceof MedicationListCell) + .mapToDouble(node -> getComboBoxWidth()) + .max() + .orElse(0); + + listView.setMinWidth(max + totalSpacing); + } + + + @Override + protected void updateItem(Medication item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setGraphic(null); + } else { + BorderPane cellPane = new BorderPane(); + cellPane.setCenter(comboBox); + cellPane.setRight(deleteButton); + BorderPane.setMargin(deleteButton, new Insets(0, 0, 0, CELL_SPACING)); + setGraphic(cellPane); + } + } + + private double getComboBoxWidth(){ + return comboBox.getWidth(); + } + + private ListCell comboBoxFactory(Object param) { + return new ListCell() { + + @Override + protected void updateItem(Medication med, boolean empty) { + super.updateItem(med, empty); + setText( + empty || med == null + ? null + : med.getComboBoxString() + ); + } + }; + } + + private ListCell comboBoxButtonFactory() { + return new ListCell() { + @Override + protected void updateItem(Medication med, boolean empty) { + super.updateItem(med, empty); + setText( + empty || med == null + ? comboBox.getPromptText() + : med.getComboBoxString() + ); + } + }; + } +} diff --git a/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java b/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java index 9489737..fa89b39 100644 --- a/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java +++ b/src/main/java/de/hitec/nhplus/medication/MedicationModalController.java @@ -1,6 +1,11 @@ package de.hitec.nhplus.medication; -import de.hitec.nhplus.treatment.Treatment; +import static de.hitec.nhplus.utils.Validator.*; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; + import javafx.beans.value.ChangeListener; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -11,12 +16,6 @@ import javafx.scene.control.TextArea; import javafx.scene.control.TextField; import javafx.stage.Stage; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Predicate; - -import static de.hitec.nhplus.utils.Validator.*; - /** * The controller for creating and editing a specific {@link Medication}. * @@ -38,21 +37,25 @@ public class MedicationModalController { public TextArea textAreaPossibleSideEffects; @FXML public Button buttonSave; + @FXML + public ListView listViewAlternativeMedication; private Stage stage; private Medication medication; private final ObservableList ingredients = FXCollections.observableArrayList(); + private final ObservableList alternativeMediaction = FXCollections.observableArrayList(); private AllMedicationController controller; private boolean isNewMedication = false; + private List allOtherMedications; /** * Initialization method that is called after the binding of all the fields. */ @FXML public void initialize( - Stage stage, - AllMedicationController controller, - Medication medication + Stage stage, + AllMedicationController controller, + Medication medication ) { this.stage = stage; this.controller = controller; @@ -62,25 +65,29 @@ public class MedicationModalController { } else { isNewMedication = true; this.medication = new Medication( - "", - "", - new ArrayList<>(), - "", - "", - 0 + "", + "", + new ArrayList<>(), + "", + "", + 0 ); this.buttonSave.setDisable(true); } listViewIngredients.setCellFactory(cellData -> new IngredientListCell()); listViewIngredients.setItems(ingredients); + showData(); + listViewAlternativeMedication.setCellFactory(cellData -> new MedicationListCell(allOtherMedications)); + listViewAlternativeMedication.setItems(alternativeMediaction); + ChangeListener inputMedicationValidationListener = (observableValue, oldText, newText) -> { boolean isValid = isValidMedicationName(textFieldName.getText()) - && isValidMedicationManufacturer(textFieldManufacturer.getText()) - && isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText()) - && isValidStock(textFieldCurrentStock.getText()); + && isValidMedicationManufacturer(textFieldManufacturer.getText()) + && isValidMedicationAdministrationMethod(textFieldAdministrationMethod.getText()) + && isValidStock(textFieldCurrentStock.getText()); this.buttonSave.setDisable(!isValid); }; @@ -96,6 +103,17 @@ public class MedicationModalController { */ private void showData() { ingredients.setAll(medication.getIngredients()); + try { + allOtherMedications = controller.getDao().readAll(); + if (!isNewMedication) { + allOtherMedications = allOtherMedications + .stream() + .filter(med -> med.getId() != medication.getId()) + .toList(); + } + } catch (Exception exception) { + exception.printStackTrace(); + } textFieldName.setText(medication.getName()); textFieldManufacturer.setText(medication.getManufacturer()); textFieldAdministrationMethod.setText(medication.getAdministrationMethod()); @@ -111,12 +129,12 @@ public class MedicationModalController { this.medication.setCurrentStock(Integer.parseInt(textFieldCurrentStock.getText())); this.medication.setPossibleSideEffects(textAreaPossibleSideEffects.getText()); this.medication.setIngredients(ingredients - .stream() - .map(Ingredient::getName) - .distinct() - .filter(Predicate.not(String::isEmpty)) - .map(Ingredient::new) - .toList() + .stream() + .map(Ingredient::getName) + .distinct() + .filter(Predicate.not(String::isEmpty)) + .map(Ingredient::new) + .toList() ); if (isNewMedication) { @@ -138,4 +156,15 @@ public class MedicationModalController { public void handleAddIngredient() { ingredients.add(new Ingredient("")); } + + public void handleAddAlternativeMedication() { + alternativeMediaction.add(new Medication( + null, + null, + List.of(), + null, + null, + -1 + )); + } } diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 06e67a7..d9cb95f 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -3,6 +3,7 @@ module de.hitec.nhplus { requires javafx.fxml; requires java.sql; requires org.xerial.sqlitejdbc; + requires org.controlsfx.controls; exports de.hitec.nhplus; opens de.hitec.nhplus to javafx.fxml; diff --git a/src/main/resources/de/hitec/nhplus/medication/AllMedicationView.fxml b/src/main/resources/de/hitec/nhplus/medication/AllMedicationView.fxml index 157caac..9be5712 100644 --- a/src/main/resources/de/hitec/nhplus/medication/AllMedicationView.fxml +++ b/src/main/resources/de/hitec/nhplus/medication/AllMedicationView.fxml @@ -49,6 +49,11 @@ minWidth="100.0" text="Lagerbestand" /> + diff --git a/src/main/resources/de/hitec/nhplus/medication/MedicationModal.fxml b/src/main/resources/de/hitec/nhplus/medication/MedicationModal.fxml index c368327..4a4990a 100644 --- a/src/main/resources/de/hitec/nhplus/medication/MedicationModal.fxml +++ b/src/main/resources/de/hitec/nhplus/medication/MedicationModal.fxml @@ -3,6 +3,7 @@ + - - -
- +
@@ -105,10 +103,45 @@
-
+ - +
+ + + + + +
+ + + + + +