Axel Nordh 2 лет назад
Родитель
Сommit
b1fa6a96ba
29 измененных файлов с 1473 добавлено и 92 удалено
  1. 3 0
      .gitignore
  2. BIN
      output.png
  3. 17 3
      pom.xml
  4. 1 0
      src/main/java/module-info.javaOLD
  5. 10 1
      src/main/java/nordh/xyz/App.java
  6. 389 0
      src/main/java/nordh/xyz/Database/DatabaseConnection.java
  7. 280 0
      src/main/java/nordh/xyz/MainController.java
  8. 226 0
      src/main/java/nordh/xyz/PlannerController.java
  9. 0 12
      src/main/java/nordh/xyz/PrimaryController.java
  10. 0 12
      src/main/java/nordh/xyz/SecondaryController.java
  11. 98 0
      src/main/java/nordh/xyz/objects/Ingredient.java
  12. 253 0
      src/main/java/nordh/xyz/objects/Recepie.java
  13. BIN
      src/main/resources/images/recycleArrowImage.jpg
  14. BIN
      src/main/resources/images/recycleArrowImage.tiff
  15. BIN
      src/main/resources/images/recycleArrowImageShrunk.jpg
  16. BIN
      src/main/resources/images/vecteezy_recycle-and-ecology-icons-collection-set-of-circle-arrow_20143023.jpg
  17. 14 0
      src/main/resources/nordh/xyz/ingredientListGui.fxml
  18. 114 0
      src/main/resources/nordh/xyz/main.fxml
  19. 36 0
      src/main/resources/nordh/xyz/planner.fxml
  20. 0 16
      src/main/resources/nordh/xyz/primary.fxml
  21. 32 0
      src/main/resources/nordh/xyz/recepie.fxml
  22. 0 16
      src/main/resources/nordh/xyz/secondary.fxml
  23. BIN
      target/classes/module-info.class
  24. BIN
      target/classes/nordh/xyz/App.class
  25. BIN
      target/classes/nordh/xyz/PrimaryController.class
  26. BIN
      target/classes/nordh/xyz/SecondaryController.class
  27. 0 16
      target/classes/nordh/xyz/primary.fxml
  28. 0 16
      target/classes/nordh/xyz/secondary.fxml
  29. BIN
      tempname.png

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+/target/**/*
+*.log
+.vscode


+ 17 - 3
pom.xml

@@ -13,12 +13,17 @@
         <dependency>
             <groupId>org.openjfx</groupId>
             <artifactId>javafx-controls</artifactId>
-            <version>13</version>
+            <version>21.0.2</version>
         </dependency>
         <dependency>
             <groupId>org.openjfx</groupId>
             <artifactId>javafx-fxml</artifactId>
-            <version>13</version>
+            <version>21.0.2</version>
+        </dependency>
+        <dependency>
+            <groupId>com.mysql</groupId>
+            <artifactId>mysql-connector-j</artifactId>
+            <version>8.3.0</version>
         </dependency>
     </dependencies>
     <build>
@@ -34,7 +39,7 @@
             <plugin>
                 <groupId>org.openjfx</groupId>
                 <artifactId>javafx-maven-plugin</artifactId>
-                <version>0.0.6</version>
+                <version>0.0.8</version>
                 <executions>
                     <execution>
                         <!-- Default configuration for running -->
@@ -44,6 +49,15 @@
                             <mainClass>nordh.xyz.App</mainClass>
                         </configuration>
                     </execution>
+                                    <execution>
+                    <id>debug</id>
+                    <configuration>
+                        <mainClass>nordh.xyz.App</mainClass>
+                        <options>
+                            <option>-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=*:8000</option>
+                        </options>
+                    </configuration>
+                </execution>
                 </executions>
             </plugin>
         </plugins>

+ 1 - 0
src/main/java/module-info.java → src/main/java/module-info.javaOLD

@@ -3,5 +3,6 @@ module nordh.xyz {
     requires javafx.fxml;
 
     opens nordh.xyz to javafx.fxml;
+
     exports nordh.xyz;
 }

+ 10 - 1
src/main/java/nordh/xyz/App.java

@@ -1,6 +1,7 @@
 package nordh.xyz;
 
 import javafx.application.Application;
+import javafx.application.HostServices;
 import javafx.fxml.FXMLLoader;
 import javafx.scene.Parent;
 import javafx.scene.Scene;
