RecipePopup.java
package no.ntnu.idatt1002.demo.view.components;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.prefs.Preferences;
import java.util.stream.Collectors;
import javax.imageio.ImageIO;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.FileChooser;
import javafx.stage.Popup;
import javafx.util.Pair;
import no.ntnu.idatt1002.demo.Logger;
import no.ntnu.idatt1002.demo.Main;
import no.ntnu.idatt1002.demo.dao.DAO;
import no.ntnu.idatt1002.demo.dao.DBConnectionProvider;
import no.ntnu.idatt1002.demo.data.Item;
import no.ntnu.idatt1002.demo.data.Recipe;
import no.ntnu.idatt1002.demo.data.RecipeIngredient;
import no.ntnu.idatt1002.demo.data.RecipeStep;
import no.ntnu.idatt1002.demo.repo.ItemRegister;
import no.ntnu.idatt1002.demo.view.components.Toast.ToastType;
/**
* A popup for adding items to the inventory.
*/
public class RecipePopup extends Popup {
Pair<Integer, Integer> pair = new Pair<>(1, 1);
/**
* Interface for when the user saves the form.
*/
public interface OnSave {
void cb(int id, String name, String categories, int cookingTime, Map<Integer, Pair<Integer, String>> ingredients,
Map<Integer, Integer> newIngredients, List<RecipeStep> steps, List<RecipeStep> newSteps);
}
private enum Page {
MAIN,
ADD_INGREDIENT,
ADD_STEP
}
/**
* Interface for when the user deletes the item.
*/
public interface OnDelete {
void cb(int id);
}
private OnSave onSave;
private OnDelete onDelete;
private Map<Integer, Integer> newIngredients = new java.util.HashMap<>();
private Page currentPage = Page.MAIN;
private VBox background = new VBox();
private InputField categoryField = new InputField("Category");
private InputField nameField = new InputField("Name");
private NumberField timefield = new NumberField();
private List<RecipeIngredientField> ingredientsField;
private PrimaryButton deleteButton;
private PrimaryButton submitButton;
private Text title;
private PrimaryButton addIngredientButton;
private VBox addIngredientBox;
private PageSwitcher pageSwitcher;
private List<StepField> stepsField;
private List<StepField> newSteps;
private PrimaryButton addStepButton;
private PrimaryButton imageButton;
/**
* Constructor for the AddPopup.
*/
public RecipePopup(Recipe recipe) {
super();
this.setAutoHide(true); // close the popup automatically
// Create a container for the popup
StackPane container = new StackPane();
// Create a VBox to hold the content of the popup
background.getStyleClass().add("popup-background");
background.setSpacing(20);
this.getContent().add(container);
container.getChildren().add(background);
// create a cross button to close the popup
CrossButton crossButton = new CrossButton();
crossButton.setOnAction(e -> this.hide());
crossButton.setPadding(new Insets(10));
container.getChildren().add(crossButton);
StackPane.setAlignment(crossButton, Pos.TOP_RIGHT);
// Add a title to the popup
title = new Text(recipe.getName());
title.getStyleClass().addAll("popup-title", "centered");
nameField.setText(recipe.getName());
categoryField.setText(recipe.getCategory());
timefield.setText(Integer.toString(recipe.getCookingTime()));
List<RecipeIngredient> recipes = recipe.getIngredients();
ingredientsField = recipes.stream()
.map(RecipeIngredientField::new)
.collect(Collectors.toList());
ingredientsField.forEach(field -> {
field.setOnDelete(() -> {
ingredientsField.remove(field);
background.getChildren().remove(field);
});
});
addIngredientBox = new VBox();
Text addIngredientTitle = new Text("Added ingredients:");
HBox box = new HBox();
box.setSpacing(2);
if (newIngredients != null) {
newIngredients.forEach((k, v) -> {
box.getChildren().add(new Text(Integer.toString(k)));
});
addIngredientBox.getChildren().addAll(addIngredientTitle, box);
}
addIngredientButton = new PrimaryButton("Add Ingredient");
addIngredientButton.setOnAction(e -> {
AddPopup addPopup = new AddPopup("Ingredient");
ItemRegister itemRegister = new ItemRegister(new DAO(new DBConnectionProvider()));
itemRegister.getAllItems();
Map<Integer, Item> items = itemRegister.getItems();
// Remove the items that are already in the recipe
items = items.entrySet().stream()
.filter(entry -> recipe.getIngredients().stream()
.noneMatch(ingredient -> ingredient.getItemId() == entry.getKey()))
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
addPopup.addField(Field.ofMap("Ingredients", items));
addPopup.setOnAdd((Object[] o) -> {
if (o == null) {
return;
}
Object[] recipeIngredients = (Object[]) o[0];
for (Object ingredient : recipeIngredients) {
int id = (int) ingredient;
newIngredients.put(id, 1);
box.getChildren().add(new Text(Integer.toString(id)));
}
});
addPopup.show(this.getScene().getWindow());
});
stepsField = recipe.getInstructions().stream()
.map(StepField::new)
.collect(Collectors.toList());
stepsField.forEach(field -> field.setOnDelete(() -> {
stepsField.remove(field);
background.getChildren().remove(field);
}));
newSteps = new ArrayList<>();
addStepButton = new PrimaryButton("Add Step");
addStepButton.setOnAction(e -> {
AddPopup addPopup = new AddPopup("Step");
addPopup.addField(Field.ofString("Instruction"));
addPopup.setOnAdd((Object[] o) -> {
String instruction = (String) o[0];
RecipeStep step = new RecipeStep(
recipe.getId(),
recipe.getInstructions().size() + newSteps.size() + 1,
instruction);
StepField field = new StepField(step);
newSteps.add(field);
switchPopupPage(currentPage);
});
addPopup.show(this.getScene().getWindow());
});
newSteps.forEach(field -> field.setOnDelete(() -> {
stepsField.remove(field);
background.getChildren().remove(field);
}));
// Add a button to submit the form
submitButton = new PrimaryButton(
"Save");
submitButton.setButtonType(PrimaryButton.Type.SECONDARY);
submitButton.setOnAction(e -> {
if (onSave != null) {
onSave.cb(
recipe.getId(),
nameField.getText(),
categoryField.getText(),
timefield.getValue(),
ingredientsField.stream()
.collect(Collectors.toMap(
RecipeIngredientField::getRecipeId,
field -> new Pair<Integer, String>(
field.getRecipeAmount(),
field.getUnit()))),
this.newIngredients,
stepsField.stream()
.map(StepField::getStep)
.collect(Collectors.toList()),
newSteps.stream().map(StepField::getStep).collect(Collectors.toList()));
}
this.hide();
});
imageButton = new PrimaryButton("Change image");
imageButton.setButtonType(PrimaryButton.Type.SECONDARY);
imageButton.setPrefWidth(300);
imageButton.setPrefHeight(50);
imageButton.getStyleClass().add("centered");
imageButton.setOnAction(e -> {
FileChooser fileChooser = new FileChooser();
fileChooser.getExtensionFilters()
.add(new FileChooser.ExtensionFilter("Image Files", "*.png", "*.jpg", "*.jpeg"));
fileChooser.setTitle("Choose an image for " + recipe.getName());
File file = fileChooser.showOpenDialog(null);
if (file != null) {
try {
BufferedImage image = ImageIO.read(file);
String installPath = Preferences.userNodeForPackage(Main.class).get("install_path", null);
File outoutFile;
if (installPath == null) {
outoutFile = new File(
"src" + File.separator + "main"
+ File.separator + "resources"
+ File.separator + "no"
+ File.separator + "ntnu"
+ File.separator + "idatt1002"
+ File.separator + "images"
+ File.separator + "recipes"
+ File.separator + recipe.getId() + ".png");
} else {
outoutFile = new File(
installPath
+ File.separator + "images"
+ File.separator + "recipes"
+ File.separator + recipe.getId() + ".png");
}
if (!outoutFile.exists()) {
outoutFile.mkdirs();
outoutFile.createNewFile();
}
ImageIO.write(
image,
"png",
outoutFile);
ToastProvider.enqueue(
new Toast("Image saved",
"Next time you enter the page, the image will be updated",
ToastType.SUCCESS));
} catch (Exception ex) {
ToastProvider.enqueue(new Toast("error", "Could not save image", ToastType.ERROR));
}
}
});
submitButton.setPrefWidth(300);
submitButton.setPrefHeight(50);
// Add a button to delete the item
deleteButton = new PrimaryButton(
"Delete");
deleteButton.setButtonType(PrimaryButton.Type.RED);
deleteButton.setOnAction(e -> {
if (onDelete != null) {
onDelete.cb(recipe.getId());
}
this.hide();
});
deleteButton.setPrefWidth(300);
deleteButton.setPrefHeight(50);
pageSwitcher = new PageSwitcher();
pageSwitcher.setOnSwitch(i -> {
int currentPageIndex = currentPage.ordinal();
currentPageIndex += i;
if (Page.values().length <= currentPageIndex) {
currentPageIndex = 0;
} else if (currentPageIndex < 0) {
currentPageIndex = Page.values().length - 1;
}
currentPage = Page.values()[currentPageIndex];
switchPopupPage(currentPage);
});
switchPopupPage(currentPage);
}
private void switchPopupPage(Page page) {
background.getChildren().clear();
background.getChildren().add(title);
switch (page) {
case MAIN:
background.getChildren().addAll(nameField, categoryField, timefield, imageButton);
break;
case ADD_INGREDIENT:
Text subtitle = new Text("Ingredients");
background.getChildren().add(subtitle);
background.getChildren().addAll(ingredientsField);
background.getChildren().addAll(addIngredientButton, addIngredientBox);
break;
case ADD_STEP:
Text stepTitle = new Text("Steps");
background.getChildren().addAll(stepTitle);
background.getChildren().addAll(stepsField);
Text newStepTitle = new Text("New Steps");
background.getChildren().add(newStepTitle);
background.getChildren().addAll(newSteps);
background.getChildren().add(addStepButton);
break;
default:
Logger.fatal("Unknown page: " + page);
break;
}
background.getChildren().addAll(submitButton, deleteButton, pageSwitcher);
}
public RecipePopup setOnSave(OnSave onSave) {
this.onSave = onSave;
return this;
}
public RecipePopup setOnDelete(OnDelete onDelete) {
this.onDelete = onDelete;
return this;
}
}