Newer
Older
package mi.hdm.controllers;
import javafx.fxml.FXML;
import javafx.scene.control.*;
Blersch Lara
committed
import javafx.scene.image.ImageView;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import mi.hdm.components.CategoryCheckBox;
import mi.hdm.components.CategoryPreviewLabel;
import mi.hdm.components.IngredientSearchResultLabel;
import mi.hdm.components.SelectedIngredientLabel;
import mi.hdm.exceptions.InvalidRecipeException;
import mi.hdm.recipes.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
Blersch Lara
committed
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.IntStream;
import static mi.hdm.helpers.Validation.isInteger;
public class RecipeEditorController extends BaseController {
private static final int ELEMENTS_PER_SEARCH_PAGE = 100;
private Recipe recipe;
private final RecipeManager recipeManager;
private final CategoryManager categoryManager;
private final IngredientManager ingredientManager;
private final RecipeSearch recipeSearch;
private List<RecipeComponent> searchResults;
private final List<Category> selectedCategories;
private final List<RecipeComponent> selectedIngredients;
private List<SelectedIngredientLabel> selectedIngredientLabels;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
private int currentPage = 0;
private int maxPages = 0;
private static final Logger log = LogManager.getLogger(RecipeEditorController.class);
@FXML
private TextField nameTextField;
@FXML
private TextArea descriptionTextArea;
@FXML
private TextField prepTimeTextField;
@FXML
private TextField imagePathTextField;
@FXML
private HBox categories;
@FXML
private FlowPane allCategories;
@FXML
private TextField ingredientSearch;
@FXML
private ScrollPane searchResultsPane;
@FXML
private TextArea preparationTextArea;
@FXML
private ScrollPane selectedIngredientsScrollPane;
private final VBox selectedIngredientsVbox;
public RecipeEditorController(Recipe recipe, RecipeManager recipeManager, IngredientManager ingredientManager, CategoryManager categoryManager) {
this.recipe = recipe;
this.recipeManager = recipeManager;
this.categoryManager = categoryManager;
this.ingredientManager = ingredientManager;
selectedCategories = new ArrayList<>(categoryManager.getCategoriesFromKeys(recipe.getCategoryCodes()));
log.info("Added categories to selectedCategories: {}", selectedCategories);
searchResults = new ArrayList<>();
selectedIngredients = new ArrayList<>();
ingredientManager.getIngredientsFromKeys(recipe.getIngredients()).forEach((i, amount) -> {
if (i instanceof Ingredient) {
selectedIngredients.add(i);
} else {
log.error("No ingredient with code {}", i.getUniqueCode());
}
});
recipeSearch = new RecipeSearch(recipeManager, ingredientManager);
selectedIngredientsVbox = new VBox();
selectedIngredientLabels = new ArrayList<>();
}
@FXML
public void initialize() {
mapCategories();
displayRecipe();
ingredientSearch.textProperty().addListener(((e) -> searchIngredients())); //adds onChange listener to the search field
selectedIngredientsScrollPane.setContent(selectedIngredientsVbox);
}
private void displayRecipe() {
nameTextField.setText(recipe.getName());
descriptionTextArea.setText(recipe.getDescription());
recipe.getIngredients().forEach((k, v) -> {
HBox ingredientHBox = new HBox();
Button deleteIngredientButton = new Button("X");
deleteIngredientButton.setStyle("-fx-text-fill: d91c1c;" +
"-fx-font-size: 12;" +
"-fx-background-size: small;");
deleteIngredientButton.setOnAction(h -> {
selectedIngredients.remove(ingredientManager.getIngredient(k));
log.debug("User deleted ingredient '{}' from recipe.", ingredientManager.getIngredient(k).get().getName());
selectedIngredientsVbox.getChildren().remove(ingredientHBox);
});
SelectedIngredientLabel label = new SelectedIngredientLabel(ingredientManager.getIngredient(k).get(), v);
selectedIngredientLabels.add(label);
ingredientHBox.getChildren().addAll(label, deleteIngredientButton);
selectedIngredientsVbox.getChildren().add(ingredientHBox);
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
});
preparationTextArea.setText(String.join("\n", recipe.getPreparation()));
prepTimeTextField.setText(String.valueOf(recipe.getPreparationTimeMins()));
imagePathTextField.setText(recipe.getImageURL().toString());
}
private void mapCategories() {
for (final Category category : categoryManager.getAllCategories()) {
CategoryCheckBox checkbox = new CategoryCheckBox(category);
if(selectedCategories.contains(category)) {
checkbox.setSelected(true);
drawSelectedCategories();
}
checkbox.setOnAction(e -> updateSelectedCategories(checkbox));
allCategories.getChildren().add(checkbox);
}
}
private void updateSelectedCategories(CategoryCheckBox checkbox) {
if (checkbox.isSelected()) {
selectedCategories.add(checkbox.getAssociatedCategory());
log.debug("Added '{}' to list of selected categories.", checkbox.getAssociatedCategory().getName());
} else {
selectedCategories.remove(checkbox.getAssociatedCategory());
log.debug("Removed '{}' from list of selected categories.", checkbox.getAssociatedCategory().getName());
}
drawSelectedCategories();
}
private void drawSelectedCategories() {
log.debug("Drawing selected categories");
categories.getChildren().clear();
for (final Category category : selectedCategories) {
CategoryPreviewLabel label = new CategoryPreviewLabel(category);
categories.getChildren().add(label);
}
}
@FXML
public void searchIngredients() {
String input = ingredientSearch.getText();
log.debug("Input in search field changed, searching through recipe components with query '{}'.", input);
searchResults = recipeSearch.searchThroughNames(input);
currentPage = 0;
maxPages = searchResults.size() / ELEMENTS_PER_SEARCH_PAGE;
drawIngredientSearchResults();
}
private void drawIngredientSearchResults() {
log.debug("Drawing search results");
VBox resultContainer = new VBox();
int start = currentPage * ELEMENTS_PER_SEARCH_PAGE;
int end = Math.min(searchResults.size(), start + ELEMENTS_PER_SEARCH_PAGE);
IntStream.range(start, end)
.mapToObj(searchResults::get)
.forEach(
result -> {
IngredientSearchResultLabel resultLabel = new IngredientSearchResultLabel(result);
resultLabel.setOnMouseClicked(e -> {
log.debug("User added ingredient '{}' to recipe.", result.getName());
if (!selectedIngredients.contains(result)) {
selectedIngredients.add(result);
HBox ingredientHBox = new HBox();
Button deleteIngredientButton = new Button("X");
deleteIngredientButton.setStyle("-fx-text-fill: d91c1c;" +
"-fx-font-size: 12;" +
"-fx-background-size: small;");
deleteIngredientButton.setOnAction(h -> {
selectedIngredients.remove(result);
log.debug("User deleted ingredient '{}' from recipe.", result.getName());
selectedIngredientsVbox.getChildren().remove(ingredientHBox);
});
SelectedIngredientLabel label = new SelectedIngredientLabel(result);
selectedIngredientLabels.add(label);
ingredientHBox.getChildren().addAll(label, deleteIngredientButton);
selectedIngredientsVbox.getChildren().add(ingredientHBox);
}
});
resultContainer.getChildren().add(resultLabel);
}
);
searchResultsPane.setContent(resultContainer);
}
@FXML
public void confirmEditRecipe() {
log.debug("User confirmed recipe editing.");
recipe.setName(nameTextField.getText());
Map<RecipeComponent, Integer> ingredients = new HashMap<>();
selectedIngredientLabels.forEach(label -> {
ingredients.put(label.getComponent(), label.getAmount());
});
Blersch Lara
committed
try {
recipe.setIngredientFromRecipeComponents(ingredients);
recipe.setDescription(descriptionTextArea.getText());
recipe.setPreparation(List.of(preparationTextArea.getText().split("\n")));
recipe.setCategoriesFromObjects(List.copyOf(selectedCategories));
String prepTime = prepTimeTextField.getText();
recipe.setPreparationTimeMins( isInteger(prepTime) ? Integer.parseInt(prepTime) : null);
Blersch Lara
committed
if (Files.exists(Paths.get(new URI(imagePathTextField.getText())))) {
recipe.setImage(new URL(imagePathTextField.getText()));
}
log.info("Recipe '{}' was edited.", recipe.getName());
changeSceneToRecipe();
} catch (InvalidRecipeException e) {
Alert a = new Alert(Alert.AlertType.ERROR);
a.setHeaderText("Error creating recipe");
a.setContentText(e.getMessage());
a.show();
log.error(e.getMessage());
log.error("Recipe not created.");
} catch (RuntimeException e) {
Alert a = new Alert(Alert.AlertType.ERROR);
a.setHeaderText("Error creating recipe");
a.setContentText("Something went wrong when creating the recipe. Check all your inputs!");
a.show();
log.error(e.getMessage());
log.error("Recipe not created.");
Blersch Lara
committed
} catch (MalformedURLException | URISyntaxException e) {
recipe.setImage(Recipe.class.getResource("/images/dish-fork-and-knife.png"));
log.error("Invalid image path, loaded default image.");
log.info("Recipe '{}' was created.", recipe.getName());
changeScene(View.RECIPE_VIEW, recipe);
}
}
@FXML
public void incrementPageCounter() {
//increment the current page and re-render
currentPage = Math.min(++currentPage, maxPages);
log.debug("User selected page {} / {}", currentPage, maxPages);
drawIngredientSearchResults();
}
@FXML
public void decrementPageCounter() {
//decrement the current page and re-render
currentPage = Math.max(0, --currentPage);
log.debug("User selected page {} / {}", currentPage, maxPages);
drawIngredientSearchResults();
}
@FXML
public void changeSceneToRecipe() {
changeScene(View.RECIPE_VIEW, recipe);