@@ -15,11 +16,19 @@ public class App extends Application {
 
     private static Scene scene;
 
+    private static HostServices hs;
+
     @Override
     public void start(Stage stage) throws IOException {
-        scene = new Scene(loadFXML("primary"), 640, 480);
+        scene = new Scene(loadFXML("main"), 1280, 960);
         stage.setScene(scene);
+        stage.setMaximized(true);
         stage.show();
+        hs = getHostServices();
+    }
+
+    public static HostServices getHostService() {
+        return hs;
     }
 
     static void setRoot(String fxml) throws IOException {

+ 389 - 0
src/main/java/nordh/xyz/Database/DatabaseConnection.java

@@ -0,0 +1,389 @@
+package nordh.xyz.Database;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.awt.image.BufferedImage;
+import javax.imageio.ImageIO;
+
+import com.mysql.cj.x.protobuf.MysqlxPrepare.Prepare;
+
+import nordh.xyz.objects.Ingredient;
+import nordh.xyz.objects.Recepie;
+
+public class DatabaseConnection {
+    private static final String USERNAME = "recepieUser";
+    private static final String PASSWORD = "v.FV-ssZPzVyev28";
+    private static final String DATABASE = "recept";
+    private static final String URL = "jdbc:mysql://nordh.xyz:3306/";
+
+    Connection conn;
+    private static DatabaseConnection instance = new DatabaseConnection();
+
+    private DatabaseConnection() {
+    }
+
+    public static DatabaseConnection getInstance() {
+        return instance;
+    }
+
+    private Connection getConnection() {
+        if (conn == null) {
+            try {
+                conn = DriverManager.getConnection(URL + DATABASE, USERNAME, PASSWORD);
+            } catch (SQLException e) {
+                System.out.println("EXCEPTION: getConnection " + e.getSQLState() + e.getMessage());
+            }
+        }
+
+        return conn;
+    }
+
+    public Set<Recepie> getRecepiesByDescription(String searchString) {
+        Set<Recepie> result = new HashSet<Recepie>();
+        String[] split = searchString.split(" ");
+
+        StringBuilder sb = new StringBuilder();
+        for (String word : split) {
+            sb.append("description LIKE '%" + word + "%' AND ");
+        }
+
+        String whereString = sb.substring(0, sb.length() - 4);
+
+        String sql = "SELECT * FROM recepie WHERE " + whereString;
+
+        addRecepie(result, sql);
+
+        return result;
+    }
+
+    private void addRecepie(Set<Recepie> result, String sql) {
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            ResultSet rs = stat.executeQuery();
+
+            while (rs.next()) {
+                Recepie r = getRecepieFromResultSet(rs);
+                result.add(r);
+            }
+        } catch (SQLException e) {
+            System.out.println("Something wrong with SQL " + sql);
+            e.printStackTrace();
+        }
+    }
+
+    private Recepie getRecepieFromResultSet(ResultSet rs) throws SQLException {
+        Recepie r = new Recepie();
+        r.setId(rs.getInt("id"));
+        r.setIngredients(getRecepieIngredients(r.getId()));
+        r.setName(rs.getString("name"));
+        r.setDescription(rs.getString("description"));
+        r.setUrl(rs.getString("url"));
+
+        String tagSql = "SELECT group_concat(rt.name) as tags FROM recepieTags rt JOIN recepieTagLinkTable rtlt ON rt.id = rtlt.tagId WHERE rtlt.recepieId = ? GROUP BY recepieId";
+
+        try (PreparedStatement stat2 = getConnection().prepareStatement(tagSql)) {
+            stat2.setInt(1, rs.getInt("id"));
+            ResultSet rs2 = stat2.executeQuery();
+
+            while (rs2.next()) {
+                r.setTags(Arrays.asList(rs2.getString("tags").split(",")));
+            }
+        } catch (SQLException e1) {
+            System.out.println("Something wrong with SQL " + tagSql);
+            e1.printStackTrace();
+        }
+        return r;
+    }
+
+    public File getRecepieImage(int recepieId) {
+        String sql = "SELECT image FROM recepieImage WHERE recepieId = ?";
+        File result = null;
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            stat.setInt(1, recepieId);
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                Blob b = rs.getBlob("image");
+                InputStream is = b.getBinaryStream(1, b.length());
+                BufferedImage image = ImageIO.read(is);
+
+                if (image != null) {
+                    File outputFile = new File("output.png");
+                    ImageIO.write(image, "png", outputFile);
+
+                    result = outputFile;
+                }
+            }
+        } catch (SQLException e) {
+            System.out.println("Something wrong with SQL " + sql);
+            e.printStackTrace();
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    // public File getRecepieImage(int recepieId) {
+    // File result = new File("tempname.png");
+    // String sql = "SELECT image FROM recepieImage WHERE recepieId = ?";
+    // try (PreparedStatement stat = getConnection().prepareStatement(sql);
+    // FileOutputStream fos = new FileOutputStream(result);) {
+
+    // stat.setInt(1, recepieId);
+    // ResultSet rs = stat.executeQuery();
+
+    // byte[] buffer = new byte[1];
+    // while (rs.next()) {
+    // InputStream is = rs.getBinaryStream("image");
+    // while (is.read(buffer) > 0) {
+    // fos.write(buffer);
+    // }
+    // is.close();
+    // }
+
+    // } catch (SQLException | IOException e) {
+    // System.out.println("EXCEPTION getRecepieImage Something wrong with SQL " +
+    // sql);
+    // e.printStackTrace();
+    // }
+
+    // return result;
+    // }
+
+    public List<Ingredient> getRecepieIngredients(int recepieId) {
+        List<Ingredient> result = new ArrayList<Ingredient>();
+        String sql = "SELECT * FROM ingredient i JOIN recepieIngredientLink ril ON ril.ingredientId = i.id WHERE ril.recepieId = ?";
+
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            stat.setInt(1, recepieId);
+
+            ResultSet rs = stat.executeQuery();
+
+            while (rs.next()) {
+                Ingredient i = new Ingredient();
+                i.setIngredientId(rs.getInt("id"));
+                i.setName(rs.getString("name"));
+                i.setAmount(rs.getString("amount"));
+                i.setMeasure(rs.getString("measurement"));
+
+                result.add(i);
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION IN getRecepieIngredients sql " + sql);
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    public Set<Recepie> getRecepiesByName(String searchString) {
+        Set<Recepie> result = new HashSet<Recepie>();
+        String[] split = searchString.split(" ");
+
+        StringBuilder sb = new StringBuilder();
+        for (String word : split) {
+            sb.append("name like '%" + word + "%' AND ");
+        }
+        String whereString = sb.substring(0, sb.length() - 4);
+        String sql = "SELECT * FROM recepie WHERE " + whereString;
+
+        addRecepie(result, sql);
+
+        return result;
+    }
+
+    public Set<Recepie> getRecepiesByIngredients(String searchString) {
+        Set<Recepie> result = new HashSet<Recepie>();
+
+        String[] split = searchString.split(" ");
+        StringBuilder sb = new StringBuilder();
+        for (String word : split) {
+            sb.append("i.name LIKE '%" + word + "%' AND ");
+        }
+        String whereString = sb.substring(0, sb.length() - 4);
+
+        String sql = "SELECT DISTINCT r.id FROM recepie r JOIN recepieIngredientLink ril ON ril.recepieId = r.id JOIN ingredient i ON i.id = ril.ingredientId WHERE "
+                + whereString;
+
+        StringBuilder sb2 = new StringBuilder();
+
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                sb2.append(rs.getInt("id") + ",");
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION getRecepiesByIngredients sql: " + sql);
+            e.printStackTrace();
+        }
+        if (sb2.length() > 0) {
+            sb2.substring(0, sb2.length() - 1);
+            String recepieSql = "SELECT * FROM recepie where id IN (" + sb2.substring(0, sb2.length() - 1) + ")";
+
+            addRecepie(result, recepieSql);
+        }
+
+        return result;
+    }
+
+    public Map<Integer, String> getRecepieSteps(int id) {
+        String sql = "SELECT stepNumber, text FROM recepieSteps rs WHERE rs.recepieId = ? ORDER BY stepNumber ASC";
+        Map<Integer, String> steps = new HashMap<>();
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            stat.setInt(1, id);
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                int step = rs.getInt("stepNumber");
+                String text = rs.getString("text");
+
+                steps.put(step, text);
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION getRecepiesByIngredients sql: " + sql);
+            e.printStackTrace();
+        }
+
+        return steps;
+    }
+
+    public List<String> getTags() {
+        String sql = "SELECT DISTINCT(name) as name FROM recepieTags";
+        List<String> result = new ArrayList<>();
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                result.add(rs.getString("name"));
+
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION getTags sql: " + sql);
+            e.printStackTrace();
+        }
+        return result;
+    }
+
+    public void updateRecepieTag(Recepie recepie, String newTag) {
+        String selectSql = "SELECT id FROM recepieTags WHERE name = ?";
+        int tagId = -1;
+        try (PreparedStatement stat = getConnection().prepareStatement(selectSql)) {
+            stat.setString(1, newTag);
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                tagId = rs.getInt("id");
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION updateRecepieTag sql: " + selectSql);
+            e.printStackTrace();
+        }
+
+        if (tagId == -1) {
+            String sql = "INSERT INTO recepieTags (name) VALUES (?)";
+            try (PreparedStatement stat = getConnection().prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);) {
+                stat.setString(1, newTag);
+                stat.executeUpdate();
+                ResultSet rs = stat.getGeneratedKeys();
+                while (rs.next()) {
+                    tagId = rs.getInt(1);
+                }
+            } catch (SQLException e) {
+                System.out.println("EXCEPTION updateRecepieTag sql: " + sql);
+                e.printStackTrace();
+            }
+        }
+
+        if (tagId > 0) {
+            String linkTableInsertSql = "INSERT INTO recepieTagLinkTable (recepieId, tagId) VALUES (?, ?)";
+            try (PreparedStatement stat = getConnection().prepareStatement(linkTableInsertSql)) {
+                stat.setInt(1, recepie.getId());
+                stat.setInt(2, tagId);
+
+                stat.executeUpdate();
+            } catch (SQLException e) {
+                System.out.println("EXCEPTION updateRecepieTag sql: " + linkTableInsertSql);
+                e.printStackTrace();
+            }
+        }
+    }
+
+    public Recepie getRandomRecepie() {
+        String sql = "SELECT * FROM recepie ORDER BY RAND() LIMIT 1";
+        Recepie result = null;
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                result = getRecepieFromResultSet(rs);
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION getRandomRecepie sql: " + sql);
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    public Recepie getRandomRecepie(List<String> selectedTags, boolean andSelected) {
+        StringBuilder sb = new StringBuilder();
+
+        String sql;
+        if (!andSelected) {
+            sb.append(String.join("','", selectedTags));
+            sb.insert(0, '\'');
+            sb.append("'");
+            sql = "SELECT r.* FROM recepie r JOIN recepieTagLinkTable rtlt ON rtlt.recepieId = r.id JOIN recepieTags rt ON rt.id = rtlt.tagId WHERE rt.name IN ("
+                    + sb.toString() + ") ORDER BY RAND() LIMIT 1";
+        } else {
+            sb.append(
+                    "SELECT r.* FROM recepie r JOIN recepieTagLinkTable rtlt ON rtlt.recepieId = r.id JOIN recepieTags rt ON rt.id = rtlt.tagId WHERE rt.name = ?");
+            for (int i = 1; i < selectedTags.size(); i++) {
+                sb.append(" AND rt.name = ?");
+            }
+            sb.append(" ORDER BY RAND() LIMIT 1");
+            sql = sb.toString();
+        }
+        Recepie result = null;
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            if (andSelected) {
+                for (int i = 0; i < selectedTags.size(); i++) {
+                    stat.setString(i + 1, selectedTags.get(i));
+                }
+            }
+            ResultSet rs = stat.executeQuery();
+            while (rs.next()) {
+                result = getRecepieFromResultSet(rs);
+            }
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION getRandomRecepie sql: " + sql);
+            e.printStackTrace();
+        }
+
+        return result;
+    }
+
+    public void removeTag(Recepie recepie, String text) {
+        String sql = "DELETE FROM recepieTagLinkTable rtlt WHERE recepieId = ? AND tagId = (SELECT id FROM recepieTags WHERE name = ?)";
+
+        try (PreparedStatement stat = getConnection().prepareStatement(sql)) {
+            stat.setInt(1, recepie.getId());
+            stat.setString(2, text);
+
+            stat.execute();
+        } catch (SQLException e) {
+            System.out.println("EXCEPTION removeTag sql: " + sql);
+            e.printStackTrace();
+        }
+    }
+
+}

