diff --git a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java index 5554d3d..9ae9b4c 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/PlantsController.java @@ -5,7 +5,6 @@ import ch.zhaw.gartenverwaltung.plantList.PlantListModel; import ch.zhaw.gartenverwaltung.types.HardinessZone; import ch.zhaw.gartenverwaltung.types.Plant; import ch.zhaw.gartenverwaltung.types.Seasons; -import javafx.application.Platform; import javafx.beans.property.ListProperty; import javafx.beans.property.SimpleListProperty; import javafx.beans.value.ChangeListener; @@ -13,18 +12,22 @@ import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.geometry.Insets; -import javafx.geometry.Bounds; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.input.KeyEvent; import javafx.scene.layout.VBox; +import javafx.stage.Modality; +import javafx.stage.Stage; import java.io.IOException; import java.net.URL; import java.util.List; +import java.util.Objects; import java.util.ResourceBundle; public class PlantsController implements Initializable { @@ -33,7 +36,6 @@ public class PlantsController implements Initializable { private final HardinessZone DEFAULT_HARDINESS_ZONE = HardinessZone.ZONE_8A; // TODO: move to model - private final ListProperty plantListProperty = new SimpleListProperty<>(FXCollections.observableArrayList()); @FXML @@ -52,18 +54,27 @@ public class PlantsController implements Initializable { private ListView list_plants; @FXML - private Button saveToMyPlant_button; + private Button selectSowDay_button; @FXML private TextField search_plants; /** - * saves the current selected plant in new JSON database + * open new window to select sow or harvest day to save the crop * @param event event */ @FXML - void saveToMyPlant(ActionEvent event) { - //ToDo model save selectedPlant to mySelectedPlant(IO) + void selectSowDate(ActionEvent event) throws IOException { + Parent root; + FXMLLoader fxmlLoader = new FXMLLoader(Objects.requireNonNull(getClass().getResource("SelectSowDay.fxml"))); + root = fxmlLoader.load(); + SelectSowDayController controller = fxmlLoader.getController(); + controller.getSelectedPlant(selectedPlant); + Stage stage = new Stage(); + stage.setScene(new Scene(root)); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setResizable(false); + stage.showAndWait(); } /** @@ -80,7 +91,7 @@ public class PlantsController implements Initializable { list_plants.itemsProperty().bind(plantListProperty); description_plant.setText(""); - saveToMyPlant_button.setDisable(true); + selectSowDay_button.setDisable(true); createFilterSeasons(); createFilterHardinessZone(); @@ -190,7 +201,7 @@ public class PlantsController implements Initializable { private void createFilterSeasons() { ToggleGroup seasonGroup = new ToggleGroup(); for (Seasons season : Seasons.values()) { - RadioButton radioButton = new RadioButton(season.name()); + RadioButton radioButton = new RadioButton(season.getName()); radioButton.setToggleGroup(seasonGroup); radioButton.setPadding(new Insets(0,0,10,0)); if (season.equals(Seasons.AllSEASONS)) { @@ -221,13 +232,18 @@ public class PlantsController implements Initializable { * image of the plant */ private void lookForSelectedListEntry() { + selectedPlant = null; + description_plant.setText(""); + selectSowDay_button.setDisable(true); + Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); + img_plant.setImage(img); list_plants.getSelectionModel().selectedItemProperty().addListener(new ChangeListener() { @Override public void changed(ObservableValue observable, Plant oldValue, Plant newValue) { if(newValue != null) { selectedPlant = newValue; description_plant.setText(selectedPlant.description()); - saveToMyPlant_button.setDisable(false); + selectSowDay_button.setDisable(false); Image img; if(selectedPlant.image() != null) { img = selectedPlant.image(); @@ -235,12 +251,10 @@ public class PlantsController implements Initializable { img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); } img_plant.setImage(img); - - } else { selectedPlant = null; description_plant.setText(""); - saveToMyPlant_button.setDisable(true); + selectSowDay_button.setDisable(true); Image img = new Image(String.valueOf(PlantsController.class.getResource("placeholder.png"))); img_plant.setImage(img); } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java b/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java new file mode 100644 index 0000000..2537e08 --- /dev/null +++ b/src/main/java/ch/zhaw/gartenverwaltung/SelectSowDayController.java @@ -0,0 +1,167 @@ +package ch.zhaw.gartenverwaltung; + +import ch.zhaw.gartenverwaltung.types.GrowthPhaseType; +import ch.zhaw.gartenverwaltung.types.Plant; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.*; +import javafx.stage.Stage; +import javafx.util.Callback; + +import java.net.URL; +import java.time.LocalDate; +import java.util.List; +import java.util.ResourceBundle; + +public class SelectSowDayController implements Initializable { + private Plant selectedPlant = null; + + @FXML + private DatePicker datepicker; + + @FXML + private Label popup_label; + + @FXML + private Button save_button; + + @FXML + private RadioButton sow_radio; + + /** + * close the date selector window + * @param event event + */ + @FXML + void cancel(ActionEvent event) { + closeWindow(); + } + + /** + * get sow date from datePicker or calculate sow date from harvest date + * save selected plant and sow date + * @param event event + */ + @FXML + void save(ActionEvent event) { + //ToDo save plant and sow date to crop/gardenplan model + LocalDate sowDate; + if (sow_radio.isSelected()) { + sowDate = datepicker.getValue(); + } else { + //ToDo method to get current lifecycle group in plant + sowDate = selectedPlant.sowDateFromHarvestDate(datepicker.getValue(), 0); + } + System.out.println(sowDate); + System.out.println(selectedPlant); + closeWindow(); + } + + /** + * save the plant which will be planted and update label + * @param plant Plant + */ + public void getSelectedPlant(Plant plant) { + selectedPlant = plant; + popup_label.setText("Select Harvest/Sow Date for" + selectedPlant.name()); + } + + /** + * add listener and set default values + * {@inheritDoc} + * @param location location + * @param resources resources + */ + @Override + public void initialize(URL location, ResourceBundle resources) { + clearDatePickerEntries(); + + Callback dayCellFactory= getDayCellFactory(); + datepicker.setDayCellFactory(dayCellFactory); + datepicker.getEditor().setEditable(false); + + enableDisableSaveButton(); + } + + /** + * clear date picker editor when radio button is changed + */ + private void clearDatePickerEntries() { + sow_radio.selectedProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, Boolean oldValue, Boolean isNowSelected) { + datepicker.getEditor().clear(); + } + }); + } + + /** + * date picker disable/enable dates according to selected plant: sow or harvest day + * @return cellFactory of datePicker + */ + private Callback getDayCellFactory() { + + final Callback dayCellFactory = new Callback() { + + @Override + public DateCell call(final DatePicker datePicker) { + return new DateCell() { + @Override + public void updateItem(LocalDate item, boolean empty) { + super.updateItem(item, empty); + setDisable(true); + setStyle("-fx-background-color: #ffc0cb;"); + List dates; + LocalDate today = LocalDate.now(); + if (sow_radio.isSelected()) { + dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.SOW); + } else { + dates = selectedPlant.getDateListOfGrowthPhase(GrowthPhaseType.HARVEST); + } + for (LocalDate date : dates) { + if (item.getMonth() == date.getMonth() + && item.getDayOfMonth() == date.getDayOfMonth() + && item.compareTo(today) > 0) { + setDisable(false); + setStyle("-fx-background-color: #32CD32;"); + } + } + if ((!sow_radio.isSelected() && selectedPlant.sowDateFromHarvestDate(item, 0).compareTo(today) < 0)) { + setDisable(true); + setStyle("-fx-background-color: #ffc0cb;"); + } + } + }; + } + }; + return dayCellFactory; + } + + /** + * close date picker window + */ + private void closeWindow() { + Stage stage = (Stage) save_button.getScene().getWindow(); + stage.close(); + } + + /** + * disable save button, when there is no date selected in date picker + */ + private void enableDisableSaveButton() { + save_button.setDisable(true); + datepicker.getEditor().textProperty().addListener(new ChangeListener() { + @Override + public void changed(ObservableValue observable, String oldValue, String newValue) { + if (newValue == null || newValue.equals("")) { + save_button.setDisable(true); + } else { + save_button.setDisable(false); + } + } + }); + } +} diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java index 3250b4f..9cb3d08 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Plant.java @@ -3,6 +3,7 @@ package ch.zhaw.gartenverwaltung.types; import javafx.scene.image.Image; import java.time.LocalDate; +import java.util.LinkedList; import java.util.List; import java.util.stream.Collectors; @@ -19,20 +20,40 @@ public record Plant( List pests, List lifecycle) { + /** + * remove all growthPhase which do not belong to the hardiness zone + * @param zone hardiness zone + */ public void inZone(HardinessZone zone) { lifecycle.removeIf(growthPhase -> !growthPhase.zone().equals(zone)); } + /** + * get all growthPhases of lifecycle group + * @param group lifecycle group + * @return list of growthPhases + */ public List lifecycleForGroup(int group) { return lifecycle.stream() - .filter(growthPhase -> growthPhase.group() != group) + .filter(growthPhase -> growthPhase.group() == group) .collect(Collectors.toList()); } + /** + * get sow date from given harvest day from lifecycle group + * @param harvestDate date of the harvest + * @param group lifecycle group + * @return LocaleDate of sow date + */ public LocalDate sowDateFromHarvestDate(LocalDate harvestDate, int group) { return harvestDate.minusDays(timeToHarvest(group)); } + /** + * calculate the days between sow and harvest day for lifecycle group + * @param group the lifecycle group + * @return Integer number of dates between sow and harvest day + */ public int timeToHarvest(int group) { List activeLifecycle = lifecycleForGroup(group); GrowthPhase sow = activeLifecycle.stream() @@ -47,4 +68,38 @@ public record Plant( int currentYear = LocalDate.now().getYear(); return (int) DAYS.between(harvest.startDate().atYear(currentYear), sow.startDate().atYear(currentYear)); } + + /** + * filter out the given growthPhase out of the lifecycle + * create list of dates for this growthPhase and return it + * @param growthPhase the wanted growthPhase + * @return a list of dates of the current year + */ + public List getDateListOfGrowthPhase(GrowthPhaseType growthPhase) { + List dates = new LinkedList<>(); + for (GrowthPhase growth : lifecycle) { + if (growth.type().equals(growthPhase)) { + dates = addDatesFromMonthDay(growth); + } + } + return dates; + } + + /** + * transform monthDay value of the given growthPhase to localDate + * return a list of dates from start to end of growth phase + * @param growthPhase the current growthPhase + * @return a list of dates of the current year + */ + private List addDatesFromMonthDay(GrowthPhase growthPhase) { + List dates = new LinkedList<>(); + LocalDate today = LocalDate.now(); + LocalDate start = growthPhase.startDate().atYear(today.getYear()); + LocalDate end = growthPhase.endDate().atYear(today.getYear()); + while (!start.isAfter(end)) { + dates.add(start); + start = start.plusDays(1); + } + return dates; + } } diff --git a/src/main/java/ch/zhaw/gartenverwaltung/types/Seasons.java b/src/main/java/ch/zhaw/gartenverwaltung/types/Seasons.java index 5e907a4..2a2d2d0 100644 --- a/src/main/java/ch/zhaw/gartenverwaltung/types/Seasons.java +++ b/src/main/java/ch/zhaw/gartenverwaltung/types/Seasons.java @@ -3,18 +3,20 @@ package ch.zhaw.gartenverwaltung.types; import java.time.MonthDay; public enum Seasons { - AllSEASONS("--01-01", "--12-31"), - SPRING("--03-01", "--05-30"), - SOMMER("--06-01", "--08-30"), - AUTUM("--09-01", "--11-30"), - WINTER("--12-01", "--02-28"); + AllSEASONS("--01-01", "--12-31", "All Seasons"), + SPRING("--03-01", "--05-30", "Spring"), + SUMMER("--06-01", "--08-30", "Summer"), + AUTUMN("--09-01", "--11-30", "Autumn"), + WINTER("--12-01", "--02-28", "Winter"); public final String startDate; public final String endDate; + public final String name; - Seasons(String startDate, String endDate) { + Seasons(String startDate, String endDate, String name) { this.startDate = startDate; this.endDate = endDate; + this.name = name; } public MonthDay getStartDate() { @@ -23,4 +25,7 @@ public enum Seasons { public MonthDay getEndDate() { return MonthDay.parse(this.endDate); } + public String getName() { + return this.name; + } } diff --git a/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml index 4fbb89b..18af41a 100644 --- a/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml +++ b/src/main/resources/ch/zhaw/gartenverwaltung/Plants.fxml @@ -1,14 +1,12 @@ - - @@ -33,15 +31,8 @@ - - - - - - - - - + + - +