+ 280 - 0
src/main/java/nordh/xyz/MainController.java

@@ -0,0 +1,280 @@
+package nordh.xyz;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import javax.swing.DesktopManager;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.beans.value.ObservableValueBase;
+import javafx.fxml.FXML;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.control.ScrollPane;
+import javafx.scene.control.TextField;
+import javafx.scene.input.KeyCode;
+import javafx.scene.layout.AnchorPane;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.VBox;
+import nordh.xyz.Database.DatabaseConnection;
+import nordh.xyz.objects.Ingredient;
+import nordh.xyz.objects.Recepie;
+
+public class MainController {
+
+    @FXML
+    TextField searchRecepieTextField;
+    @FXML
+    Button searchRecepieButton;
+    @FXML
+    Label nameCountLabel;
+    @FXML
+    Label ingredientCountLabel;
+    @FXML
+    Label descriptionCountLabel;
+    @FXML
+    Label totalCountLabel;
+    @FXML
+    ScrollPane recepieScrollPane;
+    @FXML
+    VBox recepieListContent;
+    @FXML
+    Pane RecepieRootPanel;
+
+    @FXML
+    ListView<Ingredient> excludedIngredientsListView;
+    @FXML
+    ListView<Ingredient> includedIngredientsListView;
+
+    @FXML
+    Button ExcludeIngredientButton;
+    @FXML
+    Button RemoveExcludedIngredientButton;
+    @FXML
+    Button IncludeIngredientButton;
+    @FXML
+    Button RemoveIncludedButton;
+
+    @FXML
+    ListView<Ingredient> ingredientListView;
+
+    private Set<Recepie> recepiesByName = new HashSet<>();
+    private Set<Recepie> recepiesByDescription = new HashSet<>();
+    private Set<Recepie> recepiesByIngredients = new HashSet<>();
+
+    private Set<Recepie> allRecepies = new HashSet<>();
+    private Set<Recepie> activeRecepies;
+
+    @FXML
+    public void initialize() {
+        searchRecepieTextField.setOnKeyPressed(event -> {
+            if (event.getCode().equals(KeyCode.ENTER)) {
+                SearchRecepieButtonAction();
+            }
+        });
+
+        searchRecepieTextField.textProperty().addListener((observable, oldValue, newValue) -> {
+            if (newValue.length() > 0) {
+                searchRecepieButton.setDisable(false);
+            } else {
+                searchRecepieButton.setDisable(true);
+            }
+        });
+
+        ingredientListView.getSelectionModel().selectedIndexProperty().addListener(new ChangeListener<Number>() {
+            @Override
+            public void changed(ObservableValue<? extends Number> arg0, Number oldValue, Number newValue) {
+                setIngredientButtonsState();
+            }
+        });
+
+        excludedIngredientsListView.getSelectionModel().selectedIndexProperty()
+                .addListener(new ChangeListener<Number>() {
+                    @Override
+                    public void changed(ObservableValue<? extends Number> arg0, Number oldValue, Number newValue) {
+                        setIngredientButtonsState();
+                    }
+                });
+
+        includedIngredientsListView.getSelectionModel().selectedIndexProperty()
+                .addListener(new ChangeListener<Number>() {
+                    @Override
+                    public void changed(ObservableValue<? extends Number> arg0, Number oldValue, Number newValue) {
+                        setIngredientButtonsState();
+                    }
+                });
+    }
+
+    protected void setIngredientButtonsState() {
+        ExcludeIngredientButton.setDisable(ingredientListView.getSelectionModel().getSelectedItems().size() <= 0);
+        RemoveExcludedIngredientButton
+                .setDisable(excludedIngredientsListView.getSelectionModel().getSelectedItems().size() <= 0);
+        IncludeIngredientButton.setDisable(ingredientListView.getSelectionModel().getSelectedItems().size() <= 0);
+        RemoveIncludedButton.setDisable(includedIngredientsListView.getSelectionModel().getSelectedItems().size() <= 0);
+    }
+
+    @FXML
+    private void nameLabelClicked() {
+        updateRecepieList(recepiesByName);
+    }
+
+    @FXML
+    private void descriptionLabelClicked() {
+        updateRecepieList(recepiesByDescription);
+    }
+
+    @FXML
+    private void ingredientLabelClicked() {
+        updateRecepieList(recepiesByIngredients);
+    }
+
+    @FXML
+    private void totalLabelClicked() {
+        updateRecepieList(allRecepies);
+    }
+
+    @FXML
+    private void SwitchToPlanner() throws IOException {
+        App.setRoot("planner");
+    }
+
+    @FXML
+    private void SearchRecepieButtonAction() {
+        allRecepies.clear();
+        ingredientListView.getItems().clear();
+        recepiesByName = DatabaseConnection.getInstance().getRecepiesByName(searchRecepieTextField.getText());
+        recepiesByDescription = DatabaseConnection.getInstance()
+                .getRecepiesByDescription(searchRecepieTextField.getText());
+        recepiesByIngredients = DatabaseConnection.getInstance()
+                .getRecepiesByIngredients(searchRecepieTextField.getText());
+
+        addRecepiesToTotalSet(recepiesByName);
+        addRecepiesToTotalSet(recepiesByDescription);
+        addRecepiesToTotalSet(recepiesByIngredients);
+
+        nameCountLabel.setText(String.valueOf(recepiesByName.size()));
+        descriptionCountLabel.setText(String.valueOf(recepiesByDescription.size()));
+        ingredientCountLabel.setText(String.valueOf(recepiesByIngredients.size()));
+        totalCountLabel.setText(String.valueOf(allRecepies.size()));
+
+        updateRecepieList(allRecepies);
+    }
+
+    private void addRecepiesToTotalSet(Set<Recepie> recepies) {
+        for (Recepie recepie : recepies) {
+            if (allRecepies.stream().noneMatch(p -> p.getName().equals(recepie.getName()))) {
+                allRecepies.add(recepie);
+            }
+        }
+    }
+
+    private void updateRecepieList(Set<Recepie> setToLoad) {
+        recepieListContent.getChildren().clear();
+        List<Recepie> sortedRecepies = new ArrayList<Recepie>(setToLoad);
+        sortedRecepies.sort((r1, r2) -> r1.getName().compareTo(r2.getName()));
+
+        List<Ingredient> excluded = excludedIngredientsListView.getItems();
+        List<Ingredient> included = includedIngredientsListView.getItems();
+
+        for (Ingredient ingredient : excluded) {
+            sortedRecepies.removeAll(
+                    sortedRecepies.stream().filter(p -> p.containsIngredient(ingredient)).collect(Collectors.toList()));
+        }
+
+        for (Ingredient ingredient : included) {
+            sortedRecepies.removeAll(sortedRecepies.stream().filter(p -> !p.containsIngredient(ingredient))
+                    .collect(Collectors.toList()));
+        }
+
+        sortedRecepies.forEach(r -> {
+            Label recepieLabel = new Label(r.getName());
+            recepieLabel.setOnMouseClicked(event -> {
+                loadRecepie(recepieLabel.getText());
+            });
+            recepieListContent.getChildren().add(recepieLabel);
+        });
+        activeRecepies = setToLoad;
+        updateIngredientList(setToLoad);
+    }
+
+    private void updateIngredientList(Set<Recepie> setToLoad) {
+        ingredientListView.getItems().clear();
+        Set<Ingredient> ingredients = new HashSet<>();
+
+        for (Recepie recepie : setToLoad) {
+            for (Ingredient ingredient : recepie.getIngredients()) {
+                if (ingredients.stream().noneMatch(p -> p.getFilteredName().equals(ingredient.getFilteredName()))) {
+                    ingredients.add(ingredient);
+                }
+            }
+        }
+
+        ArrayList<Ingredient> sortedIngredients = new ArrayList<>(ingredients);
+        sortedIngredients.sort((i1, i2) -> i1.getFilteredName().compareToIgnoreCase(i2.getFilteredName()));
+
+        ingredientListView.getItems().addAll(sortedIngredients);
+    }
+
+    private void loadRecepie(String text) {
+        if (activeRecepies.size() > 0) {
+            Optional<Recepie> first = activeRecepies.stream().filter(p -> p.getName().equals(text)).findFirst();
+            if (first.isPresent()) {
+                RecepieRootPanel.getChildren().clear();
+                RecepieRootPanel.getChildren().add(first.get().getGui());
+            } else {
+                Alert alert = new Alert(Alert.AlertType.INFORMATION);
+                alert.setTitle("No Recepies");
+                alert.setHeaderText("Clicked on " + text + " but no recepie with that name found!");
+            }
+        } else {
+            Alert alert = new Alert(Alert.AlertType.INFORMATION);
+            alert.setTitle("No Recepies");
+            alert.setHeaderText("Clicked on " + text + " but no active recepies!");
+        }
+
+    }
+
+    @FXML
+    private void ExcludeIngredient() {
+        Ingredient i = ingredientListView.getSelectionModel().getSelectedItem();
+        excludedIngredientsListView.getItems().add(i);
+        ingredientListView.getItems().remove(i);
+        ingredientListView.getSelectionModel().select(-1);
+
+        updateRecepieList(activeRecepies);
+    }
+
+    @FXML
+    private void RemoveExcludedIngredient() {
+        Ingredient i = excludedIngredientsListView.getSelectionModel().getSelectedItem();
+        excludedIngredientsListView.getItems().remove(i);
+        ingredientListView.getItems().add(i);
+        updateRecepieList(activeRecepies);
+    }
+
+    @FXML
+    private void IncludeIngredient() {
+        Ingredient i = ingredientListView.getSelectionModel().getSelectedItem();
+        ingredientListView.getItems().remove(i);
+        includedIngredientsListView.getItems().add(i);
+        updateRecepieList(activeRecepies);
+    }
+
+    @FXML
+    private void RemoveIncluded() {
+        Ingredient i = includedIngredientsListView.getSelectionModel().getSelectedItem();
+        includedIngredientsListView.getItems().remove(i);
+        ingredientListView.getItems().add(i);
+        updateRecepieList(activeRecepies);
+    }
+
+}

+ 226 - 0
src/main/java/nordh/xyz/PlannerController.java

@@ -0,0 +1,226 @@
+package nordh.xyz;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.stream.Collector;
+import java.util.stream.Collectors;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.geometry.Insets;
+import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListCell;
+import javafx.scene.control.ListView;
+import javafx.scene.control.RadioButton;
+import javafx.scene.control.Toggle;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Pane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.VBox;
+import nordh.xyz.Database.DatabaseConnection;
+import nordh.xyz.objects.Recepie;
+
+/**
+ * Denna ska planera X rätter. (Default 5) Rätterna ska vara varierade, t.ex. 1
+ * pasta, 1 fisk, 1 gryta/soppa osv.. Den ska i framtiden hålla koll på om man
+ * haft receptet tidigare och inte ge samma igen. Man ska kunna betygsätta
+ * recepten i efterhand (barn, vuxen.. eller varje familjemedlem)
+ * 
+ * <p>
+ * == Kanske för en egen panel==
+ * </p>
+ * <p>
+ * Det måste finna ett sätt att klassa maträtterna, och spara detta i en
+ * databas. om det är Vegetariskt, Veganskt, pasta, fisk, soppa, efterrätt,
+ * huvudrätt.. någon form av taggning.
+ * </p>
+ * 
+ */
+public class PlannerController {
+
+    @FXML
+    Pane recepiePane;
+    @FXML
+    ListView<RecepieListViewItem> generatedRecepiesListView;
+    @FXML
+    VBox generateSettingsVBox;
+    protected boolean andSelected = true;
+
+    @FXML
+    public void initialize() {
+        generatedRecepiesListView.getSelectionModel().selectedItemProperty().addListener((event) -> {
+            updateRecepieView(generatedRecepiesListView.getSelectionModel().getSelectedItem());
+        });
+
+        generatedRecepiesListView.setCellFactory(param -> new ListCell<RecepieListViewItem>() {
+            protected void updateItem(RecepieListViewItem item, boolean empty) {
+                super.updateItem(item, empty);
+                if (empty || item == null) {
+                    // EMPTY
+                } else {
+                    setGraphic(item.getGui());
+                }
+            };
+        });
+
+        generateSettingsPanel();
+    }
+
+    private void generateSettingsPanel() {
+        List<String> tags = DatabaseConnection.getInstance().getTags();
+
+        ToggleGroup buttonGroup = new ToggleGroup();
+        HBox buttonBox = new HBox();
+
+        buttonBox.setPadding(new Insets(10d));
+
+        RadioButton andRadiobutton = new RadioButton("AND");
+        RadioButton orRadiobutton = new RadioButton("OR");
+        andRadiobutton.setUserData("AND");
+        orRadiobutton.setUserData("OR");
+        andRadiobutton.setSelected(true);
+
+        andRadiobutton.setToggleGroup(buttonGroup);
+        orRadiobutton.setToggleGroup(buttonGroup);
+
+        buttonBox.getChildren().addAll(andRadiobutton, orRadiobutton);
+
+        generateSettingsVBox.getChildren().add(buttonBox);
+
+        buttonGroup.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
+
+            @Override
+            public void changed(ObservableValue<? extends Toggle> ov, Toggle oldValue, Toggle newValue) {
+                andSelected = newValue.getUserData().equals("AND");
+            }
+
+        });
+
+        for (String tag : tags) {
+            generateSettingsVBox.getChildren().add(new CheckBox(tag));
+        }
+    }
+
+    @FXML
+    private void switchToMain() throws IOException {
+        App.setRoot("main");
+    }
+
+    @FXML
+    private void GenerateRecepie() {
+        ObservableList<Node> children = generateSettingsVBox.getChildren();
+        List<CheckBox> selectedBoxs = children.stream().filter(p -> p instanceof CheckBox).map(m -> (CheckBox) m)
+                .filter(p -> p.isSelected()).collect(Collectors.toList());
+        if (selectedBoxs.size() == 0) {
+            addRandomRecepieToListView();
+        } else {
+            addRandomRecepieToListView(selectedBoxs);
+        }
+    }
+
+    private void addRandomRecepieToListView(List<CheckBox> selectedBoxs) {
+        List<String> selectedTags = selectedBoxs.stream().map(m -> m.getText()).collect(Collectors.toList());
+        Recepie r = DatabaseConnection.getInstance().getRandomRecepie(selectedTags, andSelected);
+        int counter = 100;
+        boolean failed = false;
+        if (r == null) {
+            failed = true;
+            showNoRecepiesAlert();
+        } else {
+            while (generatedRecepiesListView.getItems().stream().map(ri -> ri.getRecepie().getName())
+                    .collect(Collectors.toList()).contains(r.getName())) {
+                r = DatabaseConnection.getInstance().getRandomRecepie(selectedTags, andSelected);
+                if (r == null || counter-- <= 0) {
+                    showNoRecepiesAlert();
+                    failed = true;
+                    break;
+                }
+            }
+        }
+        if (!failed) {
+            generatedRecepiesListView.getItems().add(new RecepieListViewItem(r));
+        }
+    }
+
+    private void showNoRecepiesAlert() {
+        Alert alert = new Alert(AlertType.INFORMATION);
+        alert.setContentText("There are no more recepies");
+        alert.show();
+    }
+
+    private void addRandomRecepieToListView() {
+        Recepie r = DatabaseConnection.getInstance().getRandomRecepie();
+        int counter = 100;
+        boolean failed = false;
+        while (generatedRecepiesListView.getItems().stream().map(ri -> ri.getRecepie().getName())
+                .collect(Collectors.toList()).contains(r.getName())) {
+            r = DatabaseConnection.getInstance().getRandomRecepie();
+            if (r == null || counter-- <= 0) {
+                showNoRecepiesAlert();
+                failed = true;
+                break;
+            }
+        }
+        if (!failed) {
+            generatedRecepiesListView.getItems().add(new RecepieListViewItem(r));
+        }
+    }
+
+    private void updateRecepieView(RecepieListViewItem r) {
+        recepiePane.getChildren().clear();
+        if (r != null) {
+            recepiePane.getChildren().add(r.getRecepie().getGui());
+        }
+    }
+
+    private class RecepieListViewItem {
+        Recepie r;
+        Button rerollButton;
+
+        RecepieListViewItem(Recepie r) {
+            this.r = r;
+            rerollButton = new Button();
+            Image img = new Image("images/recycleArrowImageShrunk.jpg");
+            ImageView imgView = new ImageView(img);
+            imgView.prefHeight(20);
+            imgView.prefWidth(20);
+            imgView.maxHeight(20);
+            imgView.maxWidth(20);
+            rerollButton.setGraphic(imgView);
+
+            rerollButton.setOnMouseClicked((event) -> {
+                generatedRecepiesListView.getItems().remove(this);
+                GenerateRecepie();
+            });
+        }
+
+        public Recepie getRecepie() {
+            return r;
+        }
+
+        public Node getGui() {
+            HBox result = new HBox();
+            if (r == null || r.getName() == null || r.getName().equals("")) {
+                return result;
+            }
+            HBox.setHgrow(result, Priority.ALWAYS);
+            Label recepieName = new Label(r.toString());
+            recepieName.setPrefWidth(150d);
+            HBox.setHgrow(recepieName, Priority.ALWAYS);
+            result.getChildren().addAll(recepieName, rerollButton);
+
+            return result;
+        }
+    }
+}

+ 0 - 12
src/main/java/nordh/xyz/PrimaryController.java

@@ -1,12 +0,0 @@
-package nordh.xyz;
-
-import java.io.IOException;
-import javafx.fxml.FXML;
-
-public class PrimaryController {
-
-    @FXML
-    private void switchToSecondary() throws IOException {
-        App.setRoot("secondary");
-    }
-}

+ 0 - 12
src/main/java/nordh/xyz/SecondaryController.java

@@ -1,12 +0,0 @@
-package nordh.xyz;
-
-import java.io.IOException;
-import javafx.fxml.FXML;
-
-public class SecondaryController {
-
-    @FXML
-    private void switchToPrimary() throws IOException {
-        App.setRoot("primary");
-    }
-}

+ 98 - 0
src/main/java/nordh/xyz/objects/Ingredient.java

@@ -0,0 +1,98 @@
+package nordh.xyz.objects;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.regex.Pattern;
+
+import javafx.scene.control.Label;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.Region;
+
+public class Ingredient extends Label {
+    String name;
+    String measure;
+    String amount;
+    private int id;
+    String filteredName;
+
+    List<String> filterList = Arrays.asList("låda", "skivad", "handskalad", "liten", "frysta", "klyftor", "riv", "och",
+            "hela", "kokta", "krossade", "färsk", "finhackad", "hackad", "bit", "grovhackade", "malen", "mjölig",
+            "arla", "svenks", "köket", "apetina", "gärna", "dansk", "kallpressad", "kallt", "tun", "små", "röda",
+            "gröna", "gula", "grov", "förp", "färdig", "fryst", "skivor", "pressad", "Svensk", "dansk");
+
+    List<String> wordEndings = Arrays.asList("er", "or", "en", "ar");
+
+    List<String> filterChars = Arrays.asList(",", "\\.", "-", "\\s+i\\s+", "®", "ca" + "\\s+g\\s+", "[0-9]+g", "[0-9]+",
+            "á", "â", "à", "%");
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        filteredName = name;
+        for (String c : filterChars) {
+            filteredName = filteredName.replaceAll("(?i)" + c, "");
+        }
+        for (String s : filterList) {
+            filteredName = filteredName.replaceAll("(?i)" + Pattern.quote(s) + "[a-zåäö]{0,}", "");
+        }
+
+        for (String s : wordEndings) {
+            filteredName = filteredName.replaceAll("(?i)" + s + "\\b", "");
+        }
+
+        filteredName = filteredName.trim();
+        setText(filteredName);
+        this.name = name;
+    }
+
+    public String getFilteredName() {
+        return filteredName;
+    }
+
+    public String getMeasure() {
+        return measure;
+    }
+
+    public void setMeasure(String measure) {
+        this.measure = measure;
+    }
+
+    public String getAmount() {
+        return amount;
+    }
+
+    public void setAmount(String amount) {
+        this.amount = amount;
+    }
+
+    public void setIngredientId(int id) {
+        this.id = id;
+    }
+
+    public int getIngredientId() {
+        return this.id;
+    }
+
+    public HBox getListGui() {
+        HBox root = new HBox(20.0d);
+        Label nameLabel = new Label(name);
+        Region spacer = new Region();
+        HBox.setHgrow(spacer, Priority.ALWAYS);
+        Label amountLabel = new Label(amount);
+        amountLabel.setMinWidth(50d);
+        Label measureLabel = new Label(measure);
+        measureLabel.setMinWidth(50d);
+
+        root.getChildren().addAll(nameLabel, spacer, amountLabel, measureLabel);
+        return root;
+    }
+
+    public Label getIngredientListGui() {
+        Label ingredientLabel = new Label(filteredName);
+        return ingredientLabel;
+    }
+
+}

+ 253 - 0
src/main/java/nordh/xyz/objects/Recepie.java

@@ -0,0 +1,253 @@
+package nordh.xyz.objects;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import javafx.application.HostServices;
+import javafx.event.ActionEvent;
+import javafx.geometry.Pos;
+import javafx.scene.Node;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Button;
+import javafx.scene.control.ButtonType;
+import javafx.scene.control.ComboBox;
+import javafx.scene.control.Hyperlink;
+import javafx.scene.control.Label;
+import javafx.scene.control.ListView;
+import javafx.scene.control.TextArea;
+import javafx.scene.control.TextInputDialog;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.image.Image;
+import javafx.scene.image.ImageView;
+import javafx.scene.layout.BorderPane;
+import javafx.scene.layout.HBox;
+import javafx.scene.layout.VBox;
+import javafx.scene.text.Font;
+import nordh.xyz.App;
+import nordh.xyz.Database.DatabaseConnection;
+
+public class Recepie {
+    private static final String NEW_TAG = "NEW TAG";
+
+    int id;
+
+    String name;
+    String description;
+
+    List<Ingredient> ingredients;
+    private String url;
+    private File recepieImage;
+
+    List<String> tags = new ArrayList<>();
+
+    public List<String> getTags() {
+        return tags;
+    }
+
+    public void setTags(List<String> tags) {
+        this.tags = tags;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public List<Ingredient> getIngredients() {
+        return ingredients;
+    }
+
+    public void setIngredients(List<Ingredient> ingredients) {
+        this.ingredients = ingredients;
+    }
+
+    public void addIngredient(Ingredient ingredient) {
+        if (!ingredients.contains(ingredient)) {
+            ingredients.add(ingredient);
+        }
+    }
+
+    public void setUrl(String url) {
+        this.url = url;
+    }
+
+    public String getUrl() {
+        return url;
+    }
+
+    public void setImage(File recepieImage) {
+        this.recepieImage = recepieImage;
+    }
+
+    private Label getTitle() {
+        Label title = new Label(name);
+        BorderPane.setAlignment(title, Pos.CENTER);
+        Font f = new Font("System Bold", 20.0);
+        title.setFont(f);
+
+        return title;
+    }
+
+    public Node getGui() {
+        BorderPane rootPane = new BorderPane();
+        rootPane.setTop(getTitle());
+        recepieImage = DatabaseConnection.getInstance().getRecepieImage(id);
+
+        VBox recepieBox = new VBox();
+        if (recepieImage != null) {
+            Image img = new Image(recepieImage.toURI().toString());
+            ImageView image = new ImageView(img);
+
+            image.prefWidth(400d);
+            image.prefHeight(400d);
+            recepieBox.getChildren().add(image);
+        }
+
+        recepieBox.getChildren().add(getRecepieDescription());
+
+        Hyperlink recepieUrl = new Hyperlink(url);
+
+        recepieUrl.setOnAction((event) -> {
+            App.getHostService().showDocument(url);
+        });
+
+        recepieBox.getChildren().add(recepieUrl);
+
+        Node tagList = createTagPane();
+
+        rootPane.setCenter(recepieBox);
+        rootPane.setLeft(getIngredientGuiList());
+        rootPane.setRight(getRecepieStepInstructions());
+        rootPane.setBottom(tagList);
+
+        return rootPane;
+    }
+
+    private VBox createTagPane() {
+        VBox result = new VBox();
+
+        for (String tag : tags) {
+            HBox item = createTag(tag);
+
+            result.getChildren().add(item);
+        }
+
+        ComboBox<String> tagList = new ComboBox<>();
+        List<String> totalTags = DatabaseConnection.getInstance().getTags();
+        tagList.getItems().addAll(totalTags);
+        tagList.getItems().add(NEW_TAG);
+
+        tagList.valueProperty().addListener((event) -> {
+            String selectedText = tagList.getSelectionModel().getSelectedItem();
+            if (NEW_TAG.equals(selectedText)) {
+                TextInputDialog ti = new TextInputDialog(NEW_TAG);
+                Optional<String> dialogResult = ti.showAndWait();
+                if (dialogResult.isPresent() && !dialogResult.get().equals(NEW_TAG)) {
+                    String newTag = dialogResult.get();
+                    if (!tags.contains(newTag)) {
+                        this.tags.add(newTag);
+                        DatabaseConnection.getInstance().updateRecepieTag(this, newTag);
+                        HBox newTagBox = createTag(selectedText);
+                        result.getChildren().add(newTagBox);
+                    }
+                }
+            } else {
+                if (!tags.contains(selectedText)) {
+                    tags.add(selectedText);
+                    DatabaseConnection.getInstance().updateRecepieTag(this, selectedText);
+                    HBox newTagBox = createTag(selectedText);
+                    result.getChildren().add(newTagBox);
+                }
+            }
+        });
+
+        result.getChildren().addAll(tagList);
+
+        return result;
+    }
+
+    private HBox createTag(String tag) {
+        HBox item = new HBox();
+
+        Label name = new Label(tag);
+        item.getChildren().add(name);
+        Button remove = new Button("X");
+
+        remove.setOnAction((event) -> {
+            DatabaseConnection.getInstance().removeTag(this, tag);
+            tags.remove(tag);
+        });
+        item.getChildren().add(remove);
+        return item;
+    }
+
+    private Node getRecepieDescription() {
+        TextArea ta = new TextArea(description);
+        ta.setWrapText(true);
+        ta.setEditable(false);
+        ta.setPrefWidth(300d);
+        ta.setMaxWidth(400d);
+
+        return ta;
+    }
+
+    private Node getRecepieStepInstructions() {
+        Map<Integer, String> steps = DatabaseConnection.getInstance().getRecepieSteps(id);
+
+        VBox stepsBox = new VBox();
+        steps.forEach((step, text) -> {
+            HBox stepBox = new HBox(20d);
+
+            Label stepLabel = new Label(String.valueOf(step));
+            Label stepText = new Label(text);
+            stepText.setPrefWidth(300d);
+            stepText.setWrapText(true);
+
+            stepBox.getChildren().addAll(stepLabel, stepText);
+            stepsBox.getChildren().add(stepBox);
+        });
+
+        return stepsBox;
+    }
+
+    private Node getIngredientGuiList() {
+        VBox list = new VBox();
+
+        for (Ingredient ingredient : ingredients) {
+            list.getChildren().add(ingredient.getListGui());
+        }
+
+        return list;
+    }
+
+    public boolean containsIngredient(Ingredient i) {
+        return ingredients.stream().anyMatch(ing -> ing.getFilteredName().equals(i.getFilteredName()));
+    }
+
+    @Override
+    public String toString() {
+        return name;
+    }
+}

BIN
src/main/resources/images/recycleArrowImage.jpg


BIN
src/main/resources/images/recycleArrowImage.tiff


BIN
src/main/resources/images/recycleArrowImageShrunk.jpg


BIN
src/main/resources/images/vecteezy_recycle-and-ecology-icons-collection-set-of-circle-arrow_20143023.jpg


+ 14 - 0
src/main/resources/nordh/xyz/ingredientListGui.fxml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.Region?>
+
+<HBox alignment="CENTER" spacing="5.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
+   <children>
+      <Label text="Ingredient Name" />
+      <Region HBox.hgrow="ALWAYS" />
+      <Label text="Amount" />
+      <Label text="Measure" />
+   </children>
+</HBox>

+ 114 - 0
src/main/resources/nordh/xyz/main.fxml

@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ListView?>
+<?import javafx.scene.control.ScrollPane?>
+<?import javafx.scene.control.SplitPane?>
+<?import javafx.scene.control.TextField?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.VBox?>
+
+<BorderPane prefHeight="960.0" prefWidth="1280.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.MainController">
+   <top>
+      <HBox alignment="CENTER" BorderPane.alignment="CENTER">
+         <children>
+            <Button mnemonicParsing="false" onAction="#SwitchToPlanner" text="Planner" />
+            <TextField fx:id="searchRecepieTextField" promptText="Search" HBox.hgrow="ALWAYS" />
+            <Button fx:id="searchRecepieButton" disable="true" mnemonicParsing="false" onAction="#SearchRecepieButtonAction" text="Search" />
+         </children>
+      </HBox>
+   </top>
+   <left>
+      <VBox fx:id="searchResultRootPanel" prefWidth="400.0" BorderPane.alignment="CENTER">
+         <children>
+            <SplitPane dividerPositions="0.75" prefHeight="160.0" prefWidth="200.0" VBox.vgrow="ALWAYS">
+              <items>
+                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="320.0">
+                     <children>
+                        <VBox AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
+                           <children>
+                              <Label onMouseClicked="#nameLabelClicked" text="Namn" />
+                              <Label onMouseClicked="#ingredientLabelClicked" text="Ingrediens" />
+                              <Label onMouseClicked="#descriptionLabelClicked" text="Beskrivning" />
+                              <Label onMouseClicked="#totalLabelClicked" text="Total" />
+                           </children>
+                        </VBox>
+                     </children>
+                  </AnchorPane>
+                <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="320.0" prefWidth="100.0">
+                     <children>
+                        <VBox AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0">
+                           <children>
+                              <Label fx:id="nameCountLabel" alignment="CENTER" contentDisplay="CENTER" text="0" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+                              <Label fx:id="ingredientCountLabel" alignment="CENTER" contentDisplay="CENTER" text="0" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+                              <Label fx:id="descriptionCountLabel" alignment="CENTER" contentDisplay="CENTER" text="0" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+                              <Label fx:id="totalCountLabel" alignment="CENTER" contentDisplay="CENTER" text="0" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+                           </children>
+                        </VBox>
+                     </children>
+                  </AnchorPane>
+              </items>
+            </SplitPane>
+            <ScrollPane fx:id="recepieScrollPane" prefHeight="200.0" VBox.vgrow="ALWAYS">
+              <content>
+                  <VBox fx:id="recepieListContent" fillWidth="false" />
+              </content>
+            </ScrollPane>
+         </children>
+      </VBox>
+   </left>
+   <center>
+      <VBox fx:id="RecepieRootPanel" prefHeight="200.0" prefWidth="100.0" BorderPane.alignment="CENTER" />
+   </center>
+   <bottom>
+      <HBox alignment="CENTER" BorderPane.alignment="CENTER">
+         <children>
+            <VBox HBox.hgrow="ALWAYS">
+               <children>
+                  <Label alignment="CENTER" contentDisplay="CENTER" text="Excluderade ingredienser" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+                  <ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER">
+                     <content>
+                        <ListView fx:id="excludedIngredientsListView" />
+                     </content>
+                  </ScrollPane>
+               </children>
+            </VBox>
+            <VBox alignment="CENTER" spacing="50.0">
+               <children>
+                  <Button fx:id="ExcludeIngredientButton" disable="true" mnemonicParsing="false" onAction="#ExcludeIngredient" text="&lt;" />
+                  <Button fx:id="RemoveExcludedIngredientButton" disable="true" mnemonicParsing="false" onAction="#RemoveExcludedIngredient" text="&gt;" />
+               </children>
+            </VBox>
+            <VBox HBox.hgrow="ALWAYS">
+               <children>
+                  <Label alignment="CENTER" contentDisplay="CENTER" text="Ingredienser" textAlignment="CENTER" />
+                  <ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER" prefHeight="200.0" VBox.vgrow="ALWAYS">
+                     <content>
+                        <ListView fx:id="ingredientListView" />
+                     </content>
+                  </ScrollPane>
+               </children>
+            </VBox>
+            <VBox alignment="CENTER" spacing="50.0">
+               <children>
+                  <Button fx:id="IncludeIngredientButton" disable="true" mnemonicParsing="false" onAction="#IncludeIngredient" text="&gt;" />
+                  <Button fx:id="RemoveIncludedButton" disable="true" mnemonicParsing="false" onAction="#RemoveIncluded" text="&lt;" />
+               </children>
+            </VBox>
+            <VBox HBox.hgrow="ALWAYS">
+               <children>
+                  <Label alignment="CENTER" contentDisplay="CENTER" text="Inkluderade ingredienser" textAlignment="CENTER" />
+                  <ScrollPane fitToHeight="true" fitToWidth="true" hbarPolicy="NEVER">
+                     <content>
+                        <ListView fx:id="includedIngredientsListView" />
+                     </content>
+                  </ScrollPane>
+               </children>
+            </VBox>
+         </children>
+      </HBox>
+   </bottom>
+</BorderPane>

+ 36 - 0
src/main/resources/nordh/xyz/planner.fxml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ListView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.layout.Pane?>
+<?import javafx.scene.layout.VBox?>
+
+<BorderPane xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.PlannerController">
+         <top>
+            <HBox BorderPane.alignment="CENTER">
+               <children>
+                    <Button fx:id="secondaryButton" onAction="#switchToMain" text="Back to Main" />
+               </children>
+         <BorderPane.margin>
+            <Insets bottom="10.0" />
+         </BorderPane.margin>
+            </HBox>
+         </top>
+         <left>
+            <VBox alignment="TOP_CENTER" prefHeight="200.0" prefWidth="250.0" BorderPane.alignment="CENTER">
+               <children>
+                  <Label contentDisplay="CENTER" text="Planning" textAlignment="CENTER" VBox.vgrow="ALWAYS" />
+            <ListView fx:id="generatedRecepiesListView" prefHeight="200.0" prefWidth="200.0" VBox.vgrow="ALWAYS" />
+            <VBox fx:id="generateSettingsVBox" prefHeight="200.0" prefWidth="100.0" />
+            <Button mnemonicParsing="false" onAction="#GenerateRecepie" text="Generera" />
+               </children>
+            </VBox>
+         </left>
+         <center>
+            <Pane fx:id="recepiePane" minHeight="200.0" minWidth="200.0" BorderPane.alignment="CENTER" />
+         </center>
+      </BorderPane>

+ 0 - 16
src/main/resources/nordh/xyz/primary.fxml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.scene.layout.VBox?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.Button?>
-<?import javafx.geometry.Insets?>
-
-<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.PrimaryController">
-   <children>
-      <Label text="Primary View" />
-      <Button fx:id="primaryButton" text="Switch to Secondary View" onAction="#switchToSecondary"/>
-   </children>
-   <padding>
-      <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
-   </padding>
-</VBox>

+ 32 - 0
src/main/resources/nordh/xyz/recepie.fxml

@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.scene.control.Hyperlink?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.ListView?>
+<?import javafx.scene.image.ImageView?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.VBox?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1">
+   <top>
+      <Label text="Recepie Title" BorderPane.alignment="CENTER">
+         <font>
+            <Font name="System Bold" size="20.0" />
+         </font>
+      </Label>
+   </top>
+   <left>
+      <VBox fx:id="ingredientList" prefWidth="200.0" BorderPane.alignment="CENTER" />
+   </left>
+   <right>
+      <VBox fx:id="stepsList" prefWidth="200.0" BorderPane.alignment="CENTER" />
+   </right>
+   <center>
+      <VBox>
+         <ImageView fx:id="recepieImage" fitHeight="300.0" fitWidth="300.0" pickOnBounds="true" preserveRatio="true" BorderPane.alignment="CENTER" />
+         <Hyperlink fx:id="recepieUrl" text="Recepie Link" VBox.vgrow="ALWAYS" />
+         <ListView fx:id="tagListView" VBox.vgrow="ALWAYS" />
+      </VBox>
+   </center>
+</BorderPane>

+ 0 - 16
src/main/resources/nordh/xyz/secondary.fxml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.scene.layout.VBox?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.Button?>
-<?import javafx.geometry.Insets?>
-
-<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.SecondaryController">
-    <children>
-        <Label text="Secondary View" />
-        <Button fx:id="secondaryButton" text="Switch to Primary View" onAction="#switchToPrimary" />
-    </children>
-    <padding>
-        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
-    </padding>
-</VBox>

BIN
target/classes/module-info.class


BIN
target/classes/nordh/xyz/App.class


BIN
target/classes/nordh/xyz/PrimaryController.class


BIN
target/classes/nordh/xyz/SecondaryController.class


+ 0 - 16
target/classes/nordh/xyz/primary.fxml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.scene.layout.VBox?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.Button?>
-<?import javafx.geometry.Insets?>
-
-<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.PrimaryController">
-   <children>
-      <Label text="Primary View" />
-      <Button fx:id="primaryButton" text="Switch to Secondary View" onAction="#switchToSecondary"/>
-   </children>
-   <padding>
-      <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
-   </padding>
-</VBox>

+ 0 - 16
target/classes/nordh/xyz/secondary.fxml

@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-
-<?import javafx.scene.layout.VBox?>
-<?import javafx.scene.control.Label?>
-<?import javafx.scene.control.Button?>
-<?import javafx.geometry.Insets?>
-
-<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nordh.xyz.SecondaryController">
-    <children>
-        <Label text="Secondary View" />
-        <Button fx:id="secondaryButton" text="Switch to Primary View" onAction="#switchToPrimary" />
-    </children>
-    <padding>
-        <Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
-    </padding>
-</VBox>