From a117f9b84349c0bfa0bf26e8cb6bdfcd5aa75f47 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 14:00:43 +0100 Subject: [PATCH 001/103] Finished first iteration of monthly budget overview. Moved it to view folder --- .../view/monthly_budget_overview.fxml | 135 ------------------ 1 file changed, 135 deletions(-) delete mode 100644 src/main/resources/view/monthly_budget_overview.fxml diff --git a/src/main/resources/view/monthly_budget_overview.fxml b/src/main/resources/view/monthly_budget_overview.fxml deleted file mode 100644 index 020e9f85..00000000 --- a/src/main/resources/view/monthly_budget_overview.fxml +++ /dev/null @@ -1,135 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.geometry.Insets?> -<?import javafx.scene.Cursor?> -<?import javafx.scene.control.Button?> -<?import javafx.scene.control.DatePicker?> -<?import javafx.scene.control.Label?> -<?import javafx.scene.control.ProgressBar?> -<?import javafx.scene.image.Image?> -<?import javafx.scene.image.ImageView?> -<?import javafx.scene.layout.AnchorPane?> -<?import javafx.scene.layout.BorderPane?> -<?import javafx.scene.layout.ColumnConstraints?> -<?import javafx.scene.layout.GridPane?> -<?import javafx.scene.layout.HBox?> -<?import javafx.scene.layout.Region?> -<?import javafx.scene.layout.RowConstraints?> -<?import javafx.scene.text.Font?> -<?import javafx.scene.text.Text?> - - -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> - <children> - <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> - <cursor> - <Cursor fx:constant="DEFAULT" /> - </cursor> - </ImageView> - <BorderPane prefHeight="400.0" prefWidth="600.0" AnchorPane.topAnchor="0.0"> - <center> - <GridPane gridLinesVisible="true" BorderPane.alignment="CENTER"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <children> - <HBox prefHeight="68.0" prefWidth="574.0" GridPane.columnSpan="2"> - <children> - <Region prefHeight="108.0" prefWidth="270.0" /> - <Label text="Label" /> - <Region prefHeight="108.0" prefWidth="100.0" /> - <DatePicker /> - </children> - </HBox> - <AnchorPane GridPane.columnSpan="2" GridPane.rowIndex="1"> - <children> - <ProgressBar layoutX="9.0" layoutY="41.0" prefHeight="18.0" prefWidth="554.0" progress="0.37" /> - </children> - </AnchorPane> - <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> - <children> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> - <graphic> - <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> - <cursor> - <Cursor fx:constant="DEFAULT" /> - </cursor> - <image> - <Image url="@../Images/pizzaslice.png" /> - </image> - </ImageView> - </graphic> - </Button> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> - <graphic> - <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> - <cursor> - <Cursor fx:constant="DEFAULT" /> - </cursor> - <image> - <Image url="@../Images/add_expense.png" /> - </image> - </ImageView> - </graphic> - </Button> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> - <graphic> - <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> - <cursor> - <Cursor fx:constant="DEFAULT" /> - </cursor> - <image> - <Image url="@../Images/overview_stonks.png" /> - </image> - </ImageView> - </graphic> - </Button> - </children> - </HBox> - </children> - </GridPane> - </center> - <bottom> - <Region prefHeight="8.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> - </bottom> - <left> - <Region prefHeight="287.0" prefWidth="14.0" BorderPane.alignment="CENTER" /> - </left> - <right> - <Region prefHeight="287.0" prefWidth="12.0" BorderPane.alignment="CENTER" /> - </right> - <top> - <HBox BorderPane.alignment="CENTER"> - <children> - <Button mnemonicParsing="false" onAction="#switchIncome" text="Return "> - <opaqueInsets> - <Insets left="100.0" /> - </opaqueInsets> - <HBox.margin> - <Insets left="10.0" top="10.0" /> - </HBox.margin> - </Button> - <Region prefHeight="70.0" prefWidth="103.0" /> - <Text strokeType="OUTSIDE" strokeWidth="0.0" text="BUDGET FEBRUARY" textAlignment="CENTER"> - <HBox.margin> - <Insets /> - </HBox.margin> - <font> - <Font size="30.0" /> - </font> - </Text> - </children> - <opaqueInsets> - <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> - </opaqueInsets> - </HBox> - </top> - </BorderPane> - </children> -</AnchorPane> -- GitLab From 49214fe214066469ef953789dae70915f982293f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 14:01:04 +0100 Subject: [PATCH 002/103] Finished first iteration of monthly budget overview. Moved it to view folder --- .../view/monthly_budget_overview.fxml | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) create mode 100644 src/main/resources/view/monthly_budget_overview.fxml diff --git a/src/main/resources/view/monthly_budget_overview.fxml b/src/main/resources/view/monthly_budget_overview.fxml new file mode 100644 index 00000000..042c1f19 --- /dev/null +++ b/src/main/resources/view/monthly_budget_overview.fxml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.Cursor?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.DatePicker?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ProgressBar?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Region?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> + + +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + </ImageView> + <BorderPane prefHeight="400.0" prefWidth="600.0" AnchorPane.topAnchor="0.0"> + <center> + <GridPane prefWidth="574.0" BorderPane.alignment="CENTER"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox alignment="BOTTOM_RIGHT" GridPane.columnSpan="2"> + <children> + <DatePicker /> + <HBox alignment="BOTTOM_CENTER" prefHeight="28.0" prefWidth="574.0"> + <children> + <Label text="5000kr left" /> + </children> + </HBox> + </children> + </VBox> + <AnchorPane GridPane.columnSpan="2" GridPane.rowIndex="1"> + <children> + <ProgressBar layoutX="10.0" layoutY="14.0" prefHeight="40.0" prefWidth="554.0" progress="0.72" /> + </children> + </AnchorPane> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> + <children> + <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> + <graphic> + <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/pizzaslice.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> + <graphic> + <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/add_expense.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> + <graphic> + <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/overview_stonks.png" /> + </image> + </ImageView> + </graphic> + </Button> + </children> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <padding> + <Insets top="20.0" /> + </padding> + </HBox> + </children> + </GridPane> + </center> + <bottom> + <Region prefHeight="55.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> + </bottom> + <left> + <Region prefHeight="287.0" prefWidth="14.0" BorderPane.alignment="CENTER" /> + </left> + <right> + <Region prefHeight="287.0" prefWidth="12.0" BorderPane.alignment="CENTER" /> + </right> + <top> + <HBox prefHeight="51.0" prefWidth="600.0" BorderPane.alignment="CENTER"> + <children> + <Button mnemonicParsing="false" onAction="#switchIncome" text="Return "> + <opaqueInsets> + <Insets left="100.0" /> + </opaqueInsets> + <HBox.margin> + <Insets left="10.0" top="10.0" /> + </HBox.margin> + </Button> + <Region prefHeight="70.0" prefWidth="103.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="BUDGET FEBRUARY" textAlignment="CENTER"> + <HBox.margin> + <Insets /> + </HBox.margin> + <font> + <Font size="30.0" /> + </font> + </Text> + </children> + <opaqueInsets> + <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> + </opaqueInsets> + </HBox> + </top> + </BorderPane> + </children> +</AnchorPane> -- GitLab From ee38932a1371c1782a75c4b90ce616d41db0c028 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 15:18:38 +0100 Subject: [PATCH 003/103] Tried to create new controllers --- .../demo/controller/ExpenseController.java | 83 +++++++++++++++++++ .../demo/controller/MenuController.java | 62 ++++++++++++++ .../demo/controller/SceneController.java | 55 ++++-------- src/main/resources/view/Expenses.fxml | 2 +- src/main/resources/view/FirstMenu.fxml | 2 +- .../view/monthly_budget_overview.fxml | 15 ++-- 6 files changed, 170 insertions(+), 49 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java new file mode 100644 index 00000000..06c46ca7 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java @@ -0,0 +1,83 @@ +package no.ntnu.idatt1002.demo.controller; + +import java.io.IOException; +import java.io.Serializable; + +import java.net.URL; +import java.util.ResourceBundle; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Economics.Expense; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + +public class ExpenseController implements Initializable { + + @FXML + private Button add; + + @FXML + private ComboBox<?> show; + + @FXML + private TableColumn<Expense, Double> amount; + + @FXML + private TableColumn<Expense, ExpenseCategory> category; + + @FXML + private TableColumn<Expense, String> date; + + @FXML + private TableColumn<Expense, String> description; + + @FXML + private TableView<Expense> expenseTableView; + + ObservableList<Expense> expenses = FXCollections.observableArrayList( + new Expense("", 1000.00, true, ExpenseCategory.FOOD, "1/1/23") + ); + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); + category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); + date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); + description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); + + expenseTableView.setItems(expenses); + } + + public void addExpense(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); + Scene newScene = new Scene(loader.load()); + Stage newStage = new Stage(); + newStage.setScene(newScene); + newStage.setResizable(false); + newStage.initModality(Modality.APPLICATION_MODAL); + + newStage.show(); + } + + public void editExpense(ActionEvent event) throws IOException { + + } + + public void deleteExpense(ActionEvent event) throws IOException { + + } + + public void switchIncome(ActionEvent event) { + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java new file mode 100644 index 00000000..87469182 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java @@ -0,0 +1,62 @@ +package no.ntnu.idatt1002.demo.controller; + +import java.awt.Menu; +import java.awt.event.ActionEvent; +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.DatePicker; +import javafx.scene.control.ProgressBar; +import javafx.stage.Stage; + +public class MenuController implements Initializable { + + @FXML + private Button addExpense; + + @FXML + private DatePicker date; + + @FXML + private Button food; + + @FXML + private Button overview; + + @FXML + private ProgressBar progressbar; + + + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + + } + + @FXML + public void switchStartMenu(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/FirstMenu.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } + + + public void switchExpenses(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index fdc28226..1cc97660 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -12,46 +12,12 @@ import javafx.scene.Scene; import javafx.stage.Modality; import javafx.stage.Stage; -public class SceneController /*implements Initializable*/ { +public class SceneController { private Stage stage; private Scene scene; private Parent root; - /*@FXML - private Button add; - - @FXML - private ComboBox<?> show; - - @FXML - private TableColumn<Expense, Double> amount; - - @FXML - private TableColumn<Expense, ExpenseCategory> category; - - @FXML - private TableColumn<Expense, String> date; - - @FXML - private TableColumn<Expense, String> description; - - @FXML - private TableView<Expense> expenseTableView; - - ObservableList<Expense> expenses = FXCollections.observableArrayList( - new Expense("", 1000.00, true, ExpenseCategory.FOOD, "1/1/23") - ); - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); - category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); - date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); - description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); - - expenseTableView.setItems(expenses); - }*/ - public void switchStartMenu(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/FirstMenu.fxml")); root = loader.load(); @@ -78,14 +44,15 @@ public class SceneController /*implements Initializable*/ { stage.show(); } - public void switchExpenses(ActionEvent event) throws IOException { + /*public void switchExpenses(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); Parent root = loader.load(); + ExpenseController expenseController = loader.getController(); stage = (Stage)((Node)event.getSource()).getScene().getWindow(); scene = new Scene(root); stage.setScene(scene); stage.show(); - } + }*/ public void switchOverview(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Overview.fxml")); @@ -96,7 +63,16 @@ public class SceneController /*implements Initializable*/ { stage.show(); } - public void addExpense(ActionEvent event) throws IOException { + public void switchMainMenu(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/monthly_budget_overview.fxml")); + Parent root = loader.load(); + stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } + + /*public void addExpense(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); Scene newScene = new Scene(loader.load()); Stage newStage = new Stage(); @@ -105,7 +81,8 @@ public class SceneController /*implements Initializable*/ { newStage.initModality(Modality.APPLICATION_MODAL); newStage.show(); - } + }*/ + public void addIncome(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addIncome.fxml")); Scene newScene = new Scene(loader.load()); diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 041a5550..b8a0919f 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -19,7 +19,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.ExpenseController"> <children> <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> <image> diff --git a/src/main/resources/view/FirstMenu.fxml b/src/main/resources/view/FirstMenu.fxml index e8fe27a4..94b6c19c 100644 --- a/src/main/resources/view/FirstMenu.fxml +++ b/src/main/resources/view/FirstMenu.fxml @@ -26,7 +26,7 @@ <Font size="24.0" /> </font> </Button> - <Button layoutX="380.0" layoutY="212.0" mnemonicParsing="false" onAction="#underProgress" text="Old Budget"> + <Button layoutX="380.0" layoutY="212.0" mnemonicParsing="false" onAction="#switchMainMenu" text="Old Budget"> <font> <Font size="24.0" /> </font> diff --git a/src/main/resources/view/monthly_budget_overview.fxml b/src/main/resources/view/monthly_budget_overview.fxml index 042c1f19..e3c5d422 100644 --- a/src/main/resources/view/monthly_budget_overview.fxml +++ b/src/main/resources/view/monthly_budget_overview.fxml @@ -19,8 +19,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> - -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.MenuController"> <children> <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> <cursor> @@ -45,7 +44,7 @@ <children> <VBox alignment="BOTTOM_RIGHT" GridPane.columnSpan="2"> <children> - <DatePicker /> + <DatePicker fx:id="date" /> <HBox alignment="BOTTOM_CENTER" prefHeight="28.0" prefWidth="574.0"> <children> <Label text="5000kr left" /> @@ -55,12 +54,12 @@ </VBox> <AnchorPane GridPane.columnSpan="2" GridPane.rowIndex="1"> <children> - <ProgressBar layoutX="10.0" layoutY="14.0" prefHeight="40.0" prefWidth="554.0" progress="0.72" /> + <ProgressBar fx:id="progressbar" layoutX="10.0" layoutY="14.0" prefHeight="40.0" prefWidth="554.0" progress="0.72" /> </children> </AnchorPane> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> + <Button fx:id="food" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> @@ -72,7 +71,7 @@ </ImageView> </graphic> </Button> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> + <Button fx:id="addExpense" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> <graphic> <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> <cursor> @@ -84,7 +83,7 @@ </ImageView> </graphic> </Button> - <Button contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> + <Button fx:id="overview" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> @@ -119,7 +118,7 @@ <top> <HBox prefHeight="51.0" prefWidth="600.0" BorderPane.alignment="CENTER"> <children> - <Button mnemonicParsing="false" onAction="#switchIncome" text="Return "> + <Button mnemonicParsing="false" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> -- GitLab From 4f0a0414e6b997b65c9d710dd1c8975e0cd189e3 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 16:23:47 +0100 Subject: [PATCH 004/103] Added icons beside add, edit and delete button. Added fifth 'recurring' row in table --- src/main/resources/Images/addIcon.png | Bin 0 -> 2982 bytes src/main/resources/Images/delete.png | Bin 0 -> 10682 bytes src/main/resources/Images/edit.png | Bin 0 -> 6425 bytes src/main/resources/view/Expenses.fxml | 5 +++-- 4 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 src/main/resources/Images/addIcon.png create mode 100644 src/main/resources/Images/delete.png create mode 100644 src/main/resources/Images/edit.png diff --git a/src/main/resources/Images/addIcon.png b/src/main/resources/Images/addIcon.png new file mode 100644 index 0000000000000000000000000000000000000000..126113997e9a7ce5cccfdadbc600b148a1a065c0 GIT binary patch literal 2982 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/delete.png b/src/main/resources/Images/delete.png new file mode 100644 index 0000000000000000000000000000000000000000..b23937817cfa4976c74f73288a6c34451934f16b GIT binary patch literal 10682 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/edit.png b/src/main/resources/Images/edit.png new file mode 100644 index 0000000000000000000000000000000000000000..833b336008545736084b8b7178591740a5bd7ac7 GIT binary patch literal 6425 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index b8a0919f..7c16746a 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -74,8 +74,8 @@ <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addExpense" text="Add" textAlignment="CENTER" /> - <Button fx:id="add1" alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER" /> - <Button fx:id="add11" alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER" /> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER" /> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER" /> </children> <opaqueInsets> <Insets /> @@ -120,6 +120,7 @@ <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> -- GitLab From 6dc54763eeacf49d6dab3040849836e781981948 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 16:42:40 +0100 Subject: [PATCH 005/103] Added button images and a new column to income too. Resized tableview for both income and expenses --- src/main/resources/view/Expenses.fxml | 31 ++++++++++++--- src/main/resources/view/Income.fxml | 57 ++++++++++++++++++++------- 2 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 7c16746a..f03cd372 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -29,7 +29,7 @@ <Cursor fx:constant="DEFAULT" /> </cursor> </ImageView> - <BorderPane prefHeight="400.0" prefWidth="593.0"> + <BorderPane prefHeight="400.0" prefWidth="600.0"> <top> <HBox BorderPane.alignment="CENTER"> <children> @@ -73,9 +73,30 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addExpense" text="Add" textAlignment="CENTER" /> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER" /> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER" /> + <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#add" text="Add" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/addIcon.png" /> + </image> + </ImageView> + </graphic></Button> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/edit.png" /> + </image> + </ImageView> + </graphic></Button> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/delete.png" /> + </image> + </ImageView> + </graphic></Button> </children> <opaqueInsets> <Insets /> @@ -133,7 +154,7 @@ <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> </opaqueInsets> <left> - <Region prefHeight="357.0" prefWidth="25.0" BorderPane.alignment="CENTER" /> + <Region prefHeight="330.0" prefWidth="1.0" BorderPane.alignment="CENTER" /> </left> <right> <Region prefHeight="357.0" prefWidth="0.0" BorderPane.alignment="CENTER" /> diff --git a/src/main/resources/view/Income.fxml b/src/main/resources/view/Income.fxml index 4a4b9e7b..a6cd5b6c 100644 --- a/src/main/resources/view/Income.fxml +++ b/src/main/resources/view/Income.fxml @@ -29,11 +29,11 @@ <Cursor fx:constant="DEFAULT" /> </cursor> </ImageView> - <BorderPane prefHeight="400.0" prefWidth="593.0"> + <BorderPane prefHeight="400.0" prefWidth="600.0"> <top> <HBox BorderPane.alignment="CENTER"> <children> - <Button cancelButton="true" mnemonicParsing="false" onAction="#switchStartMenu" text="Return "> + <Button mnemonicParsing="false" onAction="#switchStartMenu" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> @@ -42,7 +42,7 @@ </HBox.margin> </Button> <Region prefHeight="70.0" prefWidth="141.0" /> - <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Income" textAlignment="CENTER" translateX="30.0"> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Income" textAlignment="CENTER" wrappingWidth="174.6796875"> <HBox.margin> <Insets /> </HBox.margin> @@ -73,9 +73,33 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addIncome" text="Add" textAlignment="CENTER" /> - <Button fx:id="add1" alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER" /> - <Button fx:id="add11" alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER" /> + <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addIncome" text="Add" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/addIcon.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/edit.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/delete.png" /> + </image> + </ImageView> + </graphic> + </Button> </children> <opaqueInsets> <Insets /> @@ -86,7 +110,7 @@ </HBox> <VBox alignment="BOTTOM_LEFT" prefHeight="200.0" prefWidth="100.0" spacing="5.0" GridPane.columnIndex="1"> <children> - <ComboBox fx:id="show" prefWidth="150.0" promptText="Show"> + <ComboBox fx:id="show" prefWidth="150.0" promptText="Show "> <opaqueInsets> <Insets /> </opaqueInsets> @@ -101,26 +125,29 @@ <Button mnemonicParsing="false" onAction="#switchOverview" text="Overview"> <HBox.margin> <Insets right="5.0" /> - </HBox.margin></Button> - <Button disable="true" mnemonicParsing="false" onAction="#switchIncome" text="Income" textFill="#4d1616" /> + </HBox.margin> + </Button> + <Button disable="true" mnemonicParsing="false" onAction="#switchIncome" text="Income" /> <Button mnemonicParsing="false" onAction="#switchExpenses" text="Expenses" /> <Button disable="true" mnemonicParsing="false" text="Savings" /> <Button mnemonicParsing="false" onAction="#switchExpenses" text="Next"> <HBox.margin> <Insets left="170.0" /> - </HBox.margin></Button> + </HBox.margin> + </Button> </children> <padding> <Insets top="10.0" /> </padding> </HBox> <TableView fx:id="expenseTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> - <columns> - <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> + <columns> + <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> - </columns> + <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> + </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> </columnResizePolicy> @@ -132,7 +159,7 @@ <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> </opaqueInsets> <left> - <Region prefHeight="357.0" prefWidth="25.0" BorderPane.alignment="CENTER" /> + <Region prefHeight="330.0" prefWidth="1.0" BorderPane.alignment="CENTER" /> </left> <right> <Region prefHeight="357.0" prefWidth="0.0" BorderPane.alignment="CENTER" /> -- GitLab From cf767bdb0265f145db7c79d701bc3deb97009bea Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 17:36:06 +0100 Subject: [PATCH 006/103] Created hardcoded list of expenses for testing --- .../idatt1002/demo/data/Economics/ExpenseRepository.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java new file mode 100644 index 00000000..4ccc19c0 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java @@ -0,0 +1,5 @@ +package no.ntnu.idatt1002.demo.data.Economics; + +public class ExpenseRepository { + +} -- GitLab From 89e109e0705d2312452aa2fea6aebe80239d5afe Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 15 Mar 2023 17:53:34 +0100 Subject: [PATCH 007/103] Created new Expense Controller, renamed old Expense Controller to ExpensesController, and created two other new controller classes for testing --- .../demo/controller/ExpenseController.java | 83 +--------------- .../demo/controller/ExpensesController.java | 98 +++++++++++++++++++ .../demo/controller/MenuController.java | 38 +++---- .../demo/controller/SceneController.java | 9 +- .../data/Economics/ExpenseRepository.java | 5 - .../demo/view/ExpenseRepository.java | 22 +++++ src/main/resources/view/Expenses.fxml | 2 +- 7 files changed, 147 insertions(+), 110 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java index 06c46ca7..5d3ad79e 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java @@ -1,83 +1,2 @@ -package no.ntnu.idatt1002.demo.controller; - -import java.io.IOException; -import java.io.Serializable; - -import java.net.URL; -import java.util.ResourceBundle; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.ComboBox; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.stage.Modality; -import javafx.stage.Stage; -import no.ntnu.idatt1002.demo.data.Economics.Expense; -import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; - -public class ExpenseController implements Initializable { - - @FXML - private Button add; - - @FXML - private ComboBox<?> show; - - @FXML - private TableColumn<Expense, Double> amount; - - @FXML - private TableColumn<Expense, ExpenseCategory> category; - - @FXML - private TableColumn<Expense, String> date; - - @FXML - private TableColumn<Expense, String> description; - - @FXML - private TableView<Expense> expenseTableView; - - ObservableList<Expense> expenses = FXCollections.observableArrayList( - new Expense("", 1000.00, true, ExpenseCategory.FOOD, "1/1/23") - ); - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); - category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); - date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); - description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); - - expenseTableView.setItems(expenses); - } - - public void addExpense(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); - Scene newScene = new Scene(loader.load()); - Stage newStage = new Stage(); - newStage.setScene(newScene); - newStage.setResizable(false); - newStage.initModality(Modality.APPLICATION_MODAL); - - newStage.show(); - } - - public void editExpense(ActionEvent event) throws IOException { - - } - - public void deleteExpense(ActionEvent event) throws IOException { - - } - - public void switchIncome(ActionEvent event) { - } +package no.ntnu.idatt1002.demo.controller;public class ExpenseController { } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java new file mode 100644 index 00000000..d46beb0e --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -0,0 +1,98 @@ +package no.ntnu.idatt1002.demo.controller; + +import java.io.IOException; + +import java.net.URL; +import java.util.ResourceBundle; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Economics.Expense; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.view.ExpenseRepository; + +public class ExpensesController implements Initializable { + @FXML + private Button add; + + @FXML + private Button edit; + + @FXML + private Button delete; + + @FXML + private ComboBox<?> show; + + @FXML + private TableColumn<Expense, Double> amount; + + @FXML + private TableColumn<Expense, ExpenseCategory> category; + + @FXML + private TableColumn<Expense, String> date; + + @FXML + private TableColumn<Expense, String> description; + + @FXML + private TableColumn<Expense, Boolean> recurring; + + @FXML + private TableView<Expense> expenseTableView; + + + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + ObservableList<Expense> expenses = FXCollections.observableArrayList(ExpenseRepository.getExpenses()); + + amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); + category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); + date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); + description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); + recurring.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); + + expenseTableView.setItems(expenses); + } + + public void add(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); + Scene newScene = new Scene(loader.load()); + Stage newStage = new Stage(); + newStage.setScene(newScene); + newStage.setResizable(false); + newStage.initModality(Modality.APPLICATION_MODAL); + + newStage.show(); + } + + public void edit(ActionEvent event) throws IOException { + + } + + public void delete(ActionEvent event) throws IOException { + + } + + public void switchIncome(ActionEvent event) { + + + } + + public void switchOverview(ActionEvent event) { + + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java index 87469182..983dff9c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java @@ -34,29 +34,33 @@ public class MenuController implements Initializable { private ProgressBar progressbar; + @FXML + public void switchStartMenu(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/FirstMenu.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } + + @FXML + public void switchExpenses(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } @Override public void initialize(URL url, ResourceBundle resourceBundle) { } - @FXML - public void switchStartMenu(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/FirstMenu.fxml")); - Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } - public void switchExpenses(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); - Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } + + } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index 1cc97660..b6e3e5e1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -44,15 +44,14 @@ public class SceneController { stage.show(); } - /*public void switchExpenses(ActionEvent event) throws IOException { + public void switchExpenses(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); Parent root = loader.load(); - ExpenseController expenseController = loader.getController(); stage = (Stage)((Node)event.getSource()).getScene().getWindow(); scene = new Scene(root); stage.setScene(scene); stage.show(); - }*/ + } public void switchOverview(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Overview.fxml")); @@ -72,7 +71,7 @@ public class SceneController { stage.show(); } - /*public void addExpense(ActionEvent event) throws IOException { + public void addExpense(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); Scene newScene = new Scene(loader.load()); Stage newStage = new Stage(); @@ -81,7 +80,7 @@ public class SceneController { newStage.initModality(Modality.APPLICATION_MODAL); newStage.show(); - }*/ + } public void addIncome(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addIncome.fxml")); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java deleted file mode 100644 index 4ccc19c0..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRepository.java +++ /dev/null @@ -1,5 +0,0 @@ -package no.ntnu.idatt1002.demo.data.Economics; - -public class ExpenseRepository { - -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java new file mode 100644 index 00000000..e6fb5b2c --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java @@ -0,0 +1,22 @@ +package no.ntnu.idatt1002.demo.view; + +import java.util.ArrayList; +import no.ntnu.idatt1002.demo.data.Economics.Expense; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; + +public class ExpenseRepository { + public static ArrayList<Expense> getExpenses () { + ArrayList<Expense> expenses = new ArrayList<>(); + expenses.add(new Expense("free", 100, true, ExpenseCategory.FOOD, "1/1/23")); + expenses.add(new Expense(149, true, ExpenseCategory.FOOD, "1/1/23")); + expenses.add(new Expense("Not free", 1000, true, ExpenseCategory.CLOTHES, "2/1/23")); + expenses.add(new Expense(200, true, ExpenseCategory.BOOKS, "3/1/23")); + + return expenses; + } + + + } + + diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index f03cd372..cf38ea0f 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -19,7 +19,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.ExpenseController"> +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.ExpensesController"> <children> <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> <image> -- GitLab From a930cf3d4b7b141847a8612260dabea1c7b8e6f4 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 12:12:11 +0100 Subject: [PATCH 008/103] Created controller for AddExpense fxml file --- .../demo/controller/AddExpenseController.java | 13 +++ .../demo/controller/ExpenseController.java | 2 - .../ntnu/idatt1002/demo/view/DialogMode.java | 2 + src/main/resources/view/AddExpense.fxml | 95 ++++++++++--------- 4 files changed, 63 insertions(+), 49 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java new file mode 100644 index 00000000..787042a6 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -0,0 +1,13 @@ +package no.ntnu.idatt1002.demo.controller; + +import no.ntnu.idatt1002.demo.data.Economics.Expense; + +public class AddExpenseController { + + Expense expense; + + public void setExpense(Expense expense) { + this.expense = expense; + + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java deleted file mode 100644 index 5d3ad79e..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpenseController.java +++ /dev/null @@ -1,2 +0,0 @@ -package no.ntnu.idatt1002.demo.controller;public class ExpenseController { -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java b/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java new file mode 100644 index 00000000..b457203f --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java @@ -0,0 +1,2 @@ +package no.ntnu.idatt1002.demo.view;public class DialogEnum { +} diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index 097992b0..76904dc6 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -1,57 +1,58 @@ <?xml version="1.0" encoding="UTF-8"?> <?import javafx.geometry.Insets?> -<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ButtonType?> <?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> -<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="11.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="189.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <padding> - <Insets bottom="10.0" left="10.0" top="20.0" /> - </padding> - <children> - <Label text="Date:" /> - <Label text="Amount:" GridPane.rowIndex="1" /> - <Label text="Description:" GridPane.rowIndex="2" /> - <Label text="Category" GridPane.rowIndex="3"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </Label> - <TextField promptText="Date" GridPane.columnIndex="1" GridPane.columnSpan="2" /> - <TextField promptText="Amount" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> - <TextField promptText="Description (optional)" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> - <ComboBox prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="3"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </ComboBox> - <Button mnemonicParsing="false" onAction="#closeButton" text="Cancel" GridPane.columnIndex="3" GridPane.rowIndex="4"> - <GridPane.margin> - <Insets left="75.0" /> - </GridPane.margin> - </Button> - <Button mnemonicParsing="false" onAction="#closeButton" text="Button" GridPane.columnIndex="3" GridPane.rowIndex="4"> - <GridPane.margin> - <Insets left="130.0" /> - </GridPane.margin> - </Button> - </children> -</GridPane> +<DialogPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> + <content> + <GridPane hgap="10.0" vgap="10.0"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="11.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="189.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <padding> + <Insets bottom="10.0" left="10.0" top="20.0" /> + </padding> + <children> + <Label text="Date:" /> + <Label text="Amount:" GridPane.rowIndex="1" /> + <Label text="Description:" GridPane.rowIndex="2" /> + <Label text="Category" GridPane.rowIndex="3"> + <GridPane.margin> + <Insets /> + </GridPane.margin> + </Label> + <TextField fx:id="dateField" prefHeight="25.0" prefWidth="16.0" promptText="Date" GridPane.columnIndex="1" GridPane.columnSpan="2" /> + <TextField fx:id="amountField" promptText="Amount" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> + <TextField fx:id="descriptionField" promptText="Description (optional)" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> + <ComboBox fx:id="recurringBox" disable="true" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="4"> + <GridPane.margin> + <Insets /> + </GridPane.margin> + </ComboBox> + <Label text="Recurring" GridPane.rowIndex="4" /> + <ComboBox fx:id="categoryBox" disable="true" prefHeight="25.0" prefWidth="214.0" GridPane.columnIndex="1" GridPane.rowIndex="3" /> + </children> + </GridPane> + </content> + <buttonTypes> + <ButtonType fx:constant="CANCEL" /> + <ButtonType fx:constant="OK" /> + </buttonTypes> +</DialogPane> -- GitLab From eec026bdf1a4d4d2cea0c45d35a26ccb1d0beab6 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 12:19:26 +0100 Subject: [PATCH 009/103] Added method body for add, edit and delete buttons and changed initialize method to newer version --- .../demo/controller/ExpensesController.java | 101 +++++++++++++++--- 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index d46beb0e..fa312e2e 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -3,7 +3,9 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; import java.net.URL; +import java.util.Optional; import java.util.ResourceBundle; +import javafx.application.Platform; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -12,20 +14,30 @@ import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; +import javafx.scene.control.Dialog; +import javafx.scene.control.DialogPane; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Pane; import javafx.stage.Modality; import javafx.stage.Stage; +import javafx.util.StringConverter; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.view.DialogMode; import no.ntnu.idatt1002.demo.view.ExpenseRepository; -public class ExpensesController implements Initializable { +public class ExpensesController { + + @FXML private Button add; - @FXML private Button edit; @@ -54,33 +66,92 @@ public class ExpensesController implements Initializable { private TableView<Expense> expenseTableView; + ObservableList<Expense> expenses = FXCollections.observableArrayList(ExpenseRepository.getExpenses()); - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - ObservableList<Expense> expenses = FXCollections.observableArrayList(ExpenseRepository.getExpenses()); + @FXML + public void initialize() { + date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); - date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurring.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); expenseTableView.setItems(expenses); } - public void add(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); - Scene newScene = new Scene(loader.load()); - Stage newStage = new Stage(); - newStage.setScene(newScene); - newStage.setResizable(false); - newStage.initModality(Modality.APPLICATION_MODAL); + @FXML + private void add(ActionEvent event) throws IOException { + // Load the FXML file for the "Add Expense" dialog box + /*FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/AddExpense.fxml")); + DialogPane dialogPane = loader.load(); + +// Create a new Dialog instance with the loaded AnchorPane + Dialog<ButtonType> dialog = new Dialog<>(); + dialog.setTitle("Add Expense"); + dialog.getDialogPane().setContent(dialogPane); + + dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); + + + Optional<ButtonType> result = dialog.showAndWait(); + if (result.isPresent() && result.get() == ButtonType.OK) { + // Get the values from the dialog and create a new Expense object - newStage.show(); + String description = descriptionField.getText(); + double amount = Double.parseDouble(amountField.getText()); + ExpenseCategory category = ExpenseCategory.FOOD;//categoryComboBox.getValue(); + boolean isRecurring = true;// recurringComboBox.getValue(); + String date = dateField.getText(); + Expense newExpense = new Expense(description, amount, isRecurring, category, date); + + // Add the new expense to the table view + System.out.println(expenses.size()); + expenses.add(newExpense); + System.out.println(expenses.size()); + + expenseTableView.setItems(expenses); + }*/ } public void edit(ActionEvent event) throws IOException { - + /* + Expense expense = null; + String dialogTitle = ""; + DialogMode mode; + + if (event.getSource().equals((edit))) { + mode = DialogMode.EDIT; + dialogTitle = "Edit expense"; + expense = expenseTableView.getSelectionModel().getSelectedItem(); + } else if (event.getSource().equals(add)) { + mode = DialogMode.ADD; + dialogTitle = "Add expense"; + expense = new Expense(0, false, ExpenseCategory.FOOD, "1/1/23"); + } else { + return; + } + + try { + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(SceneController.class.getResource("/view/addIncome.fxml")); + DialogPane addExpenseDialog = loader.load(); + + ExpenseController expenseController = loader.getController(); + expenseController.setExpense(expense); + + Dialog<ButtonType> dialog = new Dialog<>(); + dialog.setDialogPane(addExpenseDialog); + dialog.setTitle(dialogTitle); + + Optional<ButtonType> clickedButton = dialog.showAndWait(); + if (clickedButton.get() == ButtonType.OK && mode == DialogMode.ADD) { + expenses.add(expense); + + } + } catch (IOException e) { + e.printStackTrace(); + }*/ } public void delete(ActionEvent event) throws IOException { -- GitLab From 5eae0f805c9245d284628b113d8fcd373377ce1e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 12:19:54 +0100 Subject: [PATCH 010/103] Added initialize method --- .../demo/controller/AddExpenseController.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 787042a6..3a59ab48 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,13 +1,23 @@ package no.ntnu.idatt1002.demo.controller; +import javafx.fxml.FXML; +import javafx.scene.control.TextField; import no.ntnu.idatt1002.demo.data.Economics.Expense; public class AddExpenseController { - Expense expense; + @FXML + private TextField dateField; - public void setExpense(Expense expense) { - this.expense = expense; + @FXML + private TextField descriptionField; + + @FXML + private TextField amountField; + + + @FXML + public void initialize() { } } -- GitLab From 0af5bd4a4e2ce76e025801e2a79ddd2986c2ab62 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 13:39:42 +0100 Subject: [PATCH 011/103] Created new dialog box --- src/main/resources/view/AddExpense.fxml | 57 +++++++++---------------- 1 file changed, 21 insertions(+), 36 deletions(-) diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index 76904dc6..40d0d50a 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -1,58 +1,43 @@ <?xml version="1.0" encoding="UTF-8"?> -<?import javafx.geometry.Insets?> -<?import javafx.scene.control.ButtonType?> <?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> -<DialogPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> +<DialogPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="480.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> + <expandableContent> + <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="200.0" prefWidth="320.0" /> + </expandableContent> <content> - <GridPane hgap="10.0" vgap="10.0"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="11.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="189.0" /> - </columnConstraints> - <rowConstraints> + <GridPane> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <padding> - <Insets bottom="10.0" left="10.0" top="20.0" /> - </padding> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> <children> <Label text="Date:" /> <Label text="Amount:" GridPane.rowIndex="1" /> <Label text="Description:" GridPane.rowIndex="2" /> - <Label text="Category" GridPane.rowIndex="3"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </Label> - <TextField fx:id="dateField" prefHeight="25.0" prefWidth="16.0" promptText="Date" GridPane.columnIndex="1" GridPane.columnSpan="2" /> - <TextField fx:id="amountField" promptText="Amount" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> - <TextField fx:id="descriptionField" promptText="Description (optional)" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> - <ComboBox fx:id="recurringBox" disable="true" prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="4"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </ComboBox> + <Label text="Category" GridPane.rowIndex="3" /> <Label text="Recurring" GridPane.rowIndex="4" /> - <ComboBox fx:id="categoryBox" disable="true" prefHeight="25.0" prefWidth="214.0" GridPane.columnIndex="1" GridPane.rowIndex="3" /> + <TextField fx:id="dateField" promptText="1/1/23" GridPane.columnIndex="1" /> + <TextField fx:id="amountField" promptText="100" GridPane.columnIndex="1" GridPane.rowIndex="1" /> + <TextField fx:id="descriptionField" promptText="(optional)" GridPane.columnIndex="1" GridPane.rowIndex="2" /> + <ComboBox fx:id="categoryBox" prefWidth="150.0" promptText="Food" GridPane.columnIndex="1" GridPane.rowIndex="3" /> + <ComboBox fx:id="recurringBox" prefWidth="150.0" promptText="True" GridPane.columnIndex="1" GridPane.rowIndex="4" /> </children> </GridPane> </content> - <buttonTypes> - <ButtonType fx:constant="CANCEL" /> - <ButtonType fx:constant="OK" /> - </buttonTypes> </DialogPane> -- GitLab From 5e422aa193860e3d6aff3302ac1d13ab47563e4b Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 13:40:16 +0100 Subject: [PATCH 012/103] Changed column id's --- src/main/resources/view/Expenses.fxml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index cf38ea0f..004c667b 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -137,11 +137,11 @@ </HBox> <TableView fx:id="expenseTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> <columns> - <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> + <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> -- GitLab From c20d221b13e32c91fc1bfc1dde09f0b1a4595a8e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 14:05:50 +0100 Subject: [PATCH 013/103] Made addExpense comboboxes offer options to choose from --- .../demo/controller/AddExpenseController.java | 34 ++++++++++++++++++- src/main/resources/view/AddExpense.fxml | 28 +++++++-------- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 3a59ab48..ebdf0451 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,8 +1,11 @@ package no.ntnu.idatt1002.demo.controller; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.control.ComboBox; import javafx.scene.control.TextField; -import no.ntnu.idatt1002.demo.data.Economics.Expense; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; public class AddExpenseController { @@ -15,9 +18,38 @@ public class AddExpenseController { @FXML private TextField amountField; + @FXML + private ComboBox<ExpenseCategory> categoryBox; + + @FXML + private ComboBox<String> recurringBox; @FXML public void initialize() { + ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList(ExpenseCategory.values()); + categoryBox.setItems(expenseCategories); + + ObservableList<String> recurring = FXCollections.observableArrayList("Yes", "No"); + recurringBox.setItems(recurring); + } + + public String getDate() { + return dateField.getText(); + } + + public String getDescription() { + return descriptionField.getText(); + } + + public double getAmount() { + return Double.parseDouble(amountField.getText()); + } + + public ExpenseCategory getCategory() { + return categoryBox.getValue(); + } + public boolean isRecurring() { + return recurringBox.getValue().equals("Yes"); } } diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index 40d0d50a..72ec14ca 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -4,28 +4,26 @@ <?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> -<?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> -<DialogPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="300.0" prefWidth="480.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> - <expandableContent> - <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="200.0" prefWidth="320.0" /> - </expandableContent> + +<DialogPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> <content> <GridPane> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - </columnConstraints> - <rowConstraints> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> + </rowConstraints> <children> <Label text="Date:" /> <Label text="Amount:" GridPane.rowIndex="1" /> @@ -36,7 +34,7 @@ <TextField fx:id="amountField" promptText="100" GridPane.columnIndex="1" GridPane.rowIndex="1" /> <TextField fx:id="descriptionField" promptText="(optional)" GridPane.columnIndex="1" GridPane.rowIndex="2" /> <ComboBox fx:id="categoryBox" prefWidth="150.0" promptText="Food" GridPane.columnIndex="1" GridPane.rowIndex="3" /> - <ComboBox fx:id="recurringBox" prefWidth="150.0" promptText="True" GridPane.columnIndex="1" GridPane.rowIndex="4" /> + <ComboBox fx:id="recurringBox" prefWidth="150.0" promptText="No" GridPane.columnIndex="1" GridPane.rowIndex="4" /> </children> </GridPane> </content> -- GitLab From 057ec3beed65f2a3d3b7e3eb6e653b15c42cad2a Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 17 Mar 2023 14:06:20 +0100 Subject: [PATCH 014/103] Added optional to ExpensesController --- .../demo/controller/ExpensesController.java | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index fa312e2e..849450a2 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -6,6 +6,7 @@ import java.net.URL; import java.util.Optional; import java.util.ResourceBundle; import javafx.application.Platform; +import javafx.beans.Observable; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -14,6 +15,7 @@ import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.Scene; import javafx.scene.control.Button; +import javafx.scene.control.ButtonBar.ButtonData; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; @@ -30,6 +32,7 @@ import javafx.stage.Stage; import javafx.util.StringConverter; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.data.Economics.Item; import no.ntnu.idatt1002.demo.view.DialogMode; import no.ntnu.idatt1002.demo.view.ExpenseRepository; @@ -48,47 +51,47 @@ public class ExpensesController { private ComboBox<?> show; @FXML - private TableColumn<Expense, Double> amount; + private TableColumn<Expense, Double> amountColumn; @FXML - private TableColumn<Expense, ExpenseCategory> category; + private TableColumn<Expense, ExpenseCategory> categoryColumn; @FXML - private TableColumn<Expense, String> date; + private TableColumn<Expense, String> dateColumn; @FXML - private TableColumn<Expense, String> description; + private TableColumn<Expense, String> descriptionColumn; @FXML - private TableColumn<Expense, Boolean> recurring; + private TableColumn<Expense, Boolean> recurringColumn; @FXML private TableView<Expense> expenseTableView; - ObservableList<Expense> expenses = FXCollections.observableArrayList(ExpenseRepository.getExpenses()); @FXML public void initialize() { - - date.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); - amount.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); - category.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); - description.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); - recurring.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); + dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); + amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); + categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); + descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); + recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); expenseTableView.setItems(expenses); } @FXML private void add(ActionEvent event) throws IOException { + // Load the FXML file for the "Add Expense" dialog box - /*FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/AddExpense.fxml")); + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); DialogPane dialogPane = loader.load(); + AddExpenseController addExpenseController = new AddExpenseController(); // Create a new Dialog instance with the loaded AnchorPane Dialog<ButtonType> dialog = new Dialog<>(); - dialog.setTitle("Add Expense"); dialog.getDialogPane().setContent(dialogPane); dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); @@ -97,13 +100,13 @@ public class ExpensesController { Optional<ButtonType> result = dialog.showAndWait(); if (result.isPresent() && result.get() == ButtonType.OK) { // Get the values from the dialog and create a new Expense object + String date = addExpenseController.getDate(); + String description = addExpenseController.getDescription(); + double amount = addExpenseController.getAmount(); + boolean isRecurring = addExpenseController.isRecurring(); + ExpenseCategory expenseCategory = addExpenseController.getCategory(); - String description = descriptionField.getText(); - double amount = Double.parseDouble(amountField.getText()); - ExpenseCategory category = ExpenseCategory.FOOD;//categoryComboBox.getValue(); - boolean isRecurring = true;// recurringComboBox.getValue(); - String date = dateField.getText(); - Expense newExpense = new Expense(description, amount, isRecurring, category, date); + Expense newExpense = new Expense(description, amount, isRecurring, expenseCategory, date); // Add the new expense to the table view System.out.println(expenses.size()); @@ -111,7 +114,7 @@ public class ExpensesController { System.out.println(expenses.size()); expenseTableView.setItems(expenses); - }*/ + } } public void edit(ActionEvent event) throws IOException { -- GitLab From dc9feb2044b4b3626bdf529da1bcb827a23e1fb9 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Sun, 19 Mar 2023 12:41:01 +0100 Subject: [PATCH 015/103] Added new images to main menu. Changed add image in expenses --- .../demo/controller/MainMenuController.java | 2 ++ src/main/resources/Images/addIcon.png | Bin 2982 -> 0 bytes src/main/resources/Images/add_image.png | Bin 0 -> 3696 bytes src/main/resources/Images/pizzaslice.png | Bin 109665 -> 64922 bytes src/main/resources/view/AddExpense.fxml | 3 +-- src/main/resources/view/Expenses.fxml | 2 +- .../view/monthly_budget_overview.fxml | 6 +++--- 7 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java delete mode 100644 src/main/resources/Images/addIcon.png create mode 100644 src/main/resources/Images/add_image.png diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java new file mode 100644 index 00000000..88479b18 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java @@ -0,0 +1,2 @@ +package no.ntnu.idatt1002.demo.controller;public class MainMenuController { +} diff --git a/src/main/resources/Images/addIcon.png b/src/main/resources/Images/addIcon.png deleted file mode 100644 index 126113997e9a7ce5cccfdadbc600b148a1a065c0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2982 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 diff --git a/src/main/resources/Images/add_image.png b/src/main/resources/Images/add_image.png new file mode 100644 index 0000000000000000000000000000000000000000..f8fd29245588cf7264711402a4610b4a23e3bfeb GIT binary patch literal 3696 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/pizzaslice.png b/src/main/resources/Images/pizzaslice.png index 6ab7c2818c2abe9a91e263d18c57b19ed7c66101..ad2aebbc8a93ef297a2a82229d385f655d9e88bc 100644 GIT binary patch literal 64922 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%0A&+Rc<`_AcEt{YmVKwct&(3ef<28E7;+!6yt> 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%0A+6IiI2SvUh=1^v2 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 A3;+NC literal 109665 zcmeFZcR1Vs`!*a!QJdN$YLB8;Z9cSiQPdu(RYFla2tjMbDvH*uTD7ZYt<)}Rui6ru zB2gnH;>q{-yN~<1kNdCZ`Hkm~=eU#O`Xk4CB-d-4*L9uO`OeL+n{@!4zP6q=fQSeH zAR@d0H%ov=girsu*S~J^pSuI$>YMKXdh%O-#InRhyntKuM8xz&H+=vg;dhe$`@I4G z<3)6fn1u8;89BurO2P?Ebbwn##KgBqh)GFFNC;<#6OIE&=t&vwNom|>G;t*3^}Q<{ zl~zQ~r&-_4WIBoFmvQoorntk*!pg=jASfg(A}TBQP+mb%>Cs~?Z5>@beY0og7M51l zHqI`tFWlV09{vG=LBWua(ARHbVмOQCfGPAPZf5`b%TvA$A{<)&^YeQpGb4%;D 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 Udo&zqkOo)}$g+V(1peOy08=nAY5)KL diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index 72ec14ca..a3a60a84 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -8,8 +8,7 @@ <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.RowConstraints?> - -<DialogPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> +<DialogPane expanded="true" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> <content> <GridPane> <columnConstraints> diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 004c667b..db56feb2 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -77,7 +77,7 @@ <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> - <Image url="@../Images/addIcon.png" /> + <Image url="@../Images/add_image.png" /> </image> </ImageView> </graphic></Button> diff --git a/src/main/resources/view/monthly_budget_overview.fxml b/src/main/resources/view/monthly_budget_overview.fxml index e3c5d422..1a6cc60f 100644 --- a/src/main/resources/view/monthly_budget_overview.fxml +++ b/src/main/resources/view/monthly_budget_overview.fxml @@ -59,7 +59,7 @@ </AnchorPane> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button fx:id="food" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> + <Button fx:id="foodButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> @@ -71,7 +71,7 @@ </ImageView> </graphic> </Button> - <Button fx:id="addExpense" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> + <Button fx:id="addExpenseButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> <graphic> <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> <cursor> @@ -83,7 +83,7 @@ </ImageView> </graphic> </Button> - <Button fx:id="overview" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> + <Button fx:id="overviewButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> -- GitLab From 813f4ac19b56f61dbed2ce3b3d6f8fe7d7a9f00f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Sun, 19 Mar 2023 12:58:27 +0100 Subject: [PATCH 016/103] Injected FXML fields from SceneBuilder --- .../demo/controller/MainMenuController.java | 35 ++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java index 88479b18..51abaaba 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java @@ -1,2 +1,35 @@ -package no.ntnu.idatt1002.demo.controller;public class MainMenuController { +package no.ntnu.idatt1002.demo.controller; +import java.io.IOException; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.DatePicker; +import javafx.scene.control.ProgressBar; + +public class MainMenuController { + + @FXML + private Button addExpenseButton; + + @FXML + private Button foodButton; + + @FXML + private Button overviewButton; + + @FXML + private ProgressBar progressbar; + + @FXML + private DatePicker date; + @FXML + public void initialize() { + + } + + @FXML + public void add() { + // sceneController.switchExpenses(e); + } + } -- GitLab From 71cf93e00323b2037a2fc02665eaa38773913e1f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Sun, 19 Mar 2023 12:59:06 +0100 Subject: [PATCH 017/103] Converted global pane variable to local --- .../demo/controller/AddExpenseController.java | 19 +++++++++++++++++++ .../demo/controller/SceneController.java | 3 +-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index ebdf0451..23ff3a03 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -3,12 +3,19 @@ package no.ntnu.idatt1002.demo.controller; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; import javafx.scene.control.TextField; +import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; public class AddExpenseController { + Expense newExpense = null; + @FXML + ButtonType okButton; + @FXML private TextField dateField; @@ -18,6 +25,7 @@ public class AddExpenseController { @FXML private TextField amountField; + @FXML private ComboBox<ExpenseCategory> categoryBox; @@ -52,4 +60,15 @@ public class AddExpenseController { public boolean isRecurring() { return recurringBox.getValue().equals("Yes"); } + + public Expense getNewExpense() { + try { + newExpense = new Expense(descriptionField.getText(), + Double.parseDouble(amountField.getText()), isRecurring(), categoryBox.getValue(), + dateField.getText()); + } catch (Exception e) { + e.printStackTrace(); + } + return newExpense; + } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index b6e3e5e1..5eb54859 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -16,11 +16,10 @@ public class SceneController { private Stage stage; private Scene scene; - private Parent root; public void switchStartMenu(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/FirstMenu.fxml")); - root = loader.load(); + Parent root = loader.load(); stage = (Stage)((Node)event.getSource()).getScene().getWindow(); scene = new Scene(root); stage.setScene(scene); -- GitLab From 89356852afc44db80ea4fd51c50232b70cbd84e9 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Sun, 19 Mar 2023 16:00:45 +0100 Subject: [PATCH 018/103] Added methods to switch scenes in Main --- .../demo/controller/MainMenuController.java | 24 ++++++- .../demo/controller/MenuController.java | 66 ------------------- ...hly_budget_overview.fxml => MainMenu.fxml} | 13 ++-- 3 files changed, 30 insertions(+), 73 deletions(-) delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java rename src/main/resources/view/{monthly_budget_overview.fxml => MainMenu.fxml} (91%) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java index 51abaaba..84a7285c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java @@ -1,10 +1,16 @@ package no.ntnu.idatt1002.demo.controller; +import java.awt.Image; import java.io.IOException; import javafx.event.ActionEvent; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.DatePicker; import javafx.scene.control.ProgressBar; +import javafx.stage.Stage; public class MainMenuController { @@ -28,8 +34,22 @@ public class MainMenuController { } @FXML - public void add() { - // sceneController.switchExpenses(e); + public void switchExpenses(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); + Parent root = loader.load(); + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); } + + @FXML + public void switchOverview(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Overview.fxml")); + Parent root = loader.load(); + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java deleted file mode 100644 index 983dff9c..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MenuController.java +++ /dev/null @@ -1,66 +0,0 @@ -package no.ntnu.idatt1002.demo.controller; - -import java.awt.Menu; -import java.awt.event.ActionEvent; -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; -import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.control.DatePicker; -import javafx.scene.control.ProgressBar; -import javafx.stage.Stage; - -public class MenuController implements Initializable { - - @FXML - private Button addExpense; - - @FXML - private DatePicker date; - - @FXML - private Button food; - - @FXML - private Button overview; - - @FXML - private ProgressBar progressbar; - - - @FXML - public void switchStartMenu(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/FirstMenu.fxml")); - Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } - - @FXML - public void switchExpenses(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Expenses.fxml")); - Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } - - @Override - public void initialize(URL url, ResourceBundle resourceBundle) { - - } - - - - - -} diff --git a/src/main/resources/view/monthly_budget_overview.fxml b/src/main/resources/view/MainMenu.fxml similarity index 91% rename from src/main/resources/view/monthly_budget_overview.fxml rename to src/main/resources/view/MainMenu.fxml index 1a6cc60f..586efca7 100644 --- a/src/main/resources/view/monthly_budget_overview.fxml +++ b/src/main/resources/view/MainMenu.fxml @@ -19,7 +19,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.MenuController"> +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.MainMenuController"> <children> <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> <cursor> @@ -47,7 +47,10 @@ <DatePicker fx:id="date" /> <HBox alignment="BOTTOM_CENTER" prefHeight="28.0" prefWidth="574.0"> <children> - <Label text="5000kr left" /> + <Label text="5000kr left" textAlignment="CENTER"> + <font> + <Font name="System Bold" size="12.0" /> + </font></Label> </children> </HBox> </children> @@ -71,19 +74,19 @@ </ImageView> </graphic> </Button> - <Button fx:id="addExpenseButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Add expense"> + <Button fx:id="addExpenseButton" contentDisplay="TOP" mnemonicParsing="false" onAction="#switchExpenses" prefHeight="125.0" prefWidth="125.0" text="Add expense"> <graphic> <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> <cursor> <Cursor fx:constant="DEFAULT" /> </cursor> <image> - <Image url="@../Images/add_expense.png" /> + <Image url="@../Images/add_image.png" /> </image> </ImageView> </graphic> </Button> - <Button fx:id="overviewButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Overview"> + <Button fx:id="overviewButton" contentDisplay="TOP" mnemonicParsing="false" onAction="#switchOverview" prefHeight="125.0" prefWidth="125.0" text="Overview"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> -- GitLab From e928846e8a5eeb1fd7b876ad3e94e7795e64392e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Sun, 19 Mar 2023 16:19:38 +0100 Subject: [PATCH 019/103] Changed image to addImage button --- src/main/resources/view/Income.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/view/Income.fxml b/src/main/resources/view/Income.fxml index a6cd5b6c..090983df 100644 --- a/src/main/resources/view/Income.fxml +++ b/src/main/resources/view/Income.fxml @@ -77,7 +77,7 @@ <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> - <Image url="@../Images/addIcon.png" /> + <Image url="@../Images/add_image.png" /> </image> </ImageView> </graphic> -- GitLab From 1fb2abe096ced13fa015c136cf6badf3b76adc8b Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Mon, 20 Mar 2023 11:39:23 +0100 Subject: [PATCH 020/103] Added method for finding total sum of expenses in ExpenseRepository --- .../idatt1002/demo/view/ExpenseDialog.java | 167 ------------------ .../demo/view/ExpenseRepository.java | 6 +- src/main/resources/Images/arrow.png | Bin 0 -> 13233 bytes 3 files changed, 5 insertions(+), 168 deletions(-) delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/view/ExpenseDialog.java create mode 100644 src/main/resources/Images/arrow.png diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseDialog.java b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseDialog.java deleted file mode 100644 index 147ef3e8..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseDialog.java +++ /dev/null @@ -1,167 +0,0 @@ -package no.ntnu.idatt1002.demo.view; - -import javafx.geometry.Insets; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; -import no.ntnu.idatt1002.demo.data.Economics.Expense; - -public class ExpenseDialog extends Dialog<Expense> { - - public enum Mode { - NEW, EDIT - } - - //private final Mode mode; - - /* - package edu.ntnu.idatt2001.mvc.contacts.views; - -import edu.ntnu.idatt2001.mvc.contacts.model.Contact; -import javafx.geometry.Insets; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Dialog; -import javafx.scene.control.Label; -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; - - public class ContactDetailsDialog extends Dialog<Contact> { - - - public enum Mode { - NEW, EDIT, INFO - } - - - private final Mode mode; - - private Contact existingContact = null; - public ContactDetailsDialog() { - super(); - this.mode = Mode.NEW; - // Create the content of the dialog - createContent(); - - } - - public ContactDetailsDialog(Contact contact, boolean editable) { - super(); - if (editable) { - this.mode = Mode.EDIT; - } else { - this.mode = Mode.INFO; - } - this.existingContact = contact; - // Create the content of the dialog - createContent(); - } - - - private void createContent() { - // Set title depending upon mode... - switch (this.mode) { - case EDIT: - setTitle("Contact Details - Edit"); - break; - - case NEW: - setTitle("Contact Details - Add"); - break; - - case INFO: - setTitle("Contact Details"); - break; - - default: - setTitle("Contact Details - UNKNOWN MODE..."); - break; - - } - - // Set the button types. - getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - - GridPane grid = new GridPane(); - grid.setHgap(10); - grid.setVgap(10); - grid.setPadding(new Insets(20, 150, 10, 10)); - - TextField name = new TextField(); - name.setPromptText("Name"); - - TextField address = new TextField(); - address.setPromptText("Address"); - - TextField phoneNumber = new TextField(); - phoneNumber.setPromptText("Phone number"); - - // Fill inn data from the provided Newspaper, if not null. - if ((mode == Mode.EDIT) || (mode == Mode.INFO)) { - name.setText(existingContact.getName()); - address.setText(existingContact.getAddress()); - phoneNumber.setText(existingContact.getPhone()); - // Set to non-editable if Mode.INFO - if (mode == Mode.INFO) { - name.setEditable(false); - address.setEditable(false); - phoneNumber.setEditable(false); - } - } - - grid.add(new Label("Name:"), 0, 0); - grid.add(name, 1, 0); - grid.add(new Label("Address:"), 0, 1); - grid.add(address, 1, 1); - grid.add(new Label("Phone number:"), 0, 2); - grid.add(phoneNumber, 1, 2); - - getDialogPane().setContent(grid); - - // Convert the result to ContactDetails-instance when the OK button is clicked. - // Check out: - // https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/Dialog.html#setResultConverter-javafx.util.Callback- - // and: https://docs.oracle.com/javase/8/javafx/api/javafx/util/Callback.html - setResultConverter((ButtonType button) -> { - Contact result = null; - if (button == ButtonType.OK) { - - if (mode == Mode.NEW) { - result = new Contact(name.getText(), phoneNumber.getText(), address.getText()); - } else if (mode == Mode.EDIT) { - existingContact.setName(name.getText()); - existingContact.setAddress(address.getText()); - existingContact.setPhone(phoneNumber.getText()); - - result = existingContact; - } - } - return result; - }); - } - } - - - - public ExpenseDialog() { - super(); - this.mode = Mode.NEW; - // Create the content of the dialog - createContent(); - } - - - public ExpenseDialog(Expense expense, boolean editable) { - super(); - if (editable) { - this.mode = Mode.EDIT; - } else { - this.mode = Mode.INFO; - } - this. = contact; - // Create the content of the dialog - createContent(); - }*/ - -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java index e6fb5b2c..0cd752af 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java +++ b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java @@ -3,6 +3,7 @@ package no.ntnu.idatt1002.demo.view; import java.util.ArrayList; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.data.Economics.Item; import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; public class ExpenseRepository { @@ -16,7 +17,10 @@ public class ExpenseRepository { return expenses; } - + public static double getSum() { + System.out.println(getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum)); + return getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum); } +} diff --git a/src/main/resources/Images/arrow.png b/src/main/resources/Images/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..9d65c03b2a4d3710c674655f1a3201935735c9f5 GIT binary patch literal 13233 zcmW+-cRZV26b^!@s)$Wh?7eGN5u^5sy;tqMtF;?SQKK<S?Y%d(cZH&A6-8;xqW0#C z?+@X}aqk(=Ip^N<K5w*^h9c1eng;*?fJj+M9u5Ft$zXmy1mR&mCDD&9G2aM0l#ILq z07A(97uMvj;A;Q?3Q(4Z>AuR`4I*s!ou0bgJk<Z9#PP)fq@1MXbHF-~4Oe2;<xONn z38%{MrIsMHyt=NKIhy4ol=1=<ws<<<%d!OQZ(I*~uhZ<)yxD7=`RttCarQHH<KpJ? zvax-Z_l&u7RqN;HgO6=KaL9r?22cZjIA~AQ2=WeoD|exe4nv(RE>}ODC}#qNZ<4BA z@0Cj$h?~K#-HQ9~NPw_dNsT0^&l!6vNAUye5bcAuh2(MnyZY_W>O?3g&z_&l?HM{5 zBJbu{o8@&p)N{ru%5j^@s08j;vQD;cD?XHl^t-hp^Xwb4Qz`pz<CBkK?ktA0VxS;v zX3M<BW+34>p{W{Qxa#|xE-s7pvF3T1stnA+`23VqJ#o^i;6__GPpFPtNc7dOpQzm! zZ*3q!I2D1R#K<7(h&hTH!Q&UElVT~!`zrY`bTzOXWM&->eE|fQ>o9|UT9;CG&(R=M zU1VQ1AI9yqAK`gl0Ab)?J=~4?%u!Tua|r@V7b5GO{Gnw%sR}W^{C*(pA&*+lY3iwx zJ1I%*5V1R4Y9Z(mX)$I`i8TJ_#sOJV__AJR)}&goY{6QnZjeF(G%7co7-0$x2upaJ zI%kqd$wAK_^@#!FObYssg5XKrYdCK$B}^^<nS)2OEEt%GA2ZXbf5_C+%Ig{U`A723 z!<EmVaA=)nSi*)<`pP0RIB5;?hM`>+#V*i&XbgmnQgf59*P^7AsX?U;*arSrTxg@W zwF*&XLVOAEK#p*X1Oc~1%mY4YBvk9`j_Ly_&|MSVgVKw!;Sy~I?NUK%L_KC@8JI!V zEBwDNkS~F<O*-3S$BrnXUk48vju{=cL=3q6y=QwJeK7WurIer9C61Ny@36ab$^R2Y z4Ye*;&Km)Agp~Mk(0ZuGn4Oh}kZ>p-9%-f=@-F#NSb}h}w3Y@r<E!Gs%~i2rX})F1 zQ~!n2F78V4w+(m%;hWFY9Q+bvWuk5mj`1HHm>9bB@Ub`h?ET}sn1M=-Fiy-WrDfP# zYU74OFCP#hT$ruaU${bnk_F)BH~Cw(0~FF0xr^gBW;o7Z6O#~mU%jO&)AAPHC@2my z5Oz#thg-(bZ2h<f-qOSebXTs8c-CGr*JTorhujh*GP9-+h1sURqnsU>yt<dB0M(MO zI(cL5SGRY7JJDlA=6_Ge_1tz$S08Dw&apJr_Ut6{`OOXk2w>pn?+8Y$AQZ}FM2HG5 z2C$%6OScXjPa#EIyZuFg!_#0xUkyk9=x-Q=U2Ag`f4_(3CdX*UqMq?ANZB~d!+Cjs z5KH~~dS-F$4U+8{A-@iA*Smu&xD+vI1{mZK=T!pBYg$^@<pOiJHu1WkzfUvH)4{yH zLm>r$9{!A*I}evCBxlS-cOF83-$@No!=eghOg$oZXNeks;sRsf4b^Xp<aY`iTisKC zNm>TKQa?>iyHC9etFQ!Or8e>z)=(1;ca11hZbU7V(f^+gYWgsOzX@-*CHsY<e;PE( zs3)8->Wg)<C~0N^ZF|^Zpd&qg1u2XVnaV}Klm)unX;lsAh0?r(Mjb@r%3A92Bs{_f z5$**|W)K?=lt%eoTcGj}O@AX*|5oojCRj_wB$rvw;@6YpE(#+OP$^l#2BmD1(l+jd zNqDX5!(a{B#E38BUKNlYTI8FPu5?g1m4PAjk0b|f%{S}@WJ*?{1UQK+-0$z%q_Gie z{#+R)?220a?GPaj6A<QuMTp4c-nIV0s-fh3QI5&OZBy&3fOI5Ql1-uSj8Yu9h*M+X zwmJDz+f^PE<1i8mP(U;?ETPGvCVKLQnGc-Agc!++3RY-U=v}SIrG%+|ej$6-xwpiQ zNujA;Y=-T)_cZ=lI1Kb&ZXTous;z@Mdy;7{&X9ZZ;HM-A!jL@VfW85wXcP?8#v;WB z4eG()D}7o?BR`iNCPw^;%AwTBJW_{Kn3x&WQCipSH03>5*4|s$`#1xIftBaPR^kf> zu^YBrG!q2Li%JPiHz0h;UYx&Xlb`w76Ir*_%ynsBNcv}US1W+!X}KY>54btb>J$)D z6&al{<G0lHg&3&SwVPkGBLK4#CU0XFJ%O*d?ZJ#nshCKT^N+A|f$M4y-;6nZsw7XV z!&3(`)F^eDcb)%z6vIE=+Apmo)$>c}zC7AlT89oyY6r1!guXxyN*JMP+sp|pQ7lbC zQ;eDW+&z)JwbRu79dv7ZpL{Xtn}#LtL4>AP(2vDwB8VXvCmDn96aof$1twp*%>PnH z8MHV%IuKn-yv^2k(_d_k3xNOydTKnovz6j-9o9TF{dk?Eff$85=QvNtky?55vsUDG zPE>9Ee%A)@su%dG3A_R@HU3dLNxk`BUquMPB=b92pi?<O2a6ba(1)_w<4*XOKk3u9 zqjkbNr`ZrMyfqO_`{=a{10x5&C~^WP@MZ0Um<nh~o~F(P-@ZE}+MB1Tl3uOE)ZB%l zmXI&~K!GrRO0cs>NfQP&mIR+UZwL^&(Uy8yFum$@>q^GBS(ekaOhf2xcOFq1ryQ6x zDw%K|*-~(xUg#TpEDDC(6Ad!Hszi7yhXe^9pP@f?-jxRyBWL)Ba0#r#Ep05Dl;D%t z4VybcnPB14hd;PTGCxRB%3g~Mt0YG6^K_2KAc=YA!!fBzid`Ql#p>u>I?yZYDnk#V zMv^-)iY3qC*V6oYdniA#Y{4BKqVwcw)C)F{wV3717zvz}xY;Po&dRmy+KNHq<`KfD z4sXr6yqW#Xoj1JnY9L&@iG~gaws}Z<9)fJph%P6K7kbc>uI^Y7Z20!{=vngFFlEmi zO@oqv7ao1O16w$B@F4}L)r^x{W+JcB-3MEYlEN|zY;2pZid<-Q9_OwmXVbJ1{<1+Z z1SGJouv!aERi0PK%*B=cyi?Hy!;|%jn2`UMjoJ`97|B%q`>|LZB+ZC^DiMl7c`V6j z@9+b}UrujI&@eeH#B5*N?GJYko{&;a<3)d$!Cr=g5T-jnw)>v*G$lC+Q&z9%j7<6% zL(M_1&NI*|{|!XiEWe)a@2b2weZKjaADbZS*qVH#f&!QQTgx|g4BXz-py67)AZ=Xk zi4T*v`f3a|c+u|o(s6sFD0~XnL27280IQ5I)pYb&SVHsgKjTo}6zHojUd|jt!n726 z{Dc=?=#Qog-)WemNFt2{q*G5RLvVS(hk*n;SF&1>EMPcW_==!^WeIn^=4vM=Rq;rg zYHUO9tZKTDLzMsw%=BKT;055_b1<AGywtkfUqxxlD8Hq6dPiZR`5h(fA1&$~`7S<J z1{NfoO1oXl2{<tb(9S$A7QA-zK+znpg+nPEnjL+p=tsX&H{96nZ8#THaN|s{>@l=V z2nn!DnmPf&Pr3e$M1;N)p!R#6d%6~0!hLky|6HGcjIzNKHl^GDRKg@D%NNg{AZ_xT zRp6k{&%8&Eu!_-^Razk_jc20ymPZ)dL3`?qVpZTTuZIe-L)AsEcwfKML?u<s`-WuU zb?UhyoOfbx*&+|?TCBGtnU-UA^x7gSFWQDXA&^R*?OGP2He(N;{?Z!aWmQBaEki?P zGhXfWMoN+`fiZ0%+*Zrtz^F=HoOcfQRR42zyK?XQGf^cf=N9=3Ujk?G$JLU$vPw^I zYR|q&iX!+aUOZsNnzP<&<o)L+C=r$*L2_KL_*YCn|5^7h?{!n_h32v!B~@>6=f9B` zfke8?3rvf1tA3}Fg}meTMGAN6^)`<uqX?O~@uS{1D{`Ksk0GU(b=iI7h0U3F`TRan zEougAMG;zZhX>OuFL@jZ#Z+pjVh&AYpMoa3KQB>Pb~cC>*}Jyp0S~0Xs0h@Z5_sep zv+iB@7tpf7XAT&9JY34GHO4C@c$r-QStFz5m1Q}xxZ+M>Ex?($q9N;GvOj6TTbXPt z!T%^>&a>T{k9G>@v2P}mEzWC(W1`GHR>rLX3T++`YmowsIi&ph^Wj4R)d!urt<{K9 z9gkM(C~Ce6mY(vGzls!~iv-d*ZOZO*sp}g{;V<@nY~h|)xhwK}hPVBz6poZ_tZ~ck zxoSbIh{;8_de+*-RUKr!Ru$jxo@xdwwU;8^?Be=AEmkhB)#fYH$QwYVmnfm{I#CFV zPA{YpOZdu0@{Z3P@BD!@C%sU~JMs5QLEgtnzoK690ZG?B8nBNiTdIGC=8|3MutYIC zRsFJP4f8T12#-#!-CFT{Je6xHf-}EBp{n_)^<3twx(zEPD{WxFMjTD|{rNmtt=`?O zrLaV7qo4tQ80S#{?_8x)(IBt4G;z$29I=gNWHHw=XYRxA!H~s0e2kN?IQE%(Y@O#5 zsc@@@j#wm#v**8`PF5R$un|3q+C$N$wJ@MkjeJR5gdK&hQparCi{@x&{}=CVTYw%- zv6bq4Q6mp{eUE>{Y`-c-FeXdu2E#63XlIrJ;4X(1G{gk0kBTew>8ZT*e59W;bMQ?W zRjA8kUzByAmNedHetT3%*6mpbczzRf0U#*)?z%T$AlENO0lV^Qg|)^bS7!_2Kd{vG z<HmKU)Zz-2|9Zrv!yBLiB}p?fb@Ap_8FgsUkfp({7>?&!=4F_SgMN7JQTNg78F~hi z^exe6sQ~AL^Q3qrRBN|DaHkuap;#9*&?4}Jw&9N>rs*s+s`gPccLH`73Nl}ae#c}h z3p*Djv6Xo(?vM<7abA$SLWWp|3~y3kV*efE=J7KEoiwTH!0ba`2t_|ETpR(hK`tn9 zB(+vOkPraYxm~XE`}sWNdWkD*UUnT%8re!w7W^Y7ha3@c@}?bW@qpSFFDS%(d!A}- zP0HSbv9gC-TZ`WP@&NUYvpJ!L%yMulE9FrvehLIdLNPo=RX{mB0Du37v6O1<eH1c* zmW8aRfAn$7;@5v69}&nLd(|kvPg-s)49`o3TbTPj($)jkjx;}d+zne1>^Qe^xu;A% z&txV61_^-3qF%}5#c^($S&vK)NZGyFrGFk1ExSwTxb`snBR?hS0Nu1t`fV9JsC~<y z-J((w9uVavjeepMBxJuXCIVU;m+YP{&||l87CbYR>wT;`N%#jcO#88axhJXJI3V%x z6H1eFnaRrK(1D<^=yTE3ekFO&V-ULia<2v(+&lfC2vIl77|?=vBP3cGar*arc2y!A zuwk5ur?3y{!+y%p4)1mHnbDvwJj0YhRfuJ8uQFW2HLE1=K_ihJ_W8*uKJM9{JEVbf z$?sDl0%wUpnNrr#hR*aK+dJygda}8yQ!jq#k5iANS*b8%l-!V%>(^aj6{d(D`=WKI z0Fl3ZEA@dow?EUZMY4uH(eID~&6UiT06x%Pb7G|mD~)mX-F}J-jWlOgXYWv2?^KFs zuWCKK9YtS7BeBH@C~}JZqbbR+sOaUBTBvE$c@L1z?$3gA5ehpuM|5wdI^|hIEqG|& zSbbkP$1^sSL?{ddyE$Rs-tMDzkX^dwTA3E=Y3UaUU{oqcHbc*D<W<~s&))QJ;~U{O ziLsT#0+3+||7VWMEKzp@?Rz^cf7Dl5&YKWGZ}~O`B%i?(ZmU?xQJZLLVB%9%FJH$- z){YS(=<DF(DWL4=7jd(BT&twbN?4cH6frWMcODef4nw{Q`0ihGXHRfZ7g_5c?QD@S zhka}V)|tH3s#vGqjF?R0N;-eWEfSOubUxfb?NiQT#Xy!{VZ2FSwrGGUv$O;@AG7ud z9?SVJNHp#*a^xOcrwg$<Zpb08?9h!66~nD7k+s($FHiz1qtXgSMbDWZR&D4LVkg)K zcpd}{@6-uYDH9?JHWemPmn?Du_42+6UXC^(8Kd~L)SehNzY?+G32!9QWeZh*k`m%D zQb>j>gg+=0FktrN4dN=kE1IgV+*;;3`Ic#vdg6@IWY<hekiRrVlO{i77u{OquyY9e zB;eERD*Yr?jli}_Dcs}x$s1?ZsQhIUA%b-~?a!4{`#j<rvi-EJ4)MqpuTSK!tUW)o zlpdK_1XdjCJ5q)#7lbtb{nbxlbGoDaGhXO2;)G~l-12+b^Y`{bO{Id3aX$#LMuVa{ z<=!M>cKDbM@K~tT;2LE8?I*|n`*nz7_`;0_#z#(HM&+sL^V<D4G=%z+3%qHx#sVT^ z^;(lk4~AJL)tIf!EXCCfKYFyOvum?#iViI67LnyX7a+54#A4$B!xipWR$?l9spe`O znj;7FZ(KNm3m^Pm3;5M5I?>vJvFHB4uJAuYR7h33xUI$l?#kQc!fieC5r`kl93ebb zF1X7j`v#pa!iA#)N<CU<_ZDmQeYy=FD~jQMHK)2;^~nhM(f_OJx+4z9{P9Ud>-NV! z`F+TO6g<*=ZN9qaOMmbwsIx(fEZ{~~XpmD3z5wlG4sE6D?xL#IdR^;6qtL(16zZLN zC_+Gi#zx<%P~~6K69|!;^I<Yw8G<cF>$B&>C1KAU%fy{<Ffyggo~jU%Em_>^{qYH? zG*1*V3J8UTX7>0No@DL)TWlA07`lU6ZDLc=l2o|HYD2mgA0^c>>&QB~T!!byS!!Dc zLaIdj;WtrR(j0ucpINM6nFj2{$b?4?jZam%=}C(Qnb+2M8bl;yQ3I14p}RqgO-qEb zw|_{PO@PH*pYh*Uc6Q{K#e$t^trmebpEPub+!P=o@$Po=gA6H3g6{}BWiXz)9{#~x z0Hmu5tOGiGeQW>ltm&!qxd5co^tzt0{HmHx@%cdWQ|F_(_swf*g$+hUgAawBXf5Hm zm<3b%+@$&xN3Bkvt8pg+3C{xRkZVat$;7%<Ea<c~5WY2M^c8G!pXa+b^vBJQ)0^?H zNnhzQ5}ioo$5zaL4UhfHIbG)n?bt%xkPVal_Or>D@=wmz?^F+8h@uvYN7j4=GyVdt zLd)LkX1kgFgT?C@=e2z!C<4r!b?MUgk1w%|SvpBlN;AnE+EM@B^cWh8<iV}!%6ht& zqyF>T24gzacehy8U}ae(?a>$Fq=ZM9ix7yVh|?NCw<n4y?m^iK#o|cRlc-&}6!>2Y zIZ4-f(+gc%mxJan__{LeN#no0X6T=nH4!(XPS5PAc%+E+I9Q*+@$pnG`>PKgEAF?9 zxuC&jX0q;A<%cW~yOr(?d5H-MR2(z&a&Osa8Z!H@b18evT}MaBe&QiZ5kO>zX~ukp zvL^m)!63ykjn^i6hp?crZd57Ca!7+i7V9x(k$fHRmZa$K$6V=VJPh`Sgpl_ZmSv2b zn6m?eL)ZhDlHf8tAcWutMH>m~0&X0lhWH#LU31Z|#Ct~AAgY#XwE7{pE*AX|TaE|O zD5{K=DXDLiP)aQx6XRM%9?3|Rwh))(<k6;my(&gP9=@&$M!i$^=(5tb&ct+X&|eo; znASe~m;zyq=y-s!@v^hwtVpqxO-pY~4qur^E~1w@GMA32qD}k(H2tNE@*v|@jHTf~ zP9gQiz#T)+;~4tfb6mTXK(+9lp!BOY+wU9WzHeeVgRXc!PSW&0)gEpA;F}Y<#A0sv zcbVDh*W-G?JHdc2r-SUFe_YZasJW!4iKg6HX7enQP8ZtOukSJ|#-K$JjNaW1Iab=Z z78BUzJZ@&2-%NEMS1atQYXgRZ1;_MMlXZl6ll@>>J3I7jmm(*=c+#9!O4t$;HFKOr zq8Z@4l>toUei$cg=r2Sjla$H-I(06RM(a6&c)<iV$nvek+qwA`s7hgnHtI6Gbwucx zyirsZi+OlBzqw&IOR<1n{B03IlRS*5Ll`sH3Rdnlo|pMJ(0&;{|0xHM^ylSdOb)T7 z70s_Eh>w-;7JGP(f3z!ovh8Ob2k;>7#wbfw$EycWWewgNvGQ>O1?}JVDwqohRK<m5 zq9&yI@E2qz25Y^arlP~+UP`0`Wc-EnR3Q1#4F2NXGv>xO{!}IwP|E4<X1-&G6-Gn< z?-(1H7v6Keat15!vQOh>In?k9Y{pbB<pjFrD&wdpmv&fUR!SPgT%B*aIEhmPRTe!? zqjs5$uY)-%yTeAGl(uQz3-G8V&-F?HNmOMGzc1l2im_DAtc(}<L|?fP%$PCbCNK|+ z=to1NP_T7~eGQ7GQ$4_=Yakw5De2{Gto`BKZ5C=nm5C&luxfr^BiSp=?|#j`0i>|Q z+FN^0QDl|E1a~hN@);bUob=duu&PGkeQoDye!&!Sv=&Cs+;jQ_{d@A=)ATPfxqu|* zpHZICJTBU%Sjo0p3j%V$xYBl*FsF%r{wLprf>TEJP-bO6IeI#g)N^ihQIac>h9aZG zQFOKb-riFe{U4i7e4+P>HLHDNi&T}gP>CC^;3lQilS!{MP@#ry83UcAGcf}9D1(ui z4Lm>?9s(|tT1C{#N_uCJ#2gI|_dM4AY|RrFVssHVxm<~==6;LYP*E3K2})xKg@!ZP z5t)}ub~_s9OT6`^SHfaeu49X3>Ssk5e!AR(J8AUgJ<#{0u|9u<#*3)3RgBq@ioI88 zoND8t@z%{9rzX6L3+65uQ#s#h5rUZ;dgB=+#@6=C6X)F<@BG?%!Wngje5N#!87uMj z;F2kY)2948wUgQE7h(*^n150ZUWBJ`dgmW{a0r+5Ir-@E9x$S*lYT>Hv+fOhxr~`g z74Z?WRN!`ecp1IYpxf3jo$3{Kn#@ANQuSQmKAOSxoGP&>!7}q7MG8cze;OQpF!P#? zr_~C;9gTZ~yr8kpg!8wqsQkjIg2;MB7gO*8ALlHWkNW`Gm-&y{_i<##oJQBeshBXA z`A@2uuzkqYiDDJfOIhRp;188eUKz%@u{kn_FVQrt`EzEg+^_3Ejj!}q%nNTB`jb(N zE;?uT&ZJfNUZY$)6<`k^*X&25=#?Q3Oo$&nFQ1`r)oV>{aMCLRS-&B_mrArmLE2RV z%7Aew_Hto5qfk+udsUn?#4mAwR_pFD{G3_#>BVlpN5G$ny0VlN1LWDq9QE{i`D+@c z6bf(l|M#+SG9&pnKAAz5CB8>#@DXo3k&*h#Deyj<@P~C<x&LOD2O^MbK}7ZlZr;;< z{m+%AiVvnfBkos)QfWpART||4W+5}CCeUWvrsmv5K557tT+AOAe<=D<w%zP=`6t5T z1u3=aZWP?^xKQ|%y|ZxgqEp2E4p++ihrHaU)0$bOVAmMdo^59>0=rhyt}b>97Tf3D zdMw7OEqew3OUhy-lLb*%&gjm`IdtO<%iacIa+_GCi5{fnx%j;)=0fC~08IloUbEJA zow04X?lpOiau^$7?*FOAUXD#C%0-+A|3|}{vX}bxI?F3tL!bWsAHxLADSn)G6$U2w z&~5%}yw%Wg``f_doOnD>oR0ySxXJ6(*?wZ%$`86#9ED3hM!Oe&=$^58X96!RjK?!c z_4PbkWiOP5DLPAmyZ(mwuT+ce<NE(E3FAh3qh!~i$np+haF?Saxm4^09T(lq_5b;# zrEe4zq9>&w@ulT+DPs{7o9@||{`q}QpT^csZ=7j&6k46EBd6a9bP;pxPjemouZ^=l zkM*%8I;McDoY-{lR3P8x&iujub{GjX9dq}_>}n=2sN`=}HD9V-^Us_Kqa!2Cg&+N| z>tFG;jG(6YfOa;eQil4kJ6COoO2v9c0YU$}7^Pci_pcenrtEm)%Wg<x64YtQv3{_p z<$!c@*yoYH$8sE%rtOjKwscW@HLxR%EzWew7H%Bu&#|0q+A&dVRNVT%M}&iN`b>gC z;GHrm-@cMbFYtN}@#*YsG?cDME}7??T4OLR{xcSb&K##RCom1!JKK#`Na*V)uG;57 zsXtJc-R<mbZ5P<)O~ZhUbX2TkfV-=U1A~al&gnwWoIX=?VJI3`BVPX_R{m-4{bH@h zxT?EzXUH~{QWByEDTBQG_65QdwJ5-G6;UIl=Ko#GB6e{pz-5`ca_7LWRd9h0mH#p{ z?1RMA5&(k%wwTiJ!c?xi?5A~?-MYv#Y;=`I$a??wQAq~@#%>L~HDQUr=Nu@WLNijQ z&9`(*=%D<Du}vz&ocu?PqH>rG4(vHmZ(<IA!iySTg6Pw{0x&7K`?Q!(NaBmaF428Y z=C7i$HhtomzUJi^r!wff{&nxOoRdC&S+fgM;iIRSBW<Rr)^>Uwx9wv)_6n)^d0I@k zIR<;D5gnI*UuC<@L<T#Pn`#x^QVpv(W9itIyHxD!qw8OSaQR;Bs>1<n@AUMIx)7tU z+7A6uN62jb&n1$wt=V4qm=OoXw}9Hivi_5#Z=XG{Gu5xF^!Ip;dhv_;$16HxkPAdF zI_)s~6#_osU-7t|GWO^x(DxWu*RZ<jnpa$x9rmGSIf?bh45YMLQxH*%oRK-zzFv*` zI>D@-7B7S8l^SW+52}`Pcj<~bPhFpztGZJYOf7NadYq?oxgAg$^Quo#aPB8+?SatW zLaMGK5yDSCe1y8hr}FzXG`;Lni*vvZ(DoVkzIhm#?kk5!h}0Z$znj}WT0Nsh^FB8y z#>zP@;t2I#4hR~mB&2#uE(W44dv&cCPz*F6)5&zxx|I!Y$tnx%Kn(A_;h--wHMnGQ z6K}YErRDjnGw$p5?b=ns&9xapA7Q7NGnGkD@aX;z)Cu8p>N1e~qdJMlxHZyr^%_$t z3ND9veYg>Ykx74=Io;vr#qIEb&O>grGAciLTuVx$HR2^(%h)lYw^5w<ov6wDhD+J? zo2itSzVw)kaM$S+c%35b`UI0TYB$(uejyWX0D#oso(o_V@j>Q1QfehpcId+g%t5^E z10~@n%7#MRR8dLM!I`}TsEa6Pwcf3HuOaPc(u$_FWIkj<M^`@bN2=H7GtLjS6X?wR z<p$?=aVK}dU-+r5YgA1&0qxYb<uZK1oyI~lYe7o64BisSu1%YBKWhpBP5u?gn)DJL zjyd^7{?15udqZciu<mS!Za^;3AN>(h(PW^cq&4tr;VsKDON7HHmqQpneIOM@Sb4tL z8qWKs!*PU*EAh^!xuJh};aX=&E(bBcOrKT#(pma@Yy%a(x{RKWOnZtI`zz8siuU%= z9{b>Im4SI~yZl8Ex*2J&1a3;@%I0Mt$LgK2r0$`HnW(FH9wkm^Zf=)MT&{&Fe7dw3 z**dlfT-t6^yU?8tCMv26!+0$o8PDN^(<_?9>mFP2NOT+9(7rW5yKnAi-m2kPOhnhs zJA?zx;zYJ`%}j!Fd}fosYE;6LBFq?~-pd=Ek@f8NLk4Sb({!QU%AnHXUj-B1twu`| z2OH#m(jj3K_S}3IrRq;n>8iyVhx+Nm?02rNPxvU<Cabue$ua~cF?-JAQR8A&)Xl5S zQK)jb#Lz7DbumZLhT~ImbX8tw9=?Sjh}K+c=1;ZU_^c{;3=;kc79)5>+1!-UX`VJa zL2Fq5!5J)E64xd-@3_X@s}_hCg|c5_#a2q~EU@eB&#rx|_-XIjrK{3>W`HV|=tw)- z=IMCh+Bb@M_I9CK`)r%1DPkbF%H;i>0@*=4%0h+x@LU1yi1Jq{Z~2zW&xDm?q7l5H zl0?zWO3)>T62l+ucP(v*S?FEeFot>eFE7&tY0w&Q%<shyc>fh?_I$j#96zvkE}9&l zczENQwp#afH$<h?H<vWXEZvn?dqM1mtQ*;K;o0QYPqcyIYdAjlP_(MeDh=#Hd}Uu3 zmz_(v3~O&vR86zP6=nFLkmmR?K+uoZF{_0qac_X=?rlJBh?aT9R26e}!V0`YKcL|- z0J0tz+<cyGTk36O(LWLtqt*ntlo{%^rXSv_)~?cS(0S1_DK=rX`fi)&op>D8g^VJd z(=!iI>@*tpa?)m&^sHCf-wy=b$r8WwDMz_<=miuUf(eckH&n&n`N+*OcsZ=<Q}TR! zJ`1jmZW-Wpq_e91Rnl%+YF-*>et|{cyZk{*O7VgWy@yg{r-w3FHEl;+T6r!N0Oqk5 z_>yOsH<ckWAze~n|1dAnS$!h2C5(GglZk!DU;Z^+qG}{chIET{@t{*KEoH~v4yXZ{ zx2^Gd#Z?+R>{$os3#XbLShFz*c*a4Wx2Pt2sW@16#KWic_rnKc8l~`qS0-5|Uz9le zS$Y<d@5--Bf#pQDaS68ot7l5B@d#8WxXG2dV$*m3QP4M>Ju<`uMbFQ?mN8qnmC^k9 z@=&23_>x1cPu|^-oAWf0^mX{5l)}PG?iWwasOvA?KF$c9*z6n*mbh{|k5+%d6HPqI zJ(Xazq9lReyv{ayu`dTHGN9vy&G{u-*7lGOh%MkjScm<;h-iY0xSg1IT}sEcae13o zQk0xig2@>KLlP|;8PZR<(QlH6*t1I)b!5x>PEq(OU=c&tv$7p|l7Fi!xW;;@0XO{= zuO^x8f!C&<LCIB(1S>2GT^VF&v-ef*xnypT>D5aroZ1m*i^!UGf3I=tzm=HA^+E{i z+HasQO!c+V!J9wz9cJOvkpd!Vf1+Zoy(AGtByeF8FXy;M)3=n30%Ril8H3pNjHx!Q zr3^eFy0>Z<!st>=g-TT_`5L3~=nSul{k`tCe_RF6w-iRiE7;fel4$}HUUQ>GxyhR* z`#3&DdH{6+_#19V_nWsk(<UN*yFd6RtXyOH`kFareaEf-tRIe1G!og<5wG#m^ETlt zQAI7Ov?_n+U~u^==)RgXf1z`vqA0UiBvG^Np2C(!t7e$fu6sd*R%D4<_>E#yt(k*5 z(JEs;T|DeFd>lxd4W}-5AHYJ#Q#`VjDd#Se%g)tg#pNwCdAh}Ldi#mx6yCM@4Zkv2 zy>+lCNc;Z#hY4%%FLj2Uh-T0F#V$%m$_56?MvZXHj7a*5mtf8HYr|ayl6NZ%V%k@U zbdnhd(GLEcK|*QNyD+yj4=O<I3nZ0mqR(38J{=eOe6|H&X7x%=zfaEW)5}iH(p!Rk zOVZifzDeSBpjpgd0-^CD!RpuqpTLnkl8gg)gKJ#3Q>RzvNQik#qpw?fkr)IRoU^R8 zTC6Y;eX09QutL9q(zO2Ng30?yd)vUuF0nVgA8Z!sws?phCbV=d9E5NXQ%}j!k)ID{ zIOlGssN+^`U05ZrppOGQzPe({HyweE>sLoPsReVCD~q912Nmrnpro?wGlLJ_8HCsM zvSeO39{IhT2XZ-U+VyWJ|B6i_o2Vv^9&<b_2l&ybs>YQHci^{sfQ4_8vTA3*^4-mU z8c~GF7P_4l7haBy^Mg*je;(slJaSJy3}|>RN4ugJ7Ulf19mxM=r+)2g-Fe9$whh&* zE9;><CI-HbCEcdc7F8v_TyFRAqO!PphamVxbw02-=&t<Lxi7}Q1e2CAEfu08P0SfM zywVj8lJ$SQMP`Z9ahoZz1s?I%bOqCY@6?ukZMgVI__%jtNNYOORJ$Xh1R&3Ni?<a~ zGr+hp;9}IDc`eKYm;G1i_YNpZmgkoRk9$j<di)r@g(5}rrGU5QwOc*y-kd9K*5DEj zl2Bb;OFIT7nmAm9xR-(BwQckel=XT1Y(XQd!S=NjeC^busM>y?(`A}#{fxN8X(Jc& zsfs*#Qtg4eYm;7XAoI?xazHX-D!v-?SoX)6<%_w~kNkvIeRyXNw4IYORgdYvDmPGy z{p1@Zx|U@iYb``YCYoQj)5fn>@p+#u=GbL-i27aQp>L~#epWvNtl>C_eNxt9bCD7d z6&0U-0yfn)bP3Tnue^>xG!V-xbroFK9KIq5aJ}NBFI+S|WVMzXO`s60w&eCAYxzCU zGkGgponr(`qdq2O3-#uC`RjeQtE&jU>2eJ7Xk>4Yac{`tBa)7Hw!&B7w7!Wk5rsC5 zQQfAW*u0>@fp)`-*OubMIIL$p5L4}fAk)0-PIy;?z1O@s8*FangS8qqi=Wr*qli%0 zl-de>^+hbNMhXp9!zLeOzuGtD2fI<|()N+g&#CEjjx^`9xth?hY*ggIu0fj!-Oe`8 z4WLu_R<Rf1u)K2a2Lo7z9iCy@_K~8t!0JPE8*)em#GJD|GzKxv$W%T6^MYs){I(t6 zy*@q6(F`arieP<Um~zC+#^}xCq~1S%8pOgvHuoIrnJs**pIyBa?h^4Urm2DBy*y>; zxuluw%i~>$sm&-uBf%l&al^+tZa=m(6P1y;66{OJU?3On#Ngc$y5F6wqDyRoB(=2p z<P}38QA%OI2eP$YX;xN!;EKXE+CO$*H`PJn2+HDCi+%yl`sd)->|yM%_uBxXKUFhH z*pU3bPwKsxBpbt?@UiPE4&7J3xuHg;BlsJDMRp~MrLcvhqn``IV)!N~yE~*xNJ4!I z^5C4n`dqm<^UL5}9)l<6PwVFR{=iS)1wJ``#E=UG#xM?jnwZ9CWyM)v5~&TqT#XHN z2}iPwgGNSAUb!x}wSV;wYC__HDy1;AhdI^QKPPjmMljNtR3++jsu@7k_-?(kqy=gq zx_i_bPYUHcq{ZN1G8_ER2izZpGhir?Uz`u<tOg?&8*SE6hesFDhQ)SoS1*NBdoHd` zX!qr=px{=&?{+@^1lIjlGxU>1qaEdHp^>0@uZMjUfP=Ll)?mZ9e+PR~f=L-SH#R#X zQ~R6qajS}88^D{yL^s<X%k$haq>q4)g3;U+txXv2-1C6})1`PN!_%HUhI)9trf2PP zu4^DK4^BLWK*EnDX)w=|L*4+0%7_*soYNdx+yK1(e$@NUw}jZqZdYC5%ysqMiRmd- zzZ`~$<YtL;o+gpra4SQ5_v|&OqHAvT$VBgeT=^TdT6DJSc#>dgIy6cE(dw1<I(n5# zc}Fu-_#{`>!G(3e)F|jcO2weVyl&FCDE}%Jm;Q%l_Os9?4Eg7S)gIqWq0C8A0j`6M z{SMbS2he^3E1}F&>fbCXtF8I+x+7S#cYXv2Qv;0NDBz~e;kwk{KFMt_182clWv<2# zdhxl)D7I+6`_AxdI5h=vcZ5#5+mQ(Ms)9)YTtAL=N^5RqRH>FURc8Ic1QVDE0%1xX z*=&90Ki^ggzH?RdT8y9NH>vP6sG-8%Jmj1;R8f4Se0j#g<s{(XD#<H<HG_tN|DWx4 z?QuT`WX1<iEIAI!3R+)(t}h!mtU@?(PQn)Y79!0T7^3|o6}(P^8DEryy6qg)+0nz) zpnnUc;eyyuS}s0zL`gOWf9cQ1D*<pkiFM^uU6F;$Nj>%_F1FhSJN`_hK!HsD)P0>K zKT3Otu!yXJMr<|dfpG)(RyhpEGwj2tZ$OZ2y-~2?)p~OD6~=YgR@kA?&}aqp!-SwK zhnoT1hc0()s2kE*xSV?rX1LH*8EER+rLXuA8buJHAR(Gm<C0Hc`KctWJV1>>CFYVj zSZ8mshF>#9aH6*Csb2u*5iR@v9R=EW;AV2031mHRPOp#KpD|v0GQ}9o{^a!w28|Bx zFtnLDX_qv6_D)r9O!Cjs!#1s6ud0LR80~-8TuXC1?T@svRd!V(DcMY?%v;)nUkuwP zFSL%kw@|fOr0+7J;$q(XQHcb?8oKkcEm9v~Py%4Md|g~W^V;epAeD03_CtbTcVgn4 zDf%$)^+?#Bl;a5r@H-x9<Vc|p9T)CD0LReXm&V)n>E=<RU}+@g2uM^ZRnR;)ho(#> z_B;D3gAW6{@>^-S*GmDcC6@?RTq>lzb6sNuPj*_y9W777L}t!332e86Lf`W8-4hni zmwQmp3dp)&0{%#$_*9PoN@ehRG;v-|@8^qNN&~0QN$?sz{k=bXalMLQd7e@D>(m0C zatdu(&+p|~I~h!&o`rUI()P;i<>Vn6xNCKKODE5jTO-@}-qXXtbXHgi-trRhce{T~ zXWaV7KisJs|1gp8;G92`wkTeF1K5=Z_oww#FmwO&PDf%Ja=aKvULSOE9<*s)bO#=k zLu5_au<3SAI+8IYQdPrae^E!GP~DUs3FY~tld{39n*ARy`Qy*d*Te0P!_rNX-<@^^ zU?lAg0e?Uol(6bpz_LLvqnt0VhhF;SvQqj|63MT)p{wh|OlfVYk`}iQM;PY{yId0s zv2a#OlUDE~i?VUIA*b!T6nmc*eY+@FKfflqp~~)h!mZ&G-sX32Q+;@A_cFN=iMlH( z3u(6^Wgdk==!f#=xdUfrX7vSen)Q<l=Zgr0Y$f23f+HAHSs$bTDkHP5jSD2OT>^7S zyOAT#YvG1ThqS0~g^!<ANE8=TN~t;xPG-`l)~R*0{>9L1>%)ygG?ToD5>SAz+o&G} zzSrZLw)F+;xDX1})V==k*}rn>ue+Y`Z5&`2utJ8|#{%$rU=hhMO4>i%o+76iPn+>h zdkp=J+tA5`%Mvh4xy94;gbxva^y;3wtOl5_F=3EQ#%?iU@DsVR!OT`el+uRU{0^F_ zoqcqt|F&uOW-uQJLoiJ0hZOQMWap-i{?ZFApO6<Jss*u!=6Qj6S8V|o`t+O5r&(;3 zN3X<}FxarL<VE)(yOJj%<qZG}GtkoWG4zS;_U}DECC_L=s&l~;_XMvO_=tFIa|{I> zufFOu)r)8a1#|$)NNphYU!&_YSA-ibQ<TMj?TMn;n5!_a59GPm)Hd3%^j-GRKxfHU z&jQeI7x!uKBm&*CX%gy6+gR}=F!2VdxoD(%aaX|s+Sr7pe0=aKqsKdWY905J)F;qF zkWfbiG%BoTa8@J%{A!a55C+)W3m!`wqln2S_Ho&*iqOZz6(_>T^ch!m;zP<gLUYRL zj9Rmjz*3|TW_{>7kitPs3H1~?FDxUN26@i3e&Dunx4?*D|34&^DfsO3_-@29>pXY# zwgb;Y6B9qW+N6bGxC&Ng-1*NLlf;munPZA@Xq5OnT)L(NEkiPEy=S;p;rHWQX(nzR zO>jTB7aQ89VuV%XN0b0uE3jMWQn+9|Gs^r11NUq74Bma_`BvFAH+i(`ABMEuPM=l8 z{JPh4WmXl;`zd~5YP&wWHsP|ZP|e-y1hY{jWId@W|GEN*C5P>kg^L+)Nm*UO(dGl< z3>6L67>60Bo!}cx&j6~t!)^}H@UgXlTMmd_5}=%T{g5CW`sE$L${xhjfVBQ`>g?^F z3i|h6rcc2A8yRUaUz&l=Zmk8gfP)`D+efYfIB@7dMIKKuwS)<Wl)Iq&+6?u_mM^sQ zKx&FGun|+ZrxBR$*>4F;c*E7j30u77+SRA8kW=-%$nX;NfAoL+j4<qKdGs&%`<0|{ z5uQib=d9>2dv|GbYT*946-N|p!E?Z&4uLZ+tplbQ3W)qmd8{&NqYvWjgNoAx1kvhV f^S~ZHzN3HOUt7}6EZBy52M3_6pdnu+YZ?9@VBP!{ literal 0 HcmV?d00001 -- GitLab From 710f9783477ccda4d6143bf36326a83176bb4c9f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 15:02:20 +0100 Subject: [PATCH 021/103] Implemented methods in AddExpense and Expenses such that information flow occurs between the two classes --- .../demo/controller/AddExpenseController.java | 67 +++++---- .../demo/controller/ExpensesController.java | 135 +++++++----------- .../ntnu/idatt1002/demo/view/DialogMode.java | 2 - 3 files changed, 93 insertions(+), 111 deletions(-) delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 23ff3a03..97abaa35 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,20 +1,32 @@ package no.ntnu.idatt1002.demo.controller; +import java.io.IOException; +import java.util.Optional; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.Button; import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; +import javafx.scene.control.Dialog; +import javafx.scene.control.DialogPane; import javafx.scene.control.TextField; +import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.view.ExpenseRepository; public class AddExpenseController { - Expense newExpense = null; @FXML - ButtonType okButton; + private Button cancelBtn; + + @FXML + private Button okBtn; @FXML private TextField dateField; @@ -25,7 +37,6 @@ public class AddExpenseController { @FXML private TextField amountField; - @FXML private ComboBox<ExpenseCategory> categoryBox; @@ -34,23 +45,15 @@ public class AddExpenseController { @FXML public void initialize() { - ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList(ExpenseCategory.values()); + ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList( + ExpenseCategory.values()); categoryBox.setItems(expenseCategories); ObservableList<String> recurring = FXCollections.observableArrayList("Yes", "No"); recurringBox.setItems(recurring); - } - - public String getDate() { - return dateField.getText(); - } - - public String getDescription() { - return descriptionField.getText(); - } - public double getAmount() { - return Double.parseDouble(amountField.getText()); + System.out.print("This is in initialize"); + System.out.println(descriptionField); } public ExpenseCategory getCategory() { @@ -61,14 +64,28 @@ public class AddExpenseController { return recurringBox.getValue().equals("Yes"); } + public void pressOkBtn(ActionEvent event) { + String date = dateField.getText(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + + newExpense = new Expense(description, amount, recurring, category, date); + System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + + final Node source = (Node) event.getSource(); + ((Stage) source.getScene().getWindow()).close(); + } + + public void pressCancelBtn(ActionEvent event) { + final Node source = (Node) event.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + + stage.close(); + } + public Expense getNewExpense() { - try { - newExpense = new Expense(descriptionField.getText(), - Double.parseDouble(amountField.getText()), isRecurring(), categoryBox.getValue(), - dateField.getText()); - } catch (Exception e) { - e.printStackTrace(); - } - return newExpense; + return this.newExpense; } -} +} \ No newline at end of file diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 849450a2..b9f5fab3 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -2,53 +2,52 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; -import java.net.URL; -import java.util.Optional; -import java.util.ResourceBundle; -import javafx.application.Platform; -import javafx.beans.Observable; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; -import javafx.fxml.Initializable; -import javafx.scene.Scene; import javafx.scene.control.Button; -import javafx.scene.control.ButtonBar.ButtonData; -import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; import javafx.stage.Modality; -import javafx.stage.Stage; -import javafx.util.StringConverter; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; -import no.ntnu.idatt1002.demo.data.Economics.Item; -import no.ntnu.idatt1002.demo.view.DialogMode; import no.ntnu.idatt1002.demo.view.ExpenseRepository; +enum DialogMode { + ADD, EDIT, DELETE +} public class ExpensesController { +/* Help me make a application using JavaFX scenebuilder. I trying to make a tableview of expenses. + An expense consists of date, amount and description, which are of type string, double and string respectively. + On the tableview window, there should be an "add"-button, which if clicked brings up a popup window that is a dialog box. + Here, you can type into textfields for date, amount and description. There should also be a OK-button here, that if clickec + closes the popup and adds the new expense to the tableview*/ + + + /** + * The mode of the dialog. NEW if new contact, EDIT if edit existing contact. + */ + private DialogMode mode; @FXML private Button add; @FXML + private TextField descriptionField; + @FXML private Button edit; @FXML private Button delete; @FXML - private ComboBox<?> show; + private ComboBox<String> show; @FXML private TableColumn<Expense, Double> amountColumn; @@ -68,7 +67,8 @@ public class ExpensesController { @FXML private TableView<Expense> expenseTableView; - ObservableList<Expense> expenses = FXCollections.observableArrayList(ExpenseRepository.getExpenses()); + ExpenseRepository expenseRegister = new ExpenseRepository(); + ObservableList<Expense> expenses = FXCollections.observableArrayList(expenseRegister.getExpenses()); @FXML public void initialize() { @@ -79,85 +79,52 @@ public class ExpensesController { recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); expenseTableView.setItems(expenses); + ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); + show.setItems(filter); } @FXML - private void add(ActionEvent event) throws IOException { - - // Load the FXML file for the "Add Expense" dialog box - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); - DialogPane dialogPane = loader.load(); - - AddExpenseController addExpenseController = new AddExpenseController(); -// Create a new Dialog instance with the loaded AnchorPane - Dialog<ButtonType> dialog = new Dialog<>(); - dialog.getDialogPane().setContent(dialogPane); - - dialog.getDialogPane().getButtonTypes().addAll(ButtonType.OK, ButtonType.CANCEL); - - - Optional<ButtonType> result = dialog.showAndWait(); - if (result.isPresent() && result.get() == ButtonType.OK) { - // Get the values from the dialog and create a new Expense object - String date = addExpenseController.getDate(); - String description = addExpenseController.getDescription(); - double amount = addExpenseController.getAmount(); - boolean isRecurring = addExpenseController.isRecurring(); - ExpenseCategory expenseCategory = addExpenseController.getCategory(); - - Expense newExpense = new Expense(description, amount, isRecurring, expenseCategory, date); - - // Add the new expense to the table view - System.out.println(expenses.size()); - expenses.add(newExpense); - System.out.println(expenses.size()); + private void handeEditButton(ActionEvent event) { - expenseTableView.setItems(expenses); - } } + @FXML + private void handleAddButton(ActionEvent event) { + // Load the FXML file for your dialog box + FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/AddExpense.fxml")); + Dialog<Expense> dialog = new Dialog<>(); + dialog.initModality(Modality.APPLICATION_MODAL); - public void edit(ActionEvent event) throws IOException { - /* - Expense expense = null; - String dialogTitle = ""; - DialogMode mode; - - if (event.getSource().equals((edit))) { - mode = DialogMode.EDIT; - dialogTitle = "Edit expense"; - expense = expenseTableView.getSelectionModel().getSelectedItem(); - } else if (event.getSource().equals(add)) { - mode = DialogMode.ADD; - dialogTitle = "Add expense"; - expense = new Expense(0, false, ExpenseCategory.FOOD, "1/1/23"); - } else { - return; + try { + // Set the Dialog's content to the loaded FXML file + dialog.getDialogPane().setContent(loader.load()); + } catch (IOException e) { + e.printStackTrace(); } + // Get the controller for the loaded FXML file + AddExpenseController dialogController = loader.getController(); - try { - FXMLLoader loader = new FXMLLoader(); - loader.setLocation(SceneController.class.getResource("/view/addIncome.fxml")); - DialogPane addExpenseDialog = loader.load(); + // Show the Dialog and wait for the user to close it + dialog.showAndWait(); - ExpenseController expenseController = loader.getController(); - expenseController.setExpense(expense); + //Get the newwly created expense from the dialog pane + Expense newExpense = dialogController.getNewExpense(); - Dialog<ButtonType> dialog = new Dialog<>(); - dialog.setDialogPane(addExpenseDialog); - dialog.setTitle(dialogTitle); + //Only add the expense to the tableview, if the expense is not null + if (newExpense != null) { + expenseTableView.getItems().add(newExpense); + } else { + System.out.println("expense is null"); + } + expenseTableView.refresh(); + } - Optional<ButtonType> clickedButton = dialog.showAndWait(); - if (clickedButton.get() == ButtonType.OK && mode == DialogMode.ADD) { - expenses.add(expense); - } - } catch (IOException e) { - e.printStackTrace(); - }*/ + @FXML + private void handleEditButton(ActionEvent event) throws IOException { + // } - public void delete(ActionEvent event) throws IOException { + public void handleDeleteButton(ActionEvent event) throws IOException { } diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java b/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java deleted file mode 100644 index b457203f..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/view/DialogMode.java +++ /dev/null @@ -1,2 +0,0 @@ -package no.ntnu.idatt1002.demo.view;public class DialogEnum { -} -- GitLab From d4c3eb98cc57d5e6f3e110862fb57a08191f6330 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 15:03:37 +0100 Subject: [PATCH 022/103] Added initialize method and switch scene methods in MainMenuController --- .../demo/controller/MainMenuController.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java index 84a7285c..275f86fb 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java @@ -9,8 +9,12 @@ import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; +import javafx.scene.image.ImageView; import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; +import no.ntnu.idatt1002.demo.view.ExpenseRepository; public class MainMenuController { @@ -28,9 +32,22 @@ public class MainMenuController { @FXML private DatePicker date; + @FXML - public void initialize() { + private ImageView progressMarker; + + @FXML + private Label today; + + ExpenseRepository expenseRepository; + @FXML + public void initialize() { + progressbar.setProgress(0.5); + //progressbar.setProgress((ExpenseRepository.getSum())/5000); + System.out.println(progressbar.getProgress()); + progressMarker.setTranslateX(-275 + progressbar.getProgress()); + today.setTranslateX(-275 + progressbar.getProgress()); } @FXML @@ -40,7 +57,8 @@ public class MainMenuController { Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); stage.setScene(scene); - stage.show(); } + stage.show(); + } @FXML public void switchOverview(ActionEvent event) throws IOException { -- GitLab From 6da86a92ee6cd242222bd5c3fbe68a38b6cf69c8 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 15:05:16 +0100 Subject: [PATCH 023/103] Changed URL in SwitchToMainMenu. Removed method that switches to AddExpense --- .../demo/controller/SceneController.java | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java index 5eb54859..8275ee2c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/SceneController.java @@ -1,10 +1,8 @@ package no.ntnu.idatt1002.demo.controller; -import java.awt.event.InputEvent; import java.io.IOException; import javafx.event.ActionEvent; -import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.Parent; @@ -62,7 +60,7 @@ public class SceneController { } public void switchMainMenu(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/monthly_budget_overview.fxml")); + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/MainMenu.fxml")); Parent root = loader.load(); stage = (Stage)((Node)event.getSource()).getScene().getWindow(); scene = new Scene(root); @@ -70,17 +68,6 @@ public class SceneController { stage.show(); } - public void addExpense(ActionEvent event) throws IOException { - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addExpense.fxml")); - Scene newScene = new Scene(loader.load()); - Stage newStage = new Stage(); - newStage.setScene(newScene); - newStage.setResizable(false); - newStage.initModality(Modality.APPLICATION_MODAL); - - newStage.show(); - } - public void addIncome(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/addIncome.fxml")); Scene newScene = new Scene(loader.load()); -- GitLab From 2e1f11a7971f0ae5385c74c5894b44046d39982f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 15:06:54 +0100 Subject: [PATCH 024/103] Reconfigured pom file for updated javafx version. Redesigned addExpense, Expenses and MainMenu, which is reflected in the fxml files --- pom.xml | 4 ++-- src/main/resources/view/AddExpense.fxml | 13 +++++++++++++ src/main/resources/view/Expenses.fxml | 2 +- src/main/resources/view/MainMenu.fxml | 19 +++++++++++++++---- 4 files changed, 31 insertions(+), 7 deletions(-) diff --git a/pom.xml b/pom.xml index e60349b9..abfdbb4c 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,7 @@ <maven.compiler.target>17</maven.compiler.target> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <junit.version>5.8.1</junit.version> - <javafx.version>17.0.1</javafx.version> + <javafx.version>19.0.2.1</javafx.version> </properties> <repositories> @@ -172,7 +172,7 @@ <!-- Default configuration for running with: mvn clean javafx:run --> <id>default-cli</id> <configuration> - <mainClass>no.ntnu.idatt1002.demo/no.ntnu.idatt1002.demo.view.MyApp</mainClass> + <mainClass>no.ntnu.idatt1002.demo.view.MyApp</mainClass> <launcher>app</launcher> <jlinkZipName>app</jlinkZipName> <jlinkImageName>app</jlinkImageName> diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index a3a60a84..67cb0b9c 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -1,11 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> +<?import javafx.geometry.Insets?> +<?import javafx.scene.control.Button?> <?import javafx.scene.control.ComboBox?> <?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.RowConstraints?> <DialogPane expanded="true" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> @@ -22,6 +25,7 @@ <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> </rowConstraints> <children> <Label text="Date:" /> @@ -34,6 +38,15 @@ <TextField fx:id="descriptionField" promptText="(optional)" GridPane.columnIndex="1" GridPane.rowIndex="2" /> <ComboBox fx:id="categoryBox" prefWidth="150.0" promptText="Food" GridPane.columnIndex="1" GridPane.rowIndex="3" /> <ComboBox fx:id="recurringBox" prefWidth="150.0" promptText="No" GridPane.columnIndex="1" GridPane.rowIndex="4" /> + <HBox alignment="BOTTOM_RIGHT" prefHeight="100.0" prefWidth="200.0" spacing="10.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="5"> + <children> + <Button fx:id="cancelBtn" mnemonicParsing="false" onAction="#pressCancelBtn" prefHeight="25.0" prefWidth="60.0" text="Cancel" /> + <Button fx:id="okBtn" mnemonicParsing="false" onAction="#pressOkBtn" prefHeight="25.0" prefWidth="60.0" text="OK" /> + </children> + <GridPane.margin> + <Insets top="20.0" /> + </GridPane.margin> + </HBox> </children> </GridPane> </content> diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index db56feb2..56e381cd 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#add" text="Add" textAlignment="CENTER"> + <Button fx:id="handleAddButton" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> diff --git a/src/main/resources/view/MainMenu.fxml b/src/main/resources/view/MainMenu.fxml index 586efca7..109698ac 100644 --- a/src/main/resources/view/MainMenu.fxml +++ b/src/main/resources/view/MainMenu.fxml @@ -15,6 +15,7 @@ <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.Region?> <?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.StackPane?> <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> @@ -55,14 +56,24 @@ </HBox> </children> </VBox> - <AnchorPane GridPane.columnSpan="2" GridPane.rowIndex="1"> + <StackPane GridPane.columnSpan="2" GridPane.rowIndex="1"> <children> - <ProgressBar fx:id="progressbar" layoutX="10.0" layoutY="14.0" prefHeight="40.0" prefWidth="554.0" progress="0.72" /> + <ImageView fx:id="progressMarker" fitHeight="20.0" fitWidth="25.0" pickOnBounds="true" preserveRatio="true" rotate="-90.0" translateX="-150.0" translateY="25.0"> + <image> + <Image url="@../Images/arrow.png" /> + </image> + </ImageView> + <ProgressBar fx:id="progressbar" prefHeight="40.0" prefWidth="554.0" progress="0.72" translateY="-10.0" /> + <Label fx:id="today" text="Today" textAlignment="CENTER" translateX="-150.0" translateY="-2.0"> + <font> + <Font name="System Bold" size="12.0" /> + </font> + </Label> </children> - </AnchorPane> + </StackPane> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button fx:id="foodButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="125.0" text="Food"> + <Button fx:id="foodButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="119.0" text="Food"> <graphic> <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> <cursor> -- GitLab From ca1ee88ec5c36faa5bd0be27dc62d263d2f54733 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 15:07:21 +0100 Subject: [PATCH 025/103] Made expense arraylist instantiate in constructor --- .../idatt1002/demo/view/ExpenseRepository.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java index 0cd752af..a6b91cda 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java +++ b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java @@ -1,5 +1,6 @@ package no.ntnu.idatt1002.demo.view; +import java.lang.reflect.Array; import java.util.ArrayList; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; @@ -7,17 +8,24 @@ import no.ntnu.idatt1002.demo.data.Economics.Item; import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; public class ExpenseRepository { - public static ArrayList<Expense> getExpenses () { - ArrayList<Expense> expenses = new ArrayList<>(); + + private final ArrayList<Expense> expenses; + + public ExpenseRepository() { + this.expenses = new ArrayList<>(); expenses.add(new Expense("free", 100, true, ExpenseCategory.FOOD, "1/1/23")); expenses.add(new Expense(149, true, ExpenseCategory.FOOD, "1/1/23")); expenses.add(new Expense("Not free", 1000, true, ExpenseCategory.CLOTHES, "2/1/23")); expenses.add(new Expense(200, true, ExpenseCategory.BOOKS, "3/1/23")); - + } + public ArrayList<Expense> getExpenses () { return expenses; } - public static double getSum() { + public void addExpense(Expense expense) { + expenses.add(expense); + } + public double getSum() { System.out.println(getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum)); return getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum); } -- GitLab From 7891e1f8b168f738ab7060482ee2964ff02b78a5 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 16:45:21 +0100 Subject: [PATCH 026/103] Made handelAddBtn and handleEditBtn essentially be the same method, and differentitated them with enums --- .../demo/controller/ExpensesController.java | 40 +++++++++++++------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index b9f5fab3..1e4401da 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -38,6 +38,7 @@ public class ExpensesController { @FXML private Button add; + @FXML private TextField descriptionField; @FXML @@ -77,40 +78,58 @@ public class ExpensesController { categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - expenseTableView.setItems(expenses); + ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); show.setItems(filter); } @FXML - private void handeEditButton(ActionEvent event) { - + public void handleAddButton(ActionEvent event) { + handleEditButton(event); } @FXML - private void handleAddButton(ActionEvent event) { + private void handleEditButton(ActionEvent event) { + Expense newExpense = null; + String dialogTitle = ""; // Load the FXML file for your dialog box FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/AddExpense.fxml")); Dialog<Expense> dialog = new Dialog<>(); dialog.initModality(Modality.APPLICATION_MODAL); + if (event.getSource().equals(add)) { + mode = DialogMode.ADD; + dialogTitle = "Add expense"; + newExpense = new Expense(1.1, false, ExpenseCategory.FOOD, "1/1/23"); + + } else if (event.getSource().equals(edit)) { + mode = DialogMode.EDIT; + dialogTitle = "Edit expense"; + newExpense = expenseTableView.getSelectionModel().getSelectedItem(); + } else { + return; + } + try { // Set the Dialog's content to the loaded FXML file dialog.getDialogPane().setContent(loader.load()); + dialog.setTitle(dialogTitle); } catch (IOException e) { e.printStackTrace(); } - // Get the controller for the loaded FXML file + AddExpenseController dialogController = loader.getController(); + dialogController.setExpense(newExpense); + // Get the controller for the loaded FXML file // Show the Dialog and wait for the user to close it dialog.showAndWait(); - //Get the newwly created expense from the dialog pane - Expense newExpense = dialogController.getNewExpense(); + //Get the newly created expense from the dialog pane + newExpense = dialogController.getNewExpense(); //Only add the expense to the tableview, if the expense is not null - if (newExpense != null) { + if (newExpense != null && mode == DialogMode.ADD) { expenseTableView.getItems().add(newExpense); } else { System.out.println("expense is null"); @@ -119,11 +138,6 @@ public class ExpensesController { } - @FXML - private void handleEditButton(ActionEvent event) throws IOException { - // - } - public void handleDeleteButton(ActionEvent event) throws IOException { } -- GitLab From c4dc07fed30b5ced45ff50614d33a5a5ff2ac5ce Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 16:46:10 +0100 Subject: [PATCH 027/103] Added method that binds the textfields with an incoming expense --- .../demo/controller/AddExpenseController.java | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 97abaa35..13445f97 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,7 +1,12 @@ package no.ntnu.idatt1002.demo.controller; import java.io.IOException; +import java.text.NumberFormat; import java.util.Optional; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -64,15 +69,27 @@ public class AddExpenseController { return recurringBox.getValue().equals("Yes"); } + public void setExpense(Expense expense) { + this.newExpense = expense; + StringProperty dateProperty = new SimpleStringProperty(expense.getDate()); + DoubleProperty amountProperty = new SimpleDoubleProperty(expense.getAmount()); + StringProperty descriptionProperty = new SimpleStringProperty(expense.getDescription()); + + dateField.textProperty().bindBidirectional(dateProperty); + amountField.textProperty().bindBidirectional(amountProperty, NumberFormat.getNumberInstance()); + descriptionField.textProperty().bindBidirectional(descriptionProperty); + } public void pressOkBtn(ActionEvent event) { - String date = dateField.getText(); - double amount = Double.parseDouble(amountField.getText()); - String description = descriptionField.getText(); - ExpenseCategory category = getCategory(); - boolean recurring = isRecurring(); - - newExpense = new Expense(description, amount, recurring, category, date); - System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + if (this.newExpense.getAmount() == 1.1) { + String date = dateField.getText(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + + newExpense = new Expense(description, amount, recurring, category, date); + System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + } final Node source = (Node) event.getSource(); ((Stage) source.getScene().getWindow()).close(); -- GitLab From 40e61b5dd89cf8d97b5d2c7cae3ce7a55b829e62 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 16:47:03 +0100 Subject: [PATCH 028/103] Changed addButton id to add and assigned onAction to edit --- src/main/resources/view/Expenses.fxml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 56e381cd..107ddd16 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="handleAddButton" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> + <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -81,7 +81,7 @@ </image> </ImageView> </graphic></Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> + <Button fx:id="edit" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> -- GitLab From 9b848555330a5a3e7e731d4ed8ce98d1df31b0bd Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 17:16:24 +0100 Subject: [PATCH 029/103] Made amount attribute to DoubleProperty. Binded amount textfield in AddExpenseController to the expense amount property --- .../demo/controller/AddExpenseController.java | 3 +- .../demo/controller/ExpensesController.java | 31 +++++++++---------- .../idatt1002/demo/data/Economics/Item.java | 16 +++++++--- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 13445f97..22a9c78b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -72,11 +72,10 @@ public class AddExpenseController { public void setExpense(Expense expense) { this.newExpense = expense; StringProperty dateProperty = new SimpleStringProperty(expense.getDate()); - DoubleProperty amountProperty = new SimpleDoubleProperty(expense.getAmount()); StringProperty descriptionProperty = new SimpleStringProperty(expense.getDescription()); dateField.textProperty().bindBidirectional(dateProperty); - amountField.textProperty().bindBidirectional(amountProperty, NumberFormat.getNumberInstance()); + amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); descriptionField.textProperty().bindBidirectional(descriptionProperty); } public void pressOkBtn(ActionEvent event) { diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 1e4401da..488cab69 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -97,34 +97,33 @@ public class ExpensesController { Dialog<Expense> dialog = new Dialog<>(); dialog.initModality(Modality.APPLICATION_MODAL); + try { + // Set the Dialog's content to the loaded FXML file + dialog.getDialogPane().setContent(loader.load()); + } catch (IOException e) { + e.printStackTrace(); + } + + // Get the controller for the loaded FXML file + AddExpenseController dialogController = loader.getController(); + if (event.getSource().equals(add)) { mode = DialogMode.ADD; dialogTitle = "Add expense"; - newExpense = new Expense(1.1, false, ExpenseCategory.FOOD, "1/1/23"); - } else if (event.getSource().equals(edit)) { + } else if (event.getSource().equals(edit) && expenseTableView.getSelectionModel().getSelectedItem() != null) { mode = DialogMode.EDIT; dialogTitle = "Edit expense"; newExpense = expenseTableView.getSelectionModel().getSelectedItem(); + dialogController.setExpense(newExpense); } else { return; } - try { - // Set the Dialog's content to the loaded FXML file - dialog.getDialogPane().setContent(loader.load()); - dialog.setTitle(dialogTitle); - } catch (IOException e) { - e.printStackTrace(); - } - - AddExpenseController dialogController = loader.getController(); - dialogController.setExpense(newExpense); - // Get the controller for the loaded FXML file - + dialog.setTitle(dialogTitle); // Show the Dialog and wait for the user to close it - dialog.showAndWait(); + dialog.showAndWait(); //Get the newly created expense from the dialog pane newExpense = dialogController.getNewExpense(); @@ -132,7 +131,7 @@ public class ExpensesController { if (newExpense != null && mode == DialogMode.ADD) { expenseTableView.getItems().add(newExpense); } else { - System.out.println("expense is null"); + System.out.println("expense is null or dialog mode is add"); } expenseTableView.refresh(); } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 04b6bc51..cec3342c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -1,6 +1,8 @@ package no.ntnu.idatt1002.demo.data.Economics; import java.util.Objects; +import javafx.beans.property.DoubleProperty; +import javafx.beans.property.SimpleDoubleProperty; /** * The Item class represents a good or service purchased in real life. The item belongs to a category and @@ -11,7 +13,7 @@ import java.util.Objects; */ public abstract class Item { private String description = ""; - private double amount; + private DoubleProperty amount; private boolean recurring; private String date; // Format example: 09.08.23 @@ -24,7 +26,7 @@ public abstract class Item { if(amount <= 1.0f || date.isBlank()) { throw new IllegalArgumentException("Both amount and date must be provided."); } else { - this.amount = amount; + this.amount = new SimpleDoubleProperty(amount); this.recurring = recurring; this.date = date; } @@ -63,7 +65,11 @@ public abstract class Item { * @return The amount of the Item as a float. */ public double getAmount() { - return amount; + return amount.get(); + } + + public DoubleProperty amountProperty() { + return this.amount; } /** @@ -74,7 +80,7 @@ public abstract class Item { if(amount <= 1.0f ) { throw new IllegalArgumentException("A positive amount must be provided"); } - this.amount = amount; + this.amount.set(amount); } /** @@ -116,7 +122,7 @@ public abstract class Item { public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Item item)) return false; - return Double.compare(item.amount, amount) == 0 && recurring == item.recurring && Objects.equals(description, item.description) && Objects.equals(date, item.date); + return Double.compare(item.amount.get(), amount.get()) == 0 && recurring == item.recurring && Objects.equals(description, item.description) && Objects.equals(date, item.date); } @Override -- GitLab From af2ae092978f189e4bf9f3aa72e23cf834887eb3 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 17:20:28 +0100 Subject: [PATCH 030/103] Deleted uneccessary test text files --- src/main/resources/expenseRegisterTest.itemRegister | 6 ------ src/main/resources/incomeRegisterTest.itemRegister | 6 ------ 2 files changed, 12 deletions(-) delete mode 100644 src/main/resources/expenseRegisterTest.itemRegister delete mode 100644 src/main/resources/incomeRegisterTest.itemRegister diff --git a/src/main/resources/expenseRegisterTest.itemRegister b/src/main/resources/expenseRegisterTest.itemRegister deleted file mode 100644 index 7f98526b..00000000 --- a/src/main/resources/expenseRegisterTest.itemRegister +++ /dev/null @@ -1,6 +0,0 @@ - -date=03.03.23 -description=description -amount=59.900001525878906 -isReoccuring=Not reoccurring -category=CLOTHES diff --git a/src/main/resources/incomeRegisterTest.itemRegister b/src/main/resources/incomeRegisterTest.itemRegister deleted file mode 100644 index 6bfe0943..00000000 --- a/src/main/resources/incomeRegisterTest.itemRegister +++ /dev/null @@ -1,6 +0,0 @@ - -date=03.03.23 -description=description -amount=59.900001525878906 -isReoccuring=Not reoccurring -category=GIFT -- GitLab From bf6ff18d2b2e05665a56977c0458137ea0b53396 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 17:21:02 +0100 Subject: [PATCH 031/103] Removed incorrect if-statement that prevented items from getting added to table --- .../demo/controller/AddExpenseController.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 22a9c78b..93956647 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -79,16 +79,13 @@ public class AddExpenseController { descriptionField.textProperty().bindBidirectional(descriptionProperty); } public void pressOkBtn(ActionEvent event) { - if (this.newExpense.getAmount() == 1.1) { - String date = dateField.getText(); - double amount = Double.parseDouble(amountField.getText()); - String description = descriptionField.getText(); - ExpenseCategory category = getCategory(); - boolean recurring = isRecurring(); - - newExpense = new Expense(description, amount, recurring, category, date); - System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); - } + String date = dateField.getText(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + newExpense = new Expense(description, amount, recurring, category, date); + System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); final Node source = (Node) event.getSource(); ((Stage) source.getScene().getWindow()).close(); -- GitLab From 14060e31106bd1dc8ffc741dfc075bb53a42d0ee Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 17:39:06 +0100 Subject: [PATCH 032/103] Made the show combox provide options to choose from - All, food or other --- .../ntnu/idatt1002/demo/controller/ExpensesController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 488cab69..b6881e9c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -73,6 +73,10 @@ public class ExpensesController { @FXML public void initialize() { + ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); + show.setItems(filter); + show.setValue("All"); + dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); @@ -80,8 +84,7 @@ public class ExpensesController { recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); expenseTableView.setItems(expenses); - ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); - show.setItems(filter); + } @FXML -- GitLab From 2416e52e15d025b407717f12154cd8680f227866 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Tue, 21 Mar 2023 17:39:28 +0100 Subject: [PATCH 033/103] Converted description from type String to StringProperty --- .../idatt1002/demo/data/Economics/Item.java | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index cec3342c..ae918ec8 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -3,6 +3,8 @@ package no.ntnu.idatt1002.demo.data.Economics; import java.util.Objects; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; /** * The Item class represents a good or service purchased in real life. The item belongs to a category and @@ -12,7 +14,7 @@ import javafx.beans.property.SimpleDoubleProperty; * */ public abstract class Item { - private String description = ""; + private StringProperty description = new SimpleStringProperty(""); private DoubleProperty amount; private boolean recurring; private String date; // Format example: 09.08.23 @@ -41,23 +43,31 @@ public abstract class Item { */ public Item (String description, double amount, boolean recurring, String date){ this(amount, recurring, date); - this.description=description; + this.description = new SimpleStringProperty(description); } + public StringProperty descriptionProperty() { + return this.description; + } /** * The method returns the description of the given Item object as a String. * @return The description of the Item as a String. */ public String getDescription() { - return description; + return description.get(); } + /** * The method sets the description of the Item object equal to the passed String. The String may be empty. * @param description The new description of the Item object as a String. */ public void setDescription(String description) { - this.description = description; + this.description.set(description); + } + + public DoubleProperty amountProperty() { + return this.amount; } /** @@ -68,10 +78,6 @@ public abstract class Item { return amount.get(); } - public DoubleProperty amountProperty() { - return this.amount; - } - /** * The method changes the price of the given Item to the passed price as a float. * @param amount The new price of the Item as a float. @@ -122,7 +128,7 @@ public abstract class Item { public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Item item)) return false; - return Double.compare(item.amount.get(), amount.get()) == 0 && recurring == item.recurring && Objects.equals(description, item.description) && Objects.equals(date, item.date); + return Double.compare(item.amount.get(), amount.get()) == 0 && recurring == item.recurring && Objects.equals(description.get(), item.description.get()) && Objects.equals(date, item.date); } @Override @@ -143,11 +149,11 @@ public abstract class Item { else{ isReoccuring = "Not reoccurring"; } - if(!description.isBlank()){ - return "\ndate=" + date+"\ndescription=" + description+"\namount="+amount+"\nisReoccuring="+isReoccuring; + if(!description.get().isBlank()){ + return "\ndate=" + date+"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; } else{ - return "\ndate="+date+"\namount="+amount+"\nisReoccuring="+isReoccuring; + return "\ndate="+date+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; } } -- GitLab From e79efd73e8c0c9335ca54df101649c2eeee06683 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Wed, 22 Mar 2023 10:55:52 +0100 Subject: [PATCH 034/103] Item now use Property for all variables --- .../idatt1002/demo/data/Economics/Item.java | 34 +++++++++---------- 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index ae918ec8..00ffad02 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -1,10 +1,8 @@ package no.ntnu.idatt1002.demo.data.Economics; import java.util.Objects; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; + +import javafx.beans.property.*; /** * The Item class represents a good or service purchased in real life. The item belongs to a category and @@ -15,9 +13,9 @@ import javafx.beans.property.StringProperty; */ public abstract class Item { private StringProperty description = new SimpleStringProperty(""); - private DoubleProperty amount; - private boolean recurring; - private String date; // Format example: 09.08.23 + private final DoubleProperty amount; + private BooleanProperty recurring; + private StringProperty date; // Format example: 09.08.23 /** * The constructor of a new Item object takes in an amount as a double. If the amount is left blank, an @@ -29,8 +27,8 @@ public abstract class Item { throw new IllegalArgumentException("Both amount and date must be provided."); } else { this.amount = new SimpleDoubleProperty(amount); - this.recurring = recurring; - this.date = date; + this.recurring = new SimpleBooleanProperty(recurring); + this.date = new SimpleStringProperty(date); } } @@ -94,7 +92,7 @@ public abstract class Item { * @return True if the transaction is a recurring one, false otherwise. */ public boolean isRecurring() { - return recurring; + return recurring.get(); } /** @@ -102,7 +100,7 @@ public abstract class Item { * @param recurring A boolean value being true if the Item is recurring and false otherwise. */ public void setRecurring(boolean recurring) { - this.recurring = recurring; + this.recurring.set(recurring); } /** @@ -110,7 +108,7 @@ public abstract class Item { * @return The date of the transaction. */ public String getDate() { - return date; + return date.get(); } /** @@ -118,17 +116,17 @@ public abstract class Item { * @param newDate The new date with which to record the Item. */ public void setDate(String newDate) { - if(date.isBlank()) { + if(date.get().isBlank()) { throw new IllegalArgumentException("A date must be provided."); } - this.date = newDate; + this.date.set(newDate); } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Item item)) return false; - return Double.compare(item.amount.get(), amount.get()) == 0 && recurring == item.recurring && Objects.equals(description.get(), item.description.get()) && Objects.equals(date, item.date); + return Double.compare(item.amount.get(), amount.get()) == 0 && item.recurring.get() == recurring.get() && Objects.equals(description.get(), item.description.get()) && Objects.equals(date.get(), item.date.get()); } @Override @@ -143,17 +141,17 @@ public abstract class Item { @Override public String toString() { String isReoccuring = ""; - if(recurring){ + if(recurring.get()){ isReoccuring = "Reoccurring"; } else{ isReoccuring = "Not reoccurring"; } if(!description.get().isBlank()){ - return "\ndate=" + date+"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; + return "\ndate=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; } else{ - return "\ndate="+date+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; + return "\ndate="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; } } -- GitLab From 79fc46bcff25bf8f2fce19962cb7fbe6586d6f11 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 11:05:59 +0100 Subject: [PATCH 035/103] Added getters for dateProperty and reoccurringProperty --- .../java/no/ntnu/idatt1002/demo/data/Economics/Item.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 00ffad02..a3d8fee1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -87,6 +87,9 @@ public abstract class Item { this.amount.set(amount); } + public BooleanProperty recurringProperty() { + return this.recurring; + } /** * The method returns true if the transaction is recurring, false otherwise. * @return True if the transaction is a recurring one, false otherwise. @@ -103,6 +106,9 @@ public abstract class Item { this.recurring.set(recurring); } + public StringProperty dateProperty() { + return this.date; + } /** * The method returns the date of the item object (income/expense). * @return The date of the transaction. -- GitLab From 97ae3c3d14a5496cf05f153f7a99515030d72e9e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 11:08:18 +0100 Subject: [PATCH 036/103] Binded date and descriptionfields in dialog box with the chosen expense values --- .../idatt1002/demo/controller/AddExpenseController.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 93956647..2d18fcc8 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -71,12 +71,10 @@ public class AddExpenseController { public void setExpense(Expense expense) { this.newExpense = expense; - StringProperty dateProperty = new SimpleStringProperty(expense.getDate()); - StringProperty descriptionProperty = new SimpleStringProperty(expense.getDescription()); - dateField.textProperty().bindBidirectional(dateProperty); + dateField.textProperty().bindBidirectional(expense.dateProperty()); amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); - descriptionField.textProperty().bindBidirectional(descriptionProperty); + descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); } public void pressOkBtn(ActionEvent event) { String date = dateField.getText(); -- GitLab From 9cd3cd982a327260b11485bedcd2d3e47b67a4b6 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Wed, 22 Mar 2023 11:13:05 +0100 Subject: [PATCH 037/103] Made equals and hash method --- .../idatt1002/demo/data/Economics/Expense.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java index 5066f6b0..698f1ff0 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java @@ -1,5 +1,7 @@ package no.ntnu.idatt1002.demo.data.Economics; +import java.util.Objects; + public class Expense extends Item{ private ExpenseCategory category; @@ -68,4 +70,17 @@ public class Expense extends Item{ public String toString() { return super.toString()+"\ncategory="+category.toString()+"\n"; } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Expense expense)) return false; + if (!super.equals(o)) return false; + return category == expense.category; + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), category); + } } -- GitLab From 12fc795c557ce2828f47ee067cd9ee3e47ea1e57 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Wed, 22 Mar 2023 11:13:32 +0100 Subject: [PATCH 038/103] Made method for getting an item from register --- .../ntnu/idatt1002/demo/data/Economics/ItemRegister.java | 8 ++++++++ .../demo/data/Economics/ExpenseRegisterTest.java | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index 80b7ad88..e6a38a5d 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -36,6 +36,14 @@ public abstract class ItemRegister<T extends Item>{ return items; } + public T getItem(T item){ + for(T anItem : items){ + if(anItem.equals(item)){ + return anItem; + } + } throw new IllegalArgumentException("Item not in register"); + } + /** * Add a new T=(Income or Expense) to the register. Throws IllegalArgumentException * if the new T=(Income or Expense) is already registered. diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java index 1d6d108b..46ffdb31 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java @@ -29,6 +29,14 @@ class ExpenseRegisterTest { assertDoesNotThrow(() -> expenseRegister.addItem(expense2)); } + @Test + @DisplayName("getItem does not throw exception when it should not") + void getItemDoesNotThrow(){ + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + expenseRegister.addItem(expense1); + assertDoesNotThrow(() -> expenseRegister.getItem(new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"))); + } + @Nested @DisplayName("Test getTotalSum and getExpenseByCategory methods") class testGetExpenseByCategoryMethod { -- GitLab From c6da72e0e6a3a3e0a0867af2c8bc2197553dea92 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Mon, 20 Mar 2023 14:05:44 +0100 Subject: [PATCH 039/103] Rewrote toString method in Income, Expense and Item --- .../java/no/ntnu/idatt1002/demo/data/Economics/Expense.java | 2 +- src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java index 698f1ff0..a658928d 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java @@ -68,7 +68,7 @@ public class Expense extends Item{ */ @Override public String toString() { - return super.toString()+"\ncategory="+category.toString()+"\n"; + return super.toString()+"category="+category.toString()+"\n\n"; } @Override diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java index 480636b0..c07821dc 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java @@ -73,6 +73,6 @@ public class Income extends Item{ */ @Override public String toString() { - return super.toString()+"\ncategory="+category.toString()+"\n"; + return super.toString()+"category="+category.toString()+"\n\n"; } } -- GitLab From 755245636e5899334490d733c4e06f8c79d214c1 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Mon, 20 Mar 2023 14:09:22 +0100 Subject: [PATCH 040/103] Made method removeItem in ItemRegister and tests --- .../demo/data/Economics/FileHandling.java | 76 ++++++++++++------- .../demo/data/Economics/ItemRegister.java | 25 ++++-- .../data/Economics/ExpenseRegisterTest.java | 54 +++++++++---- .../demo/data/Economics/FileHandlingTest.java | 1 - .../data/Economics/IncomeRegisterTest.java | 57 ++++++++++---- 5 files changed, 147 insertions(+), 66 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java index 390906b5..bfbe5ab7 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java @@ -10,7 +10,15 @@ import java.io.*; * FileHandling is a class for writing and reading * Item-objects to and from a file. */ -public class FileHandling { +public class FileHandling{ + private static final String filePath = "src/main/resources/Economics/"; + private static final String fileType = ".register"; + private static final String date = "date="; + private static final String description = "description="; + private static final String amount = "amount="; + private static final String isReoccuring = "isReoccuring="; + private static final String category = "category="; + /** * Method for writing (adding) an ItemRegister to a file. * @@ -18,36 +26,42 @@ public class FileHandling { * @throws IOException if an input or output exception occurred. */ public <T extends Item>void writeItemRegisterToFile(final ItemRegister<T> itemRegister, String fileTitle) throws IOException { - try (BufferedWriter bw = new BufferedWriter(new FileWriter("src/main/resources/" + fileTitle + ".itemRegister"))) { + try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath + fileTitle + fileType))) { bw.write(itemRegister.toString()); } catch (IOException ex) { throw new IOException("Error writing story to file: " + ex.getMessage()); } } - public ItemRegister<Income> readIncomeRegisterFromFile(String fileTitle) throws IOException { + /** + * Method for reading (getting) an IncomeRegister from a file. + * + * @param fileTitle the name of the file you want to read from + * @return the IncomeRegister from the file. + * @throws IOException if an input or output exception occurred + */ + public IncomeRegister readIncomeRegisterFromFile(String fileTitle) throws IOException { IncomeRegister incomeRegister = new IncomeRegister(); String date = ""; String description = ""; double amount = 0; boolean reoccuring = false; IncomeCategory incomeCategory = null; - try (BufferedReader br = new BufferedReader(new FileReader("src/main/resources/" + fileTitle + ".itemRegister"))) { - br.readLine(); + try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { String line; String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); - if(line.startsWith("date=")) { - date = line.replace("date=", ""); - } else if (line.startsWith("description=")) { - description = line.replace("description=",""); - } else if (line.startsWith("amount=")) { - amount = Double.parseDouble(line.replace("amount=","")); - } else if (line.startsWith("isReoccuring=")) { - reoccuring = line.replace("isReoccuring=","").equals("Reoccurring"); - } else if (line.startsWith("category=")) { - line = line.replace("category=",""); + if(line.startsWith(FileHandling.date)) { + date = line.replace(FileHandling.date, ""); + } else if (line.startsWith(FileHandling.description)) { + description = line.replace(FileHandling.description,""); + } else if (line.startsWith(FileHandling.amount)) { + amount = Double.parseDouble(line.replace(FileHandling.amount,"")); + } else if (line.startsWith(FileHandling.isReoccuring)) { + reoccuring = line.replace(FileHandling.isReoccuring,"").equals("Reoccurring"); + } else if (line.startsWith(FileHandling.category)) { + line = line.replace(FileHandling.category,""); incomeCategory = switch (line) { case "GIFT" -> IncomeCategory.GIFT; case "STUDENT_LOAN" -> IncomeCategory.STUDENT_LOAN; @@ -67,29 +81,35 @@ public class FileHandling { return incomeRegister; } - public ItemRegister<Expense> readExpenseRegisterFromFile(String fileTitle) throws IOException { + /** + * Method for reading (getting) an ExpenseRegister from a file. + * + * @param fileTitle the name of the file you want to read from. + * @return the ExpenseRegister from the file. + * @throws IOException if an input or output exception occurred + */ + public ExpenseRegister readExpenseRegisterFromFile(String fileTitle) throws IOException { ExpenseRegister expenseRegister = new ExpenseRegister(); String date = ""; String description = ""; double amount = 0; boolean reoccuring = false; ExpenseCategory expenseCategory = null; - try (BufferedReader br = new BufferedReader(new FileReader("src/main/resources/" + fileTitle + ".itemRegister"))) { - br.readLine(); + try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { String line; String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); - if (line.startsWith("date=")) { - date = line.replace("date=", ""); - } else if (line.startsWith("description=")) { - description = line.replace("description=", ""); - } else if (line.startsWith("amount=")) { - amount = Double.parseDouble(line.replace("amount=", "")); - } else if (line.startsWith("isReoccuring=")) { - reoccuring = line.replace("isReoccuring=", "").equals("Reoccurring"); - } else if (line.startsWith("category=")) { - line = line.replace("category=", ""); + if (line.startsWith(FileHandling.date)) { + date = line.replace(FileHandling.date, ""); + } else if (line.startsWith(FileHandling.description)) { + description = line.replace(FileHandling.description, ""); + } else if (line.startsWith(FileHandling.amount)) { + amount = Double.parseDouble(line.replace(FileHandling.amount, "")); + } else if (line.startsWith(FileHandling.isReoccuring)) { + reoccuring = line.replace(FileHandling.isReoccuring, "").equals("Reoccurring"); + } else if (line.startsWith(FileHandling.category)) { + line = line.replace(FileHandling.category, ""); expenseCategory = switch (line) { case "FOOD" -> ExpenseCategory.FOOD; case "CLOTHES" -> ExpenseCategory.CLOTHES; diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index e6a38a5d..583b7058 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -13,7 +13,7 @@ public abstract class ItemRegister<T extends Item>{ List<T> items; /** - * Class constructor that creates an empty List for storing T=(Income or Expense). + * Class constructor that creates an empty List for storing item´s. */ public ItemRegister() { this.items = new ArrayList<>(); @@ -29,8 +29,8 @@ public abstract class ItemRegister<T extends Item>{ } /** - * Get a List of every T=(Income or Expense). - * @return T=(Income or Expense) List. + * Get a List of every item. + * @return item List. */ public List<T> getItems() { return items; @@ -45,11 +45,10 @@ public abstract class ItemRegister<T extends Item>{ } /** - * Add a new T=(Income or Expense) to the register. Throws IllegalArgumentException - * if the new T=(Income or Expense) is already registered. + * Add a new item to the register. * - * @param newItem the T=(Income or Expense) you want to add. - * @throws IllegalArgumentException if the item is already in the register. + * @param newItem the item you want to add. + * @throws IllegalArgumentException if newItem is already in the register. */ public void addItem(T newItem) throws IllegalArgumentException{ if(items.contains(newItem)){ @@ -58,6 +57,18 @@ public abstract class ItemRegister<T extends Item>{ items.add(newItem); } + /** + * Remove an item from the register. + * + * @param itemWantRemoved the item you want to remove. + * @throws IllegalArgumentException if the itemWantRemoved is not in the register. + */ + public void removeItem(T itemWantRemoved) throws IllegalArgumentException{ + if(!items.remove(itemWantRemoved)){ + throw new IllegalArgumentException("The item is not in the register"); + } + } + /** * Check if items is empty. * diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java index 46ffdb31..cf9f6ba0 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java @@ -12,21 +12,47 @@ import static org.junit.jupiter.api.Assertions.assertThrows; class ExpenseRegisterTest { ExpenseRegister expenseRegister = new ExpenseRegister(); - @Test - @DisplayName("addItem method throws exception when it should") - void addItemThrows() { - Expense expense = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - expenseRegister.addItem(expense); - assertThrows(IllegalArgumentException.class, () -> expenseRegister.addItem(expense)); + @Nested + @DisplayName("Test addItem") + class testAddItem { + @Test + @DisplayName("addItem method throws exception when it should") + void addItemThrows() { + Expense expense = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + expenseRegister.addItem(expense); + assertThrows(IllegalArgumentException.class, () -> expenseRegister.addItem(expense)); + } + + @Test + @DisplayName("addItem method does not throw exception when it should not") + void addItemDoesNotThrow() { + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense2 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, "02.03.23"); + assertDoesNotThrow(() -> expenseRegister.addItem(expense1)); + assertDoesNotThrow(() -> expenseRegister.addItem(expense2)); + } } - @Test - @DisplayName("addItem method does not throw exception when it should not") - void addItemDoesNotThrow() { - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - Expense expense2 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, "02.03.23"); - assertDoesNotThrow(() -> expenseRegister.addItem(expense1)); - assertDoesNotThrow(() -> expenseRegister.addItem(expense2)); + @Nested + @DisplayName("Test removeItem") + class testRemoveItem{ + @Test + @DisplayName("removeItem does throw exception when it should") + void removeItemDoesThrow(){ + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense notExpense1 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, "02.03.23"); + expenseRegister.addItem(expense1); + assertThrows(IllegalArgumentException.class, () -> expenseRegister.removeItem(notExpense1)); + } + @Test + @DisplayName("removeItem method does not throw exception when it should not") + void removeItemDoesNotThrow(){ + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense1Copy = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + expenseRegister.addItem(expense1); + assertDoesNotThrow(() -> expenseRegister.removeItem(expense1Copy)); + } + } @Test @@ -38,7 +64,7 @@ class ExpenseRegisterTest { } @Nested - @DisplayName("Test getTotalSum and getExpenseByCategory methods") + @DisplayName("Test getTotalSum and getExpenseByCategory") class testGetExpenseByCategoryMethod { Expense expense1; Expense expense2; diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java index 6c6289c7..aaa5097a 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java @@ -2,7 +2,6 @@ package no.ntnu.idatt1002.demo.data.Economics; import org.junit.jupiter.api.*; -import java.io.FileNotFoundException; import java.io.IOException; import static org.junit.jupiter.api.Assertions.*; diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java index 1c4c173d..64d18f51 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java @@ -11,27 +11,52 @@ import static org.junit.jupiter.api.Assertions.*; public class IncomeRegisterTest { IncomeRegister incomeRegister = new IncomeRegister(); - @Test - @DisplayName("addItem method throws exception when it should") - void addItemThrows() { - Income income = new Income("description", 59.9f, false, IncomeCategory.SALARY, "03.03.23"); - incomeRegister.addItem(income); - assertThrows(IllegalArgumentException.class, () -> incomeRegister.addItem(income)); - } - @Test - @DisplayName("addItem method does not throw exception when it should not") - void addItemDoesNotThrow() { - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); - Income income2 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, "02.03.23"); - assertDoesNotThrow(() -> incomeRegister.addItem(income1)); - assertDoesNotThrow(() -> incomeRegister.addItem(income2)); + @Nested + @DisplayName("Test addItem") + class testAddItem{ + @Test + @DisplayName("addItem method throws exception when it should") + void addItemThrows() { + Income income = new Income("description", 59.9f, false, IncomeCategory.SALARY, "03.03.23"); + incomeRegister.addItem(income); + assertThrows(IllegalArgumentException.class, () -> incomeRegister.addItem(income)); + } + + @Test + @DisplayName("addItem method does not throw exception when it should not") + void addItemDoesNotThrow() { + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income2 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, "02.03.23"); + assertDoesNotThrow(() -> incomeRegister.addItem(income1)); + assertDoesNotThrow(() -> incomeRegister.addItem(income2)); + } } @Nested - @DisplayName("test getTotalSum and getIncomeByCategory methods") - class testGetTotalSumAndGetIncomeByCategoryMethods { + @DisplayName("Test removeItem") + class testRemoveItem{ + @Test + @DisplayName("removeItem does throw exception when it should") + void removeItemDoesThrow(){ + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income notIncome1 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, "02.03.23"); + incomeRegister.addItem(income1); + assertThrows(IllegalArgumentException.class, () -> incomeRegister.removeItem(notIncome1)); + } + @Test + @DisplayName("removeItem method does not throw exception when it should not") + void removeItemDoesNotThrow(){ + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income1Copy = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + incomeRegister.addItem(income1); + assertDoesNotThrow(() -> incomeRegister.removeItem(income1Copy)); + } + } + @Nested + @DisplayName("Test getTotalSum and getIncomeByCategory") + class testGetTotalSumAndGetIncomeByCategoryMethods { Income income1; Income income2; Income income3; -- GitLab From ef72ac55faa9a2efee8432099a9e9f9bee06fbe2 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Tue, 21 Mar 2023 17:22:35 +0100 Subject: [PATCH 041/103] Rewrote javadoc --- .../no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java | 1 + .../java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java | 1 + 2 files changed, 2 insertions(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java index fa1dfbc8..1a9c4a12 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java @@ -27,6 +27,7 @@ public class IncomeRegister extends ItemRegister<Income>{ /** * Method for getting every Income * in a given IncomeCategory. + * * @param category the IncomeCategory you want to get every Income of. * @return a List of every Income with category. */ diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index 583b7058..24843428 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -30,6 +30,7 @@ public abstract class ItemRegister<T extends Item>{ /** * Get a List of every item. + * * @return item List. */ public List<T> getItems() { -- GitLab From e66cb4b4f4f4ea6fa50f24389ef1e37d0b9c6f8b Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Tue, 21 Mar 2023 17:30:40 +0100 Subject: [PATCH 042/103] Added test textfiles for Filehandling --- .../resources/Economics/expenseRegisterTest.register | 6 ++++++ .../resources/Economics/incomeRegisterTest.register | 6 ++++++ .../idatt1002/demo/data/Economics/ExpenseTest.java | 4 +--- .../demo/data/Economics/FileHandlingTest.java | 12 ++++++------ 4 files changed, 19 insertions(+), 9 deletions(-) create mode 100644 src/main/resources/Economics/expenseRegisterTest.register create mode 100644 src/main/resources/Economics/incomeRegisterTest.register diff --git a/src/main/resources/Economics/expenseRegisterTest.register b/src/main/resources/Economics/expenseRegisterTest.register new file mode 100644 index 00000000..c7d08411 --- /dev/null +++ b/src/main/resources/Economics/expenseRegisterTest.register @@ -0,0 +1,6 @@ +date=03.03.23 +description=description +amount=59.900001525878906 +isReoccuring=Not reoccurring +category=CLOTHES + diff --git a/src/main/resources/Economics/incomeRegisterTest.register b/src/main/resources/Economics/incomeRegisterTest.register new file mode 100644 index 00000000..0b3082de --- /dev/null +++ b/src/main/resources/Economics/incomeRegisterTest.register @@ -0,0 +1,6 @@ +date=03.03.23 +description=description +amount=59.900001525878906 +isReoccuring=Not reoccurring +category=GIFT + diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java index 338f63fb..191c8513 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java @@ -1,7 +1,5 @@ package no.ntnu.idatt1002.demo.data.Economics; -import no.ntnu.idatt1002.demo.data.Economics.Expense; -import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -14,7 +12,7 @@ class ExpenseTest { void constructorThrows(){ assertThrows(IllegalArgumentException.class, () -> new Expense("description", 8.5f, false, null, "03.03.23")); assertThrows(IllegalArgumentException.class, () -> new Expense("description", -10.0f, false, ExpenseCategory.BOOKS, "03.03.23")); - }; + } @Test @DisplayName("The Expense constructor does not throw exceptions when it should not.") diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java index aaa5097a..59ffe367 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java @@ -23,7 +23,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(incomeRegister, fileTitle)); } @@ -43,7 +43,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(incomeRegister, fileTitle)); } @@ -67,7 +67,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(incomeRegister, fileTitle)); } @@ -92,7 +92,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(expenseRegister, fileTitle)); } @@ -112,7 +112,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(expenseRegister, fileTitle)); } @@ -137,7 +137,7 @@ class FileHandlingTest { } @Test @DisplayName("Writing to file does not throw exception") - void writingToFileDoesNotThrowException() throws IOException { + void writingToFileDoesNotThrowException() { assertDoesNotThrow(()->fileHandling.writeItemRegisterToFile(expenseRegister, fileTitle)); } -- GitLab From 1bb2bb8b2d6f3250810235d39fdab158f0d82194 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 11:31:11 +0100 Subject: [PATCH 043/103] Synched up backend register and frontend observable list by adding refreshObservableList method --- .../demo/controller/ExpensesController.java | 42 +++++++++++-------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index b6881e9c..5f09afad 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -7,6 +7,9 @@ import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; @@ -15,8 +18,10 @@ import javafx.scene.control.TableView; import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.Modality; +import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; import no.ntnu.idatt1002.demo.view.ExpenseRepository; enum DialogMode { @@ -24,13 +29,6 @@ enum DialogMode { } public class ExpensesController { -/* Help me make a application using JavaFX scenebuilder. I trying to make a tableview of expenses. - An expense consists of date, amount and description, which are of type string, double and string respectively. - On the tableview window, there should be an "add"-button, which if clicked brings up a popup window that is a dialog box. - Here, you can type into textfields for date, amount and description. There should also be a OK-button here, that if clickec - closes the popup and adds the new expense to the tableview*/ - - /** * The mode of the dialog. NEW if new contact, EDIT if edit existing contact. */ @@ -68,11 +66,12 @@ public class ExpensesController { @FXML private TableView<Expense> expenseTableView; - ExpenseRepository expenseRegister = new ExpenseRepository(); - ObservableList<Expense> expenses = FXCollections.observableArrayList(expenseRegister.getExpenses()); + ExpenseRegister expenseRegister = new ExpenseRegister(); + ObservableList<Expense> expenses = FXCollections.observableArrayList(expenseRegister.getItems()); @FXML public void initialize() { + //if xfil er tom -> expenseRegister = null, else expenseregister leses av filen ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); show.setItems(filter); show.setValue("All"); @@ -82,17 +81,17 @@ public class ExpensesController { categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - expenseTableView.setItems(expenses); - + refreshObservableList(); + expenseTableView.setItems(expenses); } @FXML - public void handleAddButton(ActionEvent event) { + protected void handleAddButton(ActionEvent event) { handleEditButton(event); } @FXML - private void handleEditButton(ActionEvent event) { + protected void handleEditButton(ActionEvent event) { Expense newExpense = null; String dialogTitle = ""; // Load the FXML file for your dialog box @@ -132,21 +131,30 @@ public class ExpensesController { //Only add the expense to the tableview, if the expense is not null if (newExpense != null && mode == DialogMode.ADD) { - expenseTableView.getItems().add(newExpense); + expenseRegister.addItem(newExpense); + refreshObservableList(); } else { System.out.println("expense is null or dialog mode is add"); } expenseTableView.refresh(); } - - public void handleDeleteButton(ActionEvent event) throws IOException { + protected void refreshObservableList() { + this.expenses.setAll(expenseRegister.getItems()); } - public void switchIncome(ActionEvent event) { + public void handleDeleteButton(ActionEvent event) throws IOException { + } + public void switchIncome(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Income.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); } public void switchOverview(ActionEvent event) { -- GitLab From 984d6ade41d41616d1633dc226f94288d1f93794 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 11:32:03 +0100 Subject: [PATCH 044/103] Made next button switch to MainMenu screen --- src/main/resources/view/Overview.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/view/Overview.fxml b/src/main/resources/view/Overview.fxml index dc81039b..4bc86e4c 100644 --- a/src/main/resources/view/Overview.fxml +++ b/src/main/resources/view/Overview.fxml @@ -96,7 +96,7 @@ <Button mnemonicParsing="false" onAction="#switchIncome" text="Income" textFill="#4d1616" /> <Button mnemonicParsing="false" onAction="#switchExpenses" text="Expenses" /> <Button disable="true" mnemonicParsing="false" text="Savings" /> - <Button defaultButton="true" mnemonicParsing="false" onAction="#switchExpenses" text="Next"> + <Button defaultButton="true" mnemonicParsing="false" onAction="#switchMainMenu" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin></Button> -- GitLab From e0147a1145bc4d14a964b5e22b4ec4f3fdfaa701 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Wed, 22 Mar 2023 12:07:20 +0100 Subject: [PATCH 045/103] Fixed FileHandling tests failing --- .../ntnu/idatt1002/demo/data/Economics/FileHandling.java | 9 +++++---- .../java/no/ntnu/idatt1002/demo/data/Economics/Item.java | 4 ++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java index bfbe5ab7..a2909089 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java @@ -48,7 +48,7 @@ public class FileHandling{ boolean reoccuring = false; IncomeCategory incomeCategory = null; try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { - String line; + String line = br.readLine(); String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); @@ -67,13 +67,13 @@ public class FileHandling{ case "STUDENT_LOAN" -> IncomeCategory.STUDENT_LOAN; default -> IncomeCategory.SALARY; }; - } - if(line.isEmpty() || (nextLine == null)) { + } if(line.isEmpty() || (nextLine == null)) { if(description.equals("")){ incomeRegister.addItem(new Income(amount, reoccuring, incomeCategory, date)); } else{ incomeRegister.addItem(new Income(description, amount, reoccuring, incomeCategory, date)); } + nextLine=br.readLine(); description = ""; } } @@ -96,7 +96,7 @@ public class FileHandling{ boolean reoccuring = false; ExpenseCategory expenseCategory = null; try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { - String line; + String line = br.readLine(); String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); @@ -123,6 +123,7 @@ public class FileHandling{ } else { expenseRegister.addItem(new Expense(description, amount, reoccuring, expenseCategory, date)); } + nextLine = br.readLine(); description = ""; } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index a3d8fee1..7135894a 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -154,10 +154,10 @@ public abstract class Item { isReoccuring = "Not reoccurring"; } if(!description.get().isBlank()){ - return "\ndate=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; + return "\ndate=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } else{ - return "\ndate="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring; + return "\ndate="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } } -- GitLab From 707a7f67b3415a2e17c531fe758d65bd1a77a6b3 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 12:09:23 +0100 Subject: [PATCH 046/103] Instantiate observable list in initialize(). Made method baseline for filehandling method to save changes in expenses --- .../demo/controller/ExpensesController.java | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 5f09afad..c9c1a5a0 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -1,5 +1,6 @@ package no.ntnu.idatt1002.demo.controller; +import java.io.File; import java.io.IOException; import javafx.collections.FXCollections; @@ -67,12 +68,13 @@ public class ExpensesController { private TableView<Expense> expenseTableView; ExpenseRegister expenseRegister = new ExpenseRegister(); - ObservableList<Expense> expenses = FXCollections.observableArrayList(expenseRegister.getItems()); + ObservableList<Expense> expenses; @FXML public void initialize() { - //if xfil er tom -> expenseRegister = null, else expenseregister leses av filen ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); + expenses = FXCollections.observableArrayList(expenseRegister.getItems()); + show.setItems(filter); show.setValue("All"); @@ -82,6 +84,8 @@ public class ExpensesController { descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); + //loadDataFromFile(); + refreshObservableList(); expenseTableView.setItems(expenses); } @@ -148,7 +152,15 @@ public class ExpensesController { } - public void switchIncome(ActionEvent event) throws IOException { + public void loadDataFromFile(String fileName) { + File file = new File(System.getProperty("user.dir") + "/src/main/resources/" + fileName + ".txt"); + if (file.length() == 0) { + + } + + } + @FXML + protected void switchIncome(ActionEvent event) throws IOException { FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Income.fxml")); Parent root = loader.load(); Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); -- GitLab From 742192fbb7e3da94f9c9652dadad985d07478573 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 12:11:29 +0100 Subject: [PATCH 047/103] Added comments to explain expense attributes. Added todo for setExpense - changes are still saved even if the cancel button is clicked when editing an expense --- .../idatt1002/demo/controller/AddExpenseController.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 2d18fcc8..02978779 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -26,7 +26,9 @@ import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import no.ntnu.idatt1002.demo.view.ExpenseRepository; public class AddExpenseController { - Expense newExpense = null; + Expense newExpense = null; //the expense that is chosen when editing or the expense that is created when adding + + Expense oldExpense = null; //an expense that is meant to track the old state of an expense before editing, in case cancel bugtton is clicked @FXML private Button cancelBtn; @@ -69,8 +71,9 @@ public class AddExpenseController { return recurringBox.getValue().equals("Yes"); } - public void setExpense(Expense expense) { + public void setExpense(Expense expense) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED this.newExpense = expense; + this.oldExpense = expense; dateField.textProperty().bindBidirectional(expense.dateProperty()); amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); -- GitLab From 21e63324be74128ad094e48a474ddd4fa6ab85a7 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 12:25:29 +0100 Subject: [PATCH 048/103] Implemented file handling class in the ExpensesController --- .../idatt1002/demo/controller/ExpensesController.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index c9c1a5a0..d7dbaf99 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -23,6 +23,7 @@ import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; +import no.ntnu.idatt1002.demo.data.Economics.FileHandling; import no.ntnu.idatt1002.demo.view.ExpenseRepository; enum DialogMode { @@ -73,11 +74,13 @@ public class ExpensesController { @FXML public void initialize() { ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); - expenses = FXCollections.observableArrayList(expenseRegister.getItems()); - show.setItems(filter); show.setValue("All"); + + expenses = FXCollections.observableArrayList(expenseRegister.getItems()); + + dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); @@ -153,6 +156,7 @@ public class ExpensesController { } public void loadDataFromFile(String fileName) { + FileHandling fileHandling = new FileHandling(); File file = new File(System.getProperty("user.dir") + "/src/main/resources/" + fileName + ".txt"); if (file.length() == 0) { -- GitLab From aca6cdbc9e38f4960f882f76e2035dead13c69d4 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 12:26:57 +0100 Subject: [PATCH 049/103] Set the comboboxes to default values. Added comment to setExpenses, because amount textfield is stored with comma, which is not a double --- .../idatt1002/demo/controller/AddExpenseController.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 02978779..dd1ac55a 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -55,9 +55,11 @@ public class AddExpenseController { ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList( ExpenseCategory.values()); categoryBox.setItems(expenseCategories); + categoryBox.setValue(ExpenseCategory.FOOD); ObservableList<String> recurring = FXCollections.observableArrayList("Yes", "No"); recurringBox.setItems(recurring); + recurringBox.setValue("No"); System.out.print("This is in initialize"); System.out.println(descriptionField); @@ -72,11 +74,8 @@ public class AddExpenseController { } public void setExpense(Expense expense) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED - this.newExpense = expense; - this.oldExpense = expense; - dateField.textProperty().bindBidirectional(expense.dateProperty()); - amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); + amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); } public void pressOkBtn(ActionEvent event) { -- GitLab From 496a4a89bb35c18e12404c548e5ce0104c43445f Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 12:55:46 +0100 Subject: [PATCH 050/103] Made recurringbox contain booleans. Binded this value to the expense recurring property --- .../idatt1002/demo/controller/AddExpenseController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index dd1ac55a..71f58648 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -48,7 +48,7 @@ public class AddExpenseController { private ComboBox<ExpenseCategory> categoryBox; @FXML - private ComboBox<String> recurringBox; + private ComboBox<Boolean> recurringBox; @FXML public void initialize() { @@ -57,9 +57,9 @@ public class AddExpenseController { categoryBox.setItems(expenseCategories); categoryBox.setValue(ExpenseCategory.FOOD); - ObservableList<String> recurring = FXCollections.observableArrayList("Yes", "No"); + ObservableList<Boolean> recurring = FXCollections.observableArrayList(true, false); recurringBox.setItems(recurring); - recurringBox.setValue("No"); + recurringBox.setValue(false); System.out.print("This is in initialize"); System.out.println(descriptionField); @@ -77,6 +77,8 @@ public class AddExpenseController { dateField.textProperty().bindBidirectional(expense.dateProperty()); amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); + //categoryBox.valueProperty().bindBidirectional(expense.getCategory()); + recurringBox.valueProperty().bindBidirectional(expense.recurringProperty()); } public void pressOkBtn(ActionEvent event) { String date = dateField.getText(); -- GitLab From e58e72c41c5aa67e24526cb11a01485894423b63 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 13:30:24 +0100 Subject: [PATCH 051/103] Made recurringBox contain a boolean instead of a yes/no string, in order to bind it together with the recurring attribute --- .../demo/controller/AddExpenseController.java | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 71f58648..273c819f 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,30 +1,17 @@ package no.ntnu.idatt1002.demo.controller; -import java.io.IOException; import java.text.NumberFormat; -import java.util.Optional; -import javafx.beans.property.DoubleProperty; -import javafx.beans.property.SimpleDoubleProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; -import javafx.event.EventHandler; import javafx.fxml.FXML; -import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; -import javafx.scene.control.Dialog; -import javafx.scene.control.DialogPane; import javafx.scene.control.TextField; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; -import no.ntnu.idatt1002.demo.view.ExpenseRepository; - public class AddExpenseController { Expense newExpense = null; //the expense that is chosen when editing or the expense that is created when adding @@ -70,7 +57,7 @@ public class AddExpenseController { } public boolean isRecurring() { - return recurringBox.getValue().equals("Yes"); + return recurringBox.getValue();//.equals("Yes"); } public void setExpense(Expense expense) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED @@ -80,6 +67,8 @@ public class AddExpenseController { //categoryBox.valueProperty().bindBidirectional(expense.getCategory()); recurringBox.valueProperty().bindBidirectional(expense.recurringProperty()); } + + @FXML public void pressOkBtn(ActionEvent event) { String date = dateField.getText(); double amount = Double.parseDouble(amountField.getText()); @@ -93,6 +82,7 @@ public class AddExpenseController { ((Stage) source.getScene().getWindow()).close(); } + @FXML public void pressCancelBtn(ActionEvent event) { final Node source = (Node) event.getSource(); final Stage stage = (Stage) source.getScene().getWindow(); -- GitLab From de7a3ade88ca8896d3ce938c2e7a249d1f7adbbf Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 13:30:48 +0100 Subject: [PATCH 052/103] Implemented delete functionality using an alert box --- .../demo/controller/ExpensesController.java | 35 ++++++++++++++----- src/main/resources/view/Expenses.fxml | 6 ++-- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index d7dbaf99..2acd76a3 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -3,6 +3,7 @@ package no.ntnu.idatt1002.demo.controller; import java.io.File; import java.io.IOException; +import java.util.Optional; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -11,7 +12,10 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; import javafx.scene.control.TableColumn; @@ -37,15 +41,15 @@ public class ExpensesController { private DialogMode mode; @FXML - private Button add; + private Button addBtn; @FXML private TextField descriptionField; @FXML - private Button edit; + private Button editBtn; @FXML - private Button delete; + private Button deleteBtn; @FXML private ComboBox<String> show; @@ -77,10 +81,10 @@ public class ExpensesController { show.setItems(filter); show.setValue("All"); - + Expense newExpense = new Expense(99, true, ExpenseCategory.FOOD, "1/1/23"); + expenseRegister.addItem(newExpense); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); - dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); @@ -116,11 +120,11 @@ public class ExpensesController { // Get the controller for the loaded FXML file AddExpenseController dialogController = loader.getController(); - if (event.getSource().equals(add)) { + if (event.getSource().equals(addBtn)) { mode = DialogMode.ADD; dialogTitle = "Add expense"; - } else if (event.getSource().equals(edit) && expenseTableView.getSelectionModel().getSelectedItem() != null) { + } else if (event.getSource().equals(editBtn) && expenseTableView.getSelectionModel().getSelectedItem() != null) { mode = DialogMode.EDIT; dialogTitle = "Edit expense"; newExpense = expenseTableView.getSelectionModel().getSelectedItem(); @@ -151,8 +155,23 @@ public class ExpensesController { } - public void handleDeleteButton(ActionEvent event) throws IOException { + @FXML + public void handleDeleteBtn(ActionEvent event) { + Optional<ButtonType> isConfirmed = showConfirmationDialog("Confirm Delete", "Delete Confirmation", + "Are you sure you would like to delete the selected expense?"); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + int selectedIdx = expenseTableView.getSelectionModel().getSelectedIndex(); + expenses.remove(selectedIdx); + } + } + + private Optional<ButtonType> showConfirmationDialog(String title, String headerText, String contentText) { + Alert alert = new Alert(AlertType.CONFIRMATION); + alert.setTitle(title); + alert.setHeaderText(headerText); + alert.setContentText(contentText); + return alert.showAndWait(); } public void loadDataFromFile(String fileName) { diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 107ddd16..7985d641 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> + <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -81,7 +81,7 @@ </image> </ImageView> </graphic></Button> - <Button fx:id="edit" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> + <Button fx:id="editBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -89,7 +89,7 @@ </image> </ImageView> </graphic></Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> + <Button fx:id="deleteBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleDeleteBtn" text="Delete" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> -- GitLab From 02332bc30f1e0f1a9107fc3806529a932dfb9ed6 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 13:32:43 +0100 Subject: [PATCH 053/103] Converted parameters in alertbox to local variables. Changed the confirmation message to accoutn for both expense and income --- .../demo/controller/ExpensesController.java | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 2acd76a3..d0f1cd4c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -28,7 +28,6 @@ import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; import no.ntnu.idatt1002.demo.data.Economics.FileHandling; -import no.ntnu.idatt1002.demo.view.ExpenseRepository; enum DialogMode { ADD, EDIT, DELETE @@ -157,19 +156,19 @@ public class ExpensesController { @FXML public void handleDeleteBtn(ActionEvent event) { - Optional<ButtonType> isConfirmed = showConfirmationDialog("Confirm Delete", "Delete Confirmation", - "Are you sure you would like to delete the selected expense?"); + Optional<ButtonType> isConfirmed = showConfirmationDialog( + ); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { int selectedIdx = expenseTableView.getSelectionModel().getSelectedIndex(); expenses.remove(selectedIdx); } } - private Optional<ButtonType> showConfirmationDialog(String title, String headerText, String contentText) { + private Optional<ButtonType> showConfirmationDialog() { Alert alert = new Alert(AlertType.CONFIRMATION); - alert.setTitle(title); - alert.setHeaderText(headerText); - alert.setContentText(contentText); + alert.setTitle("Confirm Delete"); + alert.setHeaderText("Delete Confirmation"); + alert.setContentText("Are you sure you would like to delete the selected entry?"); return alert.showAndWait(); } -- GitLab From 980d35bdd619dfcc64d7a327b91c85147670d5fd Mon Sep 17 00:00:00 2001 From: Andreas Kluge Svendsrud <andreksv@stud.ntnu.no> Date: Wed, 22 Mar 2023 13:58:33 +0100 Subject: [PATCH 054/103] Reworked Item class, reworked Filehandling class and made method isEmpty --- .../demo/data/Economics/ExpenseRegister.java | 2 ++ .../demo/data/Economics/FileHandling.java | 34 ++++++++++++++----- .../demo/data/Economics/IncomeRegister.java | 2 ++ .../idatt1002/demo/data/Economics/Item.java | 14 +++++--- .../demo/data/Economics/ItemRegister.java | 1 + .../data/Economics/ExpenseRegisterTest.java | 8 ----- .../demo/data/Economics/FileHandlingTest.java | 20 +++++++++++ 7 files changed, 61 insertions(+), 20 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegister.java index 1a279301..b54e3afb 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegister.java @@ -6,6 +6,8 @@ import java.util.List; * ExpenseRegister is a class for * storing Expenses. Subclass of * ItemRegister. + * + * @author andreas */ public class ExpenseRegister extends ItemRegister<Expense> { diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java index a2909089..e80d13c3 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java @@ -9,6 +9,8 @@ import java.io.*; /** * FileHandling is a class for writing and reading * Item-objects to and from a file. + * + * @author andreas */ public class FileHandling{ private static final String filePath = "src/main/resources/Economics/"; @@ -34,11 +36,28 @@ public class FileHandling{ } /** + * Method for checking if a .register file is empty. + * + * @param fileTitle the name of the file you want to check + * @return true or false depending on if file is empty. + * @throws IOException if an input or output exception occurred. + */ + public boolean isEmpty(String fileTitle) throws IOException { + try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { + if (br.readLine().equals("The register is Empty")) { + return true; + } else { + return false; + } + } + } + + /** * Method for reading (getting) an IncomeRegister from a file. * - * @param fileTitle the name of the file you want to read from + * @param fileTitle the name of the file you want to read from. * @return the IncomeRegister from the file. - * @throws IOException if an input or output exception occurred + * @throws IOException if an input or output exception occurred. */ public IncomeRegister readIncomeRegisterFromFile(String fileTitle) throws IOException { IncomeRegister incomeRegister = new IncomeRegister(); @@ -48,7 +67,7 @@ public class FileHandling{ boolean reoccuring = false; IncomeCategory incomeCategory = null; try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { - String line = br.readLine(); + String line; String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); @@ -67,13 +86,13 @@ public class FileHandling{ case "STUDENT_LOAN" -> IncomeCategory.STUDENT_LOAN; default -> IncomeCategory.SALARY; }; - } if(line.isEmpty() || (nextLine == null)) { + } + if(line.isEmpty() || (nextLine == null)) { if(description.equals("")){ incomeRegister.addItem(new Income(amount, reoccuring, incomeCategory, date)); } else{ incomeRegister.addItem(new Income(description, amount, reoccuring, incomeCategory, date)); } - nextLine=br.readLine(); description = ""; } } @@ -96,7 +115,7 @@ public class FileHandling{ boolean reoccuring = false; ExpenseCategory expenseCategory = null; try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { - String line = br.readLine(); + String line; String nextLine = br.readLine(); while ((line = nextLine) != null) { nextLine = br.readLine(); @@ -123,11 +142,10 @@ public class FileHandling{ } else { expenseRegister.addItem(new Expense(description, amount, reoccuring, expenseCategory, date)); } - nextLine = br.readLine(); description = ""; } } } return expenseRegister; } -} \ No newline at end of file +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java index 1a9c4a12..4e8c1a3c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegister.java @@ -6,6 +6,8 @@ import java.util.List; * IncomeRegister is a class for * storing Income. Subclass of * ItemRegister. + * + * @author andreas */ public class IncomeRegister extends ItemRegister<Income>{ /** diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 7135894a..3e204103 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -40,8 +40,8 @@ public abstract class Item { * @param amount The price of the item as a float. */ public Item (String description, double amount, boolean recurring, String date){ - this(amount, recurring, date); - this.description = new SimpleStringProperty(description); + this(amount, recurring, date); + this.description = new SimpleStringProperty(description); } public StringProperty descriptionProperty() { @@ -87,9 +87,14 @@ public abstract class Item { this.amount.set(amount); } + /** + * The method returns the BooleanProperty of recurring. + * @return BooleanProperty whose value is either true or false. + */ public BooleanProperty recurringProperty() { return this.recurring; } + /** * The method returns true if the transaction is recurring, false otherwise. * @return True if the transaction is a recurring one, false otherwise. @@ -154,10 +159,10 @@ public abstract class Item { isReoccuring = "Not reoccurring"; } if(!description.get().isBlank()){ - return "\ndate=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } else{ - return "\ndate="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } } @@ -183,3 +188,4 @@ public abstract class Item { } + diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index 24843428..70da9679 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -8,6 +8,7 @@ import java.util.List; * for storing either Income or Expense. * * @param <T> Income or Expense + * @author andreas */ public abstract class ItemRegister<T extends Item>{ List<T> items; diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java index cf9f6ba0..3f8c5564 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java @@ -55,14 +55,6 @@ class ExpenseRegisterTest { } - @Test - @DisplayName("getItem does not throw exception when it should not") - void getItemDoesNotThrow(){ - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - expenseRegister.addItem(expense1); - assertDoesNotThrow(() -> expenseRegister.getItem(new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"))); - } - @Nested @DisplayName("Test getTotalSum and getExpenseByCategory") class testGetExpenseByCategoryMethod { diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java index 59ffe367..b2140dd6 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java @@ -9,6 +9,26 @@ class FileHandlingTest { FileHandling fileHandling = new FileHandling(); ExpenseRegister expenseRegister = new ExpenseRegister(); IncomeRegister incomeRegister = new IncomeRegister(); + + @Nested + @DisplayName("Test isEmpty method") + class testIsEmpty{ + @Test + @DisplayName("isEmpty returns true if a file is empty") + void isEmptyReturnsTrueIfAFileIsEmpty() throws IOException { + fileHandling.writeItemRegisterToFile(incomeRegister, "incomeRegisterTest"); + assertTrue(fileHandling.isEmpty("incomeRegisterTest")); + } + + @Test + @DisplayName("isEmpty returns false if a file is not empty") + void isEmptyReturnsFalseIfFileIsNotEmpty() throws IOException { + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + incomeRegister.addItem(income1); + fileHandling.writeItemRegisterToFile(incomeRegister, "incomeRegisterTest"); + assertFalse(fileHandling.isEmpty("incomeRegisterTest")); + } + } @Nested @DisplayName("FileHandling IncomeRegister to file") class fileHandlingIncomeRegisterToFile{ -- GitLab From 0a048b827a4185eeaf32f74c4060c85e22e2db3c Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 14:37:42 +0100 Subject: [PATCH 055/103] Added a DatePicker to the dialog box for testing --- .../demo/controller/AddExpenseController.java | 24 ++++++++++++------- src/main/resources/view/AddExpense.fxml | 4 +++- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 273c819f..0614e78c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -1,6 +1,7 @@ package no.ntnu.idatt1002.demo.controller; import java.text.NumberFormat; +import java.time.LocalDate; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -8,6 +9,7 @@ import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Button; import javafx.scene.control.ComboBox; +import javafx.scene.control.DatePicker; import javafx.scene.control.TextField; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; @@ -15,7 +17,7 @@ import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; public class AddExpenseController { Expense newExpense = null; //the expense that is chosen when editing or the expense that is created when adding - Expense oldExpense = null; //an expense that is meant to track the old state of an expense before editing, in case cancel bugtton is clicked + Expense chosenExpense = null; //an expense that is meant to track the old state of an expense before editing, in case cancel bugtton is clicked @FXML private Button cancelBtn; @@ -25,6 +27,9 @@ public class AddExpenseController { @FXML private TextField dateField; + @FXML + private DatePicker datePicker; + @FXML private TextField descriptionField; @@ -70,13 +75,16 @@ public class AddExpenseController { @FXML public void pressOkBtn(ActionEvent event) { - String date = dateField.getText(); - double amount = Double.parseDouble(amountField.getText()); - String description = descriptionField.getText(); - ExpenseCategory category = getCategory(); - boolean recurring = isRecurring(); - newExpense = new Expense(description, amount, recurring, category, date); - System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + if (newExpense == null) { + String date = dateField.getText(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + newExpense = new Expense(description, amount, recurring, category, date); + System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + } + final Node source = (Node) event.getSource(); ((Stage) source.getScene().getWindow()).close(); diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index 67cb0b9c..db41d454 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -3,6 +3,7 @@ <?import javafx.geometry.Insets?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.DatePicker?> <?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> @@ -33,7 +34,6 @@ <Label text="Description:" GridPane.rowIndex="2" /> <Label text="Category" GridPane.rowIndex="3" /> <Label text="Recurring" GridPane.rowIndex="4" /> - <TextField fx:id="dateField" promptText="1/1/23" GridPane.columnIndex="1" /> <TextField fx:id="amountField" promptText="100" GridPane.columnIndex="1" GridPane.rowIndex="1" /> <TextField fx:id="descriptionField" promptText="(optional)" GridPane.columnIndex="1" GridPane.rowIndex="2" /> <ComboBox fx:id="categoryBox" prefWidth="150.0" promptText="Food" GridPane.columnIndex="1" GridPane.rowIndex="3" /> @@ -47,6 +47,8 @@ <Insets top="20.0" /> </GridPane.margin> </HBox> + <DatePicker fx:id="datePicker" GridPane.columnIndex="2" /> + <TextField fx:id="dateField" GridPane.columnIndex="1" /> </children> </GridPane> </content> -- GitLab From 08b0d1e8de2000d7234545c8d1bbd49c7f9222cb Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 14:38:44 +0100 Subject: [PATCH 056/103] Implemented Filehandling method, which allows registers to be saved to file when switching scenes --- .../demo/controller/ExpensesController.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index d0f1cd4c..3eedc713 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -41,9 +41,6 @@ public class ExpensesController { @FXML private Button addBtn; - - @FXML - private TextField descriptionField; @FXML private Button editBtn; @@ -75,11 +72,12 @@ public class ExpensesController { ObservableList<Expense> expenses; @FXML - public void initialize() { + public void initialize() throws IOException { ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); show.setItems(filter); show.setValue("All"); + //expenseRegister = loadDataFromFile("wawawiwa"); Expense newExpense = new Expense(99, true, ExpenseCategory.FOOD, "1/1/23"); expenseRegister.addItem(newExpense); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); @@ -90,8 +88,6 @@ public class ExpensesController { descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - //loadDataFromFile(); - refreshObservableList(); expenseTableView.setItems(expenses); } @@ -173,16 +169,23 @@ public class ExpensesController { return alert.showAndWait(); } - public void loadDataFromFile(String fileName) { + public ExpenseRegister loadDataFromFile(String fileName) throws IOException { FileHandling fileHandling = new FileHandling(); - File file = new File(System.getProperty("user.dir") + "/src/main/resources/" + fileName + ".txt"); - if (file.length() == 0) { - + if (fileHandling.isEmpty(fileName)) { + //expenseRegister = new ExpenseRegister(); + } else { + expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); } + return expenseRegister; + } + public void saveDataToFile(String fileName) throws IOException { + FileHandling fileHandling = new FileHandling(); + fileHandling.writeItemRegisterToFile(expenseRegister, fileName); } @FXML protected void switchIncome(ActionEvent event) throws IOException { + saveDataToFile("Expenses"); FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Income.fxml")); Parent root = loader.load(); Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); @@ -191,7 +194,15 @@ public class ExpensesController { stage.show(); } - public void switchOverview(ActionEvent event) { + @FXML + public void switchOverview(ActionEvent event) throws IOException { + saveDataToFile("Expenses"); + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Overview.fxml")); + Parent root = loader.load(); + Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); } } -- GitLab From eb3b1fb50e5dac4dd33be8c7e9d4ad9b5d0e8605 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 14:50:30 +0100 Subject: [PATCH 057/103] Generalised all methods that switch scene in Expenses --- .../demo/controller/ExpensesController.java | 33 ++++++++++--------- src/main/resources/view/Expenses.fxml | 8 ++--- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 3eedc713..249eb6f4 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -50,6 +50,14 @@ public class ExpensesController { @FXML private ComboBox<String> show; + @FXML + private Button incomeBtn; + + @FXML + private Button overviewBtn; + + @FXML + private Button returnBtn; @FXML private TableColumn<Expense, Double> amountColumn; @@ -183,26 +191,21 @@ public class ExpensesController { FileHandling fileHandling = new FileHandling(); fileHandling.writeItemRegisterToFile(expenseRegister, fileName); } - @FXML - protected void switchIncome(ActionEvent event) throws IOException { - saveDataToFile("Expenses"); - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Income.fxml")); - Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); - } @FXML - public void switchOverview(ActionEvent event) throws IOException { - saveDataToFile("Expenses"); - FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/Overview.fxml")); + public void switchScene(ActionEvent event) throws IOException { + FXMLLoader loader = new FXMLLoader(); + if (event.getSource() == incomeBtn) { + loader.setLocation(SceneController.class.getResource("/view/Income.fxml")); + } else if (event.getSource() == overviewBtn) { + loader.setLocation(SceneController.class.getResource("/view/Overview.fxml")); + } else if (event.getSource() == returnBtn) { + loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); + } Parent root = loader.load(); - Stage stage = (Stage)((Node)event.getSource()).getScene().getWindow(); + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); - } } diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 7985d641..400e0084 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -33,7 +33,7 @@ <top> <HBox BorderPane.alignment="CENTER"> <children> - <Button mnemonicParsing="false" onAction="#switchIncome" text="Return "> + <Button fx:id="returnBtn" mnemonicParsing="false" onAction="#switchScene" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> @@ -119,14 +119,14 @@ </VBox> <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button mnemonicParsing="false" onAction="#switchOverview" text="Overview"> + <Button fx:id="overviewBtn" mnemonicParsing="false" onAction="#switchScene" text="Overview"> <HBox.margin> <Insets right="5.0" /> </HBox.margin></Button> - <Button mnemonicParsing="false" onAction="#switchIncome" text="Income" /> + <Button fx:id="incomeBtn" mnemonicParsing="false" onAction="#switchScene" text="Income" /> <Button disable="true" mnemonicParsing="false" text="Expenses" /> <Button disable="true" mnemonicParsing="false" text="Savings" /> - <Button mnemonicParsing="false" onAction="#switchOverview" text="Next"> + <Button disable="true" mnemonicParsing="false" onAction="#switchScene" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin></Button> -- GitLab From e6fc4f36b7798de328f68dfd04b6c30075d942ef Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 14:58:59 +0100 Subject: [PATCH 058/103] Use saveDataToFile in switchScene in order to save to file --- .../no/ntnu/idatt1002/demo/controller/ExpensesController.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 249eb6f4..52d91f46 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -194,6 +194,7 @@ public class ExpensesController { @FXML public void switchScene(ActionEvent event) throws IOException { + saveDataToFile("Expense"); FXMLLoader loader = new FXMLLoader(); if (event.getSource() == incomeBtn) { loader.setLocation(SceneController.class.getResource("/view/Income.fxml")); -- GitLab From f2acf17427f916153ee7d446afdd661444c2d3a9 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 15:47:47 +0100 Subject: [PATCH 059/103] Enabled filereading for tableview such that files can be loaded --- .../idatt1002/demo/controller/ExpensesController.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 52d91f46..d69a4e9c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -85,9 +85,7 @@ public class ExpensesController { show.setItems(filter); show.setValue("All"); - //expenseRegister = loadDataFromFile("wawawiwa"); - Expense newExpense = new Expense(99, true, ExpenseCategory.FOOD, "1/1/23"); - expenseRegister.addItem(newExpense); + expenseRegister = loadDataFromFile("Expense"); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); @@ -182,7 +180,11 @@ public class ExpensesController { if (fileHandling.isEmpty(fileName)) { //expenseRegister = new ExpenseRegister(); } else { + try{ expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); + } catch(IOException e) { + e.printStackTrace(); + } } return expenseRegister; } -- GitLab From c33fb5fbaab67bbe84bcbe5d4849f8cbbff76ae0 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 16:13:21 +0100 Subject: [PATCH 060/103] Refactored handleDeleteBtn to remove the selected from the register first, before removing from the observable list --- .../idatt1002/demo/controller/ExpensesController.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index d69a4e9c..f154d492 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -76,7 +76,7 @@ public class ExpensesController { @FXML private TableView<Expense> expenseTableView; - ExpenseRegister expenseRegister = new ExpenseRegister(); + ExpenseRegister expenseRegister; ObservableList<Expense> expenses; @FXML @@ -94,7 +94,6 @@ public class ExpensesController { descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - refreshObservableList(); expenseTableView.setItems(expenses); } @@ -161,8 +160,9 @@ public class ExpensesController { Optional<ButtonType> isConfirmed = showConfirmationDialog( ); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { - int selectedIdx = expenseTableView.getSelectionModel().getSelectedIndex(); - expenses.remove(selectedIdx); + Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); + expenseRegister.removeItem(chosenExpense); + refreshObservableList(); } } @@ -178,7 +178,7 @@ public class ExpensesController { public ExpenseRegister loadDataFromFile(String fileName) throws IOException { FileHandling fileHandling = new FileHandling(); if (fileHandling.isEmpty(fileName)) { - //expenseRegister = new ExpenseRegister(); + expenseRegister = new ExpenseRegister(); } else { try{ expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); -- GitLab From 25fd135ca514a863b904e3d32e838b4b3e4f8698 Mon Sep 17 00:00:00 2001 From: Andreas Kluge Svendsrud <andreksv@stud.ntnu.no> Date: Wed, 22 Mar 2023 16:57:15 +0100 Subject: [PATCH 061/103] Item made variable date use LocalDate, and all other files support this change --- .../demo/controller/AddExpenseController.java | 22 ++++++------ .../demo/controller/ExpensesController.java | 5 ++- .../demo/controller/MainMenuController.java | 5 +-- .../demo/data/Economics/Expense.java | 5 +-- .../demo/data/Economics/FileHandling.java | 17 ++++++---- .../idatt1002/demo/data/Economics/Income.java | 6 ++-- .../idatt1002/demo/data/Economics/Item.java | 33 +++++++++--------- .../demo/data/Economics/ItemRegister.java | 9 ----- .../demo/view/ExpenseRepository.java | 34 ------------------- .../Economics/expenseRegisterTest.register | 2 +- .../Economics/incomeRegisterTest.register | 6 ---- .../data/Economics/ExpenseRegisterTest.java | 22 ++++++------ .../demo/data/Economics/ExpenseTest.java | 9 +++-- .../demo/data/Economics/FileHandlingTest.java | 25 ++++++++------ .../data/Economics/IncomeRegisterTest.java | 22 ++++++------ .../demo/data/Economics/IncomeTest.java | 10 ++++-- 16 files changed, 103 insertions(+), 129 deletions(-) delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 0614e78c..23682f82 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -2,6 +2,9 @@ package no.ntnu.idatt1002.demo.controller; import java.text.NumberFormat; import java.time.LocalDate; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.event.ActionEvent; @@ -66,7 +69,7 @@ public class AddExpenseController { } public void setExpense(Expense expense) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED - dateField.textProperty().bindBidirectional(expense.dateProperty()); + dateField.textProperty().bindBidirectional(new SimpleStringProperty(expense.getDate().toString())); amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); //categoryBox.valueProperty().bindBidirectional(expense.getCategory()); @@ -75,16 +78,13 @@ public class AddExpenseController { @FXML public void pressOkBtn(ActionEvent event) { - if (newExpense == null) { - String date = dateField.getText(); - double amount = Double.parseDouble(amountField.getText()); - String description = descriptionField.getText(); - ExpenseCategory category = getCategory(); - boolean recurring = isRecurring(); - newExpense = new Expense(description, amount, recurring, category, date); - System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); - } - + LocalDate date = LocalDate.parse(dateField.getText()); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + newExpense = new Expense(description, amount, recurring, category, date); + System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); final Node source = (Node) event.getSource(); ((Stage) source.getScene().getWindow()).close(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index f154d492..83a74789 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -3,6 +3,8 @@ package no.ntnu.idatt1002.demo.controller; import java.io.File; import java.io.IOException; +import java.time.LocalDate; +import java.time.Month; import java.util.Optional; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -85,7 +87,8 @@ public class ExpensesController { show.setItems(filter); show.setValue("All"); - expenseRegister = loadDataFromFile("Expense"); + Expense newExpense = new Expense(99, true, ExpenseCategory.FOOD, LocalDate.of(2023, Month.JULY, 5)); + expenseRegister.addItem(newExpense); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java index 275f86fb..55680195 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/MainMenuController.java @@ -1,5 +1,4 @@ package no.ntnu.idatt1002.demo.controller; -import java.awt.Image; import java.io.IOException; import javafx.event.ActionEvent; import javafx.fxml.FXML; @@ -13,8 +12,6 @@ import javafx.scene.control.Label; import javafx.scene.control.ProgressBar; import javafx.scene.image.ImageView; import javafx.stage.Stage; -import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; -import no.ntnu.idatt1002.demo.view.ExpenseRepository; public class MainMenuController { @@ -40,7 +37,7 @@ public class MainMenuController { private Label today; - ExpenseRepository expenseRepository; + //ExpenseRepository expenseRepository; @FXML public void initialize() { progressbar.setProgress(0.5); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java index a658928d..da0f64d1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java @@ -1,5 +1,6 @@ package no.ntnu.idatt1002.demo.data.Economics; +import java.time.LocalDate; import java.util.Objects; public class Expense extends Item{ @@ -15,7 +16,7 @@ public class Expense extends Item{ * @param category The category to which the Expense belongs to, provided as a value of ExpenseCategory. * @param date The date of the Expense at format "dd.mm.yy". */ - public Expense(double amount, boolean recurring, ExpenseCategory category, String date) { + public Expense(double amount, boolean recurring, ExpenseCategory category, LocalDate date) { super(amount, recurring, date); if(category == null) { @@ -33,7 +34,7 @@ public class Expense extends Item{ * @param category The category to which the Expense belongs to, provided as a value of ExpenseCategory * @param date The date of the Expense at format "dd.mm.yy". */ - public Expense(String description, double amount, boolean recurring, ExpenseCategory category, String date) { + public Expense(String description, double amount, boolean recurring, ExpenseCategory category, LocalDate date) { super(description, amount, recurring, date); if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java index e80d13c3..fcabd6e4 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/FileHandling.java @@ -4,6 +4,7 @@ import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.*; +import java.time.LocalDate; /** @@ -29,7 +30,11 @@ public class FileHandling{ */ public <T extends Item>void writeItemRegisterToFile(final ItemRegister<T> itemRegister, String fileTitle) throws IOException { try (BufferedWriter bw = new BufferedWriter(new FileWriter(filePath + fileTitle + fileType))) { - bw.write(itemRegister.toString()); + if (itemRegister.isEmpty()){ + bw.write(""); + } else{ + bw.write(itemRegister.toString()); + } } catch (IOException ex) { throw new IOException("Error writing story to file: " + ex.getMessage()); } @@ -44,7 +49,7 @@ public class FileHandling{ */ public boolean isEmpty(String fileTitle) throws IOException { try (BufferedReader br = new BufferedReader(new FileReader(filePath + fileTitle + fileType))) { - if (br.readLine().equals("The register is Empty")) { + if (br.readLine() == null) { return true; } else { return false; @@ -61,7 +66,7 @@ public class FileHandling{ */ public IncomeRegister readIncomeRegisterFromFile(String fileTitle) throws IOException { IncomeRegister incomeRegister = new IncomeRegister(); - String date = ""; + LocalDate date = null; String description = ""; double amount = 0; boolean reoccuring = false; @@ -72,7 +77,7 @@ public class FileHandling{ while ((line = nextLine) != null) { nextLine = br.readLine(); if(line.startsWith(FileHandling.date)) { - date = line.replace(FileHandling.date, ""); + date = LocalDate.parse(line.replace(FileHandling.date, "")); } else if (line.startsWith(FileHandling.description)) { description = line.replace(FileHandling.description,""); } else if (line.startsWith(FileHandling.amount)) { @@ -109,7 +114,7 @@ public class FileHandling{ */ public ExpenseRegister readExpenseRegisterFromFile(String fileTitle) throws IOException { ExpenseRegister expenseRegister = new ExpenseRegister(); - String date = ""; + LocalDate date = null; String description = ""; double amount = 0; boolean reoccuring = false; @@ -120,7 +125,7 @@ public class FileHandling{ while ((line = nextLine) != null) { nextLine = br.readLine(); if (line.startsWith(FileHandling.date)) { - date = line.replace(FileHandling.date, ""); + date = LocalDate.parse(line.replace(FileHandling.date, "")); } else if (line.startsWith(FileHandling.description)) { description = line.replace(FileHandling.description, ""); } else if (line.startsWith(FileHandling.amount)) { diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java index c07821dc..d4f4db57 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java @@ -1,5 +1,7 @@ package no.ntnu.idatt1002.demo.data.Economics; +import java.time.LocalDate; + /** * The Income class inherits from the Item class. The Item class additionally has a private field for an * enum value of IncomeCategory. @@ -20,7 +22,7 @@ public class Income extends Item{ * @param category The category to which the Income belongs to, provided as a value of IncomeCategory. * @param date The date of the Income at format "dd.mm.yy". */ - public Income(double amount, boolean recurring, IncomeCategory category, String date) { + public Income(double amount, boolean recurring, IncomeCategory category, LocalDate date) { super(amount, recurring, date); if(category == null) { @@ -38,7 +40,7 @@ public class Income extends Item{ * @param category The category to which the income belongs to, provided as a value of IncomeCategory. * @param date The date of the Income at format "dd.mm.yy". */ - public Income(String description, double amount, boolean recurring, IncomeCategory category, String date) { + public Income(String description, double amount, boolean recurring, IncomeCategory category, LocalDate date) { super(description, amount, recurring, date); if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 3e204103..53c8c81b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -1,5 +1,6 @@ package no.ntnu.idatt1002.demo.data.Economics; +import java.time.LocalDate; import java.util.Objects; import javafx.beans.property.*; @@ -15,20 +16,20 @@ public abstract class Item { private StringProperty description = new SimpleStringProperty(""); private final DoubleProperty amount; private BooleanProperty recurring; - private StringProperty date; // Format example: 09.08.23 + private LocalDate date; // Format example: 09.08.23 /** * The constructor of a new Item object takes in an amount as a double. If the amount is left blank, an * IllegalArgumentException is thrown. * @param amount price of an Item as a float. */ - public Item (double amount, boolean recurring, String date){ - if(amount <= 1.0f || date.isBlank()) { + public Item (double amount, boolean recurring, LocalDate date){ + if(amount <= 1.0f || date.toString().isBlank()) { throw new IllegalArgumentException("Both amount and date must be provided."); } else { this.amount = new SimpleDoubleProperty(amount); this.recurring = new SimpleBooleanProperty(recurring); - this.date = new SimpleStringProperty(date); + this.date = date; } } @@ -39,7 +40,7 @@ public abstract class Item { * @param description A description of the item as a String. * @param amount The price of the item as a float. */ - public Item (String description, double amount, boolean recurring, String date){ + public Item (String description, double amount, boolean recurring, LocalDate date){ this(amount, recurring, date); this.description = new SimpleStringProperty(description); } @@ -111,33 +112,33 @@ public abstract class Item { this.recurring.set(recurring); } + /** public StringProperty dateProperty() { return this.date; } + */ /** * The method returns the date of the item object (income/expense). * @return The date of the transaction. */ - public String getDate() { - return date.get(); + public LocalDate getDate() { + return this.date; } /** - * Sets the date field of the Item object to a new String provided as an argument. + * Sets the date field of the Item object to a new LocalDate provided as an argument. + * * @param newDate The new date with which to record the Item. */ - public void setDate(String newDate) { - if(date.get().isBlank()) { - throw new IllegalArgumentException("A date must be provided."); - } - this.date.set(newDate); + public void setDate(LocalDate newDate) { + this.date = newDate; } @Override public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Item item)) return false; - return Double.compare(item.amount.get(), amount.get()) == 0 && item.recurring.get() == recurring.get() && Objects.equals(description.get(), item.description.get()) && Objects.equals(date.get(), item.date.get()); + return Double.compare(item.amount.get(), amount.get()) == 0 && item.recurring.get() == recurring.get() && Objects.equals(description.get(), item.description.get()) && Objects.equals(date, item.date); } @Override @@ -159,10 +160,10 @@ public abstract class Item { isReoccuring = "Not reoccurring"; } if(!description.get().isBlank()){ - return "date=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date=" + date.toString() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } else{ - return "date="+date.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date="+date.toString() +"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java index 70da9679..24996d5f 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/ItemRegister.java @@ -37,15 +37,6 @@ public abstract class ItemRegister<T extends Item>{ public List<T> getItems() { return items; } - - public T getItem(T item){ - for(T anItem : items){ - if(anItem.equals(item)){ - return anItem; - } - } throw new IllegalArgumentException("Item not in register"); - } - /** * Add a new item to the register. * diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java b/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java deleted file mode 100644 index a6b91cda..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/view/ExpenseRepository.java +++ /dev/null @@ -1,34 +0,0 @@ -package no.ntnu.idatt1002.demo.view; - -import java.lang.reflect.Array; -import java.util.ArrayList; -import no.ntnu.idatt1002.demo.data.Economics.Expense; -import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; -import no.ntnu.idatt1002.demo.data.Economics.Item; -import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; - -public class ExpenseRepository { - - private final ArrayList<Expense> expenses; - - public ExpenseRepository() { - this.expenses = new ArrayList<>(); - expenses.add(new Expense("free", 100, true, ExpenseCategory.FOOD, "1/1/23")); - expenses.add(new Expense(149, true, ExpenseCategory.FOOD, "1/1/23")); - expenses.add(new Expense("Not free", 1000, true, ExpenseCategory.CLOTHES, "2/1/23")); - expenses.add(new Expense(200, true, ExpenseCategory.BOOKS, "3/1/23")); - } - public ArrayList<Expense> getExpenses () { - return expenses; - } - - public void addExpense(Expense expense) { - expenses.add(expense); - } - public double getSum() { - System.out.println(getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum)); - return getExpenses().stream().map(e -> e.getAmount()).reduce(0.0, Double::sum); - } -} - - diff --git a/src/main/resources/Economics/expenseRegisterTest.register b/src/main/resources/Economics/expenseRegisterTest.register index c7d08411..84a96af7 100644 --- a/src/main/resources/Economics/expenseRegisterTest.register +++ b/src/main/resources/Economics/expenseRegisterTest.register @@ -1,4 +1,4 @@ -date=03.03.23 +date=2023-03-03 description=description amount=59.900001525878906 isReoccuring=Not reoccurring diff --git a/src/main/resources/Economics/incomeRegisterTest.register b/src/main/resources/Economics/incomeRegisterTest.register index 0b3082de..e69de29b 100644 --- a/src/main/resources/Economics/incomeRegisterTest.register +++ b/src/main/resources/Economics/incomeRegisterTest.register @@ -1,6 +0,0 @@ -date=03.03.23 -description=description -amount=59.900001525878906 -isReoccuring=Not reoccurring -category=GIFT - diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java index 3f8c5564..a447a3c2 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseRegisterTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.time.Month; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -18,7 +20,7 @@ class ExpenseRegisterTest { @Test @DisplayName("addItem method throws exception when it should") void addItemThrows() { - Expense expense = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); expenseRegister.addItem(expense); assertThrows(IllegalArgumentException.class, () -> expenseRegister.addItem(expense)); } @@ -26,8 +28,8 @@ class ExpenseRegisterTest { @Test @DisplayName("addItem method does not throw exception when it should not") void addItemDoesNotThrow() { - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - Expense expense2 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, "02.03.23"); + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); + Expense expense2 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, LocalDate.of(2023, Month.MARCH, 3)); assertDoesNotThrow(() -> expenseRegister.addItem(expense1)); assertDoesNotThrow(() -> expenseRegister.addItem(expense2)); } @@ -39,16 +41,16 @@ class ExpenseRegisterTest { @Test @DisplayName("removeItem does throw exception when it should") void removeItemDoesThrow(){ - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - Expense notExpense1 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, "02.03.23"); + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); + Expense notExpense1 = new Expense("anotherDescription", 6.5f, true, ExpenseCategory.BOOKS, LocalDate.of(2023, Month.MARCH, 2)); expenseRegister.addItem(expense1); assertThrows(IllegalArgumentException.class, () -> expenseRegister.removeItem(notExpense1)); } @Test @DisplayName("removeItem method does not throw exception when it should not") void removeItemDoesNotThrow(){ - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - Expense expense1Copy = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); + Expense expense1Copy = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); expenseRegister.addItem(expense1); assertDoesNotThrow(() -> expenseRegister.removeItem(expense1Copy)); } @@ -64,9 +66,9 @@ class ExpenseRegisterTest { @BeforeEach void addExpensesToRegister() { - expense1 = new Expense("description1", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - expense2 = new Expense("description2", 62.4f, true, ExpenseCategory.FOOD, "01.02.21"); - expense3 = new Expense("description3", 9.81f, false, ExpenseCategory.CLOTHES, "05.07.23"); + expense1 = new Expense("description1", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); + expense2 = new Expense("description2", 62.4f, true, ExpenseCategory.FOOD, LocalDate.of(2021, Month.FEBRUARY, 1)); + expense3 = new Expense("description3", 9.81f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.JULY, 5)); expenseRegister.addItem(expense1); expenseRegister.addItem(expense2); diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java index 191c8513..60ea2208 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/ExpenseTest.java @@ -3,6 +3,9 @@ package no.ntnu.idatt1002.demo.data.Economics; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.time.Month; + import static org.junit.jupiter.api.Assertions.*; class ExpenseTest { @@ -10,14 +13,14 @@ class ExpenseTest { @Test @DisplayName("The Expense constructor throws exceptions when it should.") void constructorThrows(){ - assertThrows(IllegalArgumentException.class, () -> new Expense("description", 8.5f, false, null, "03.03.23")); - assertThrows(IllegalArgumentException.class, () -> new Expense("description", -10.0f, false, ExpenseCategory.BOOKS, "03.03.23")); + assertThrows(IllegalArgumentException.class, () -> new Expense("description", 8.5f, false, null, LocalDate.of(2023, Month.MARCH, 3))); + assertThrows(IllegalArgumentException.class, () -> new Expense("description", -10.0f, false, ExpenseCategory.BOOKS, LocalDate.of(2023, Month.MARCH, 3))); } @Test @DisplayName("The Expense constructor does not throw exceptions when it should not.") void constructorDoesThrow() { - assertDoesNotThrow(() -> new Expense("description", 59.9f, false, ExpenseCategory.BOOKS, "03.03.23")); + assertDoesNotThrow(() -> new Expense("description", 59.9f, false, ExpenseCategory.BOOKS, LocalDate.of(2023, Month.MARCH, 3))); } } \ No newline at end of file diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java index b2140dd6..70dce32b 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/FileHandlingTest.java @@ -3,6 +3,9 @@ package no.ntnu.idatt1002.demo.data.Economics; import org.junit.jupiter.api.*; import java.io.IOException; +import java.time.LocalDate; +import java.time.Month; + import static org.junit.jupiter.api.Assertions.*; class FileHandlingTest { @@ -23,7 +26,7 @@ class FileHandlingTest { @Test @DisplayName("isEmpty returns false if a file is not empty") void isEmptyReturnsFalseIfFileIsNotEmpty() throws IOException { - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); incomeRegister.addItem(income1); fileHandling.writeItemRegisterToFile(incomeRegister, "incomeRegisterTest"); assertFalse(fileHandling.isEmpty("incomeRegisterTest")); @@ -38,7 +41,7 @@ class FileHandlingTest { class fileHandlingIncomeRegisterWithIncomeThatHasDescription{ @BeforeEach void addIncomeToIncomeRegister(){ - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); incomeRegister.addItem(income1); } @Test @@ -58,7 +61,7 @@ class FileHandlingTest { class fileHandlingIncomeRegisterWithIncomeThatDoesNotHaveDescription{ @BeforeEach void addIncomeToIncomeRegister(){ - Income income1 = new Income(59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income1 = new Income(59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); incomeRegister.addItem(income1); } @Test @@ -78,9 +81,9 @@ class FileHandlingTest { class fileHandlingIncomeRegisterWithMultipleIncome{ @BeforeEach void addIncomeToIncomeRegister(){ - Income income1 = new Income(59.9f, false, IncomeCategory.GIFT, "03.03.23"); - Income income2 = new Income("description2", 62.4f, true, IncomeCategory.GIFT, "01.02.21"); - Income income3 = new Income("description3", 9.81f, false, IncomeCategory.SALARY, "05.07.23"); + Income income1 = new Income(59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); + Income income2 = new Income("description2", 62.4f, true, IncomeCategory.GIFT, LocalDate.of(2021, Month.FEBRUARY, 1)); + Income income3 = new Income("description3", 9.81f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.JULY, 5)); incomeRegister.addItem(income1); incomeRegister.addItem(income2); incomeRegister.addItem(income3); @@ -107,7 +110,7 @@ class FileHandlingTest { class fileHandlingExpenseRegisterWithExpenseThatHasDescription{ @BeforeEach void addExpenseToExpenseRegister(){ - Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense1 = new Expense("description", 59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); expenseRegister.addItem(expense1); } @Test @@ -127,7 +130,7 @@ class FileHandlingTest { class fileHandlingExpenseRegisterWithExpenseThatDoesNotHaveDescription{ @BeforeEach void addExpenseToExpenseRegister(){ - Expense expense1 = new Expense(59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); + Expense expense1 = new Expense(59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); expenseRegister.addItem(expense1); } @Test @@ -147,9 +150,9 @@ class FileHandlingTest { class fileHandlingExpenseRegisterWithMultipleExpensen{ @BeforeEach void addExpenseToExpenseRegister(){ - Expense expense1 = new Expense(59.9f, false, ExpenseCategory.CLOTHES, "03.03.23"); - Expense expense2 = new Expense("description2", 62.4f, true, ExpenseCategory.FOOD, "01.02.21"); - Expense expense3 = new Expense("description3", 9.81f, false, ExpenseCategory.CLOTHES, "05.07.23"); + Expense expense1 = new Expense(59.9f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.MARCH, 3)); + Expense expense2 = new Expense("description2", 62.4f, true, ExpenseCategory.FOOD, LocalDate.of(2021, Month.FEBRUARY, 1)); + Expense expense3 = new Expense("description3", 9.81f, false, ExpenseCategory.CLOTHES, LocalDate.of(2023, Month.JULY, 5)); expenseRegister.addItem(expense1); expenseRegister.addItem(expense2); diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java index 64d18f51..23380150 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeRegisterTest.java @@ -5,6 +5,8 @@ import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.time.Month; import java.util.List; import static org.junit.jupiter.api.Assertions.*; @@ -18,7 +20,7 @@ public class IncomeRegisterTest { @Test @DisplayName("addItem method throws exception when it should") void addItemThrows() { - Income income = new Income("description", 59.9f, false, IncomeCategory.SALARY, "03.03.23"); + Income income = new Income("description", 59.9f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 3)); incomeRegister.addItem(income); assertThrows(IllegalArgumentException.class, () -> incomeRegister.addItem(income)); } @@ -26,8 +28,8 @@ public class IncomeRegisterTest { @Test @DisplayName("addItem method does not throw exception when it should not") void addItemDoesNotThrow() { - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); - Income income2 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, "02.03.23"); + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); + Income income2 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 2)); assertDoesNotThrow(() -> incomeRegister.addItem(income1)); assertDoesNotThrow(() -> incomeRegister.addItem(income2)); } @@ -39,16 +41,16 @@ public class IncomeRegisterTest { @Test @DisplayName("removeItem does throw exception when it should") void removeItemDoesThrow(){ - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); - Income notIncome1 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, "02.03.23"); + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 3)); + Income notIncome1 = new Income("anotherDescription", 6.5f, true, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 2)); incomeRegister.addItem(income1); assertThrows(IllegalArgumentException.class, () -> incomeRegister.removeItem(notIncome1)); } @Test @DisplayName("removeItem method does not throw exception when it should not") void removeItemDoesNotThrow(){ - Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); - Income income1Copy = new Income("description", 59.9f, false, IncomeCategory.GIFT, "03.03.23"); + Income income1 = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 2)); + Income income1Copy = new Income("description", 59.9f, false, IncomeCategory.GIFT, LocalDate.of(2023, Month.MARCH, 2)); incomeRegister.addItem(income1); assertDoesNotThrow(() -> incomeRegister.removeItem(income1Copy)); } @@ -63,9 +65,9 @@ public class IncomeRegisterTest { @BeforeEach void addIncomeToRegister() { - income1 = new Income("description1", 59.9f, false, IncomeCategory.SALARY, "03.03.23"); - income2 = new Income("description2", 62.4f, true, IncomeCategory.GIFT, "01.02.21"); - income3 = new Income("description3", 9.81f, false, IncomeCategory.SALARY, "05.07.23"); + income1 = new Income("description1", 59.9f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 3)); + income2 = new Income("description2", 62.4f, true, IncomeCategory.GIFT,LocalDate.of(2021, Month.FEBRUARY, 1)); + income3 = new Income("description3", 9.81f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.JULY, 5)); incomeRegister.addItem(income1); incomeRegister.addItem(income2); incomeRegister.addItem(income3); diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeTest.java index 69302606..17cf1fb9 100644 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeTest.java +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Economics/IncomeTest.java @@ -1,10 +1,14 @@ package no.ntnu.idatt1002.demo.data.Economics; +import javafx.util.converter.LocalDateStringConverter; import no.ntnu.idatt1002.demo.data.Economics.Income; import no.ntnu.idatt1002.demo.data.Economics.IncomeCategory; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; +import java.time.LocalDate; +import java.time.Month; + import static org.junit.jupiter.api.Assertions.*; class IncomeTest { @@ -12,14 +16,14 @@ class IncomeTest { @Test @DisplayName("The Income constructor throws exceptions when it should.") void constructorThrows(){ - assertThrows(IllegalArgumentException.class, () -> new Income("description", 8.5f, false, null, "03.03.23")); - assertThrows(IllegalArgumentException.class, () -> new Income("description", -10.0f, false, IncomeCategory.SALARY, "03.03.23")); + assertThrows(IllegalArgumentException.class, () -> new Income("description", 8.5f, false, null, LocalDate.of(2023, Month.MARCH, 3))); + assertThrows(IllegalArgumentException.class, () -> new Income("description", -10.0f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 3))); }; @Test @DisplayName("The Income constructor does not throw exceptions when it should not.") void constructorDoesThrow() { - assertDoesNotThrow(() -> new Income("description", 59.9f, false, IncomeCategory.SALARY, "03.03.23")); + assertDoesNotThrow(() -> new Income("description", 59.9f, false, IncomeCategory.SALARY, LocalDate.of(2023, Month.MARCH, 3))); } } -- GitLab From 0f8de756f70fd2a3a584622813b43ee19a29a08f Mon Sep 17 00:00:00 2001 From: Andreas Kluge Svendsrud <andreksv@stud.ntnu.no> Date: Thu, 23 Mar 2023 14:24:38 +0100 Subject: [PATCH 062/103] Item, Income and Expense now use ObjectProperty for date and for Expense- and IncomeCategory --- .../demo/data/Economics/Expense.java | 18 +- .../idatt1002/demo/data/Economics/Income.java | 29 +++- .../idatt1002/demo/data/Economics/Item.java | 12 +- src/main/resources/Images/add_image 2.png | Bin 0 -> 3696 bytes src/main/resources/Images/arrow 2.png | Bin 0 -> 13233 bytes src/main/resources/Images/delete 2.png | Bin 0 -> 10682 bytes src/main/resources/Images/edit 2.png | Bin 0 -> 6425 bytes src/main/resources/view/MainMenu 2.fxml | 160 ++++++++++++++++++ 8 files changed, 199 insertions(+), 20 deletions(-) create mode 100644 src/main/resources/Images/add_image 2.png create mode 100644 src/main/resources/Images/arrow 2.png create mode 100644 src/main/resources/Images/delete 2.png create mode 100644 src/main/resources/Images/edit 2.png create mode 100644 src/main/resources/view/MainMenu 2.fxml diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java index da0f64d1..11848b02 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java @@ -1,10 +1,13 @@ package no.ntnu.idatt1002.demo.data.Economics; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + import java.time.LocalDate; import java.util.Objects; public class Expense extends Item{ - private ExpenseCategory category; + private ObjectProperty<ExpenseCategory> category; /** * This constructor uses the super constructor to set the fields for 'amount' and 'recurring' and then sets the @@ -22,7 +25,7 @@ public class Expense extends Item{ if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = category; + this.category = new SimpleObjectProperty<>(category); } /** @@ -39,8 +42,7 @@ public class Expense extends Item{ if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = category; - + this.category = new SimpleObjectProperty<>(category); } /** @@ -48,7 +50,7 @@ public class Expense extends Item{ * @return The category the Expense belongs to as a value of the ExpenseCategory enum class. */ public ExpenseCategory getCategory() { - return category; + return category.get(); } /** @@ -59,7 +61,7 @@ public class Expense extends Item{ if(expenseCategory == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = expenseCategory; + this.category.set(expenseCategory); } /** @@ -69,7 +71,7 @@ public class Expense extends Item{ */ @Override public String toString() { - return super.toString()+"category="+category.toString()+"\n\n"; + return super.toString()+"category="+category.get()+"\n\n"; } @Override @@ -77,7 +79,7 @@ public class Expense extends Item{ if (this == o) return true; if (!(o instanceof Expense expense)) return false; if (!super.equals(o)) return false; - return category == expense.category; + return category.get() == expense.category.get(); } @Override diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java index d4f4db57..904ab935 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java @@ -1,6 +1,10 @@ package no.ntnu.idatt1002.demo.data.Economics; +import javafx.beans.property.ObjectProperty; +import javafx.beans.property.SimpleObjectProperty; + import java.time.LocalDate; +import java.util.Objects; /** * The Income class inherits from the Item class. The Item class additionally has a private field for an @@ -10,7 +14,7 @@ import java.time.LocalDate; */ public class Income extends Item{ - private IncomeCategory category; + private ObjectProperty<IncomeCategory> category; /** * This constructor uses the super constructor to set the fields for 'amount' and 'recurring' and then sets the @@ -28,7 +32,7 @@ public class Income extends Item{ if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = category; + this.category = new SimpleObjectProperty<>(category); } /** @@ -45,7 +49,7 @@ public class Income extends Item{ if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = category; + this.category = new SimpleObjectProperty<>(category); } @@ -54,7 +58,7 @@ public class Income extends Item{ * @return The category to which the Income belongs to as a value of the IncomeCategory enum. */ public IncomeCategory getCategory() { - return category; + return category.get(); } /** @@ -65,7 +69,7 @@ public class Income extends Item{ if(category == null) { throw new IllegalArgumentException("The income must belong to a category."); } - this.category = category; + this.category.set(category); } /** @@ -75,6 +79,19 @@ public class Income extends Item{ */ @Override public String toString() { - return super.toString()+"category="+category.toString()+"\n\n"; + return super.toString()+"category="+category.get()+"\n\n"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof Income income)) return false; + if (!super.equals(o)) return false; + return Objects.equals(category.get(), income.category.get()); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), category); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 53c8c81b..944fefff 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -16,7 +16,7 @@ public abstract class Item { private StringProperty description = new SimpleStringProperty(""); private final DoubleProperty amount; private BooleanProperty recurring; - private LocalDate date; // Format example: 09.08.23 + private ObjectProperty<LocalDate> date; // Format example: 09.08.23 /** * The constructor of a new Item object takes in an amount as a double. If the amount is left blank, an @@ -29,7 +29,7 @@ public abstract class Item { } else { this.amount = new SimpleDoubleProperty(amount); this.recurring = new SimpleBooleanProperty(recurring); - this.date = date; + this.date = new SimpleObjectProperty<>(date); } } @@ -122,7 +122,7 @@ public abstract class Item { * @return The date of the transaction. */ public LocalDate getDate() { - return this.date; + return this.date.get(); } /** @@ -131,7 +131,7 @@ public abstract class Item { * @param newDate The new date with which to record the Item. */ public void setDate(LocalDate newDate) { - this.date = newDate; + this.date.set(newDate); } @Override @@ -160,10 +160,10 @@ public abstract class Item { isReoccuring = "Not reoccurring"; } if(!description.get().isBlank()){ - return "date=" + date.toString() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date=" + date.get() +"\ndescription=" + description.get()+"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } else{ - return "date="+date.toString() +"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; + return "date="+date.get() +"\namount="+amount.get()+"\nisReoccuring="+isReoccuring+"\n"; } } diff --git a/src/main/resources/Images/add_image 2.png b/src/main/resources/Images/add_image 2.png new file mode 100644 index 0000000000000000000000000000000000000000..f8fd29245588cf7264711402a4610b4a23e3bfeb GIT binary patch literal 3696 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/arrow 2.png b/src/main/resources/Images/arrow 2.png new file mode 100644 index 0000000000000000000000000000000000000000..9d65c03b2a4d3710c674655f1a3201935735c9f5 GIT binary patch literal 13233 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/delete 2.png b/src/main/resources/Images/delete 2.png new file mode 100644 index 0000000000000000000000000000000000000000..b23937817cfa4976c74f73288a6c34451934f16b GIT binary patch literal 10682 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/Images/edit 2.png b/src/main/resources/Images/edit 2.png new file mode 100644 index 0000000000000000000000000000000000000000..833b336008545736084b8b7178591740a5bd7ac7 GIT binary patch literal 6425 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/view/MainMenu 2.fxml b/src/main/resources/view/MainMenu 2.fxml new file mode 100644 index 00000000..109698ac --- /dev/null +++ b/src/main/resources/view/MainMenu 2.fxml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.Cursor?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.DatePicker?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.ProgressBar?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.BorderPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Region?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> + +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.MainMenuController"> + <children> + <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + </ImageView> + <BorderPane prefHeight="400.0" prefWidth="600.0" AnchorPane.topAnchor="0.0"> + <center> + <GridPane prefWidth="574.0" BorderPane.alignment="CENTER"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox alignment="BOTTOM_RIGHT" GridPane.columnSpan="2"> + <children> + <DatePicker fx:id="date" /> + <HBox alignment="BOTTOM_CENTER" prefHeight="28.0" prefWidth="574.0"> + <children> + <Label text="5000kr left" textAlignment="CENTER"> + <font> + <Font name="System Bold" size="12.0" /> + </font></Label> + </children> + </HBox> + </children> + </VBox> + <StackPane GridPane.columnSpan="2" GridPane.rowIndex="1"> + <children> + <ImageView fx:id="progressMarker" fitHeight="20.0" fitWidth="25.0" pickOnBounds="true" preserveRatio="true" rotate="-90.0" translateX="-150.0" translateY="25.0"> + <image> + <Image url="@../Images/arrow.png" /> + </image> + </ImageView> + <ProgressBar fx:id="progressbar" prefHeight="40.0" prefWidth="554.0" progress="0.72" translateY="-10.0" /> + <Label fx:id="today" text="Today" textAlignment="CENTER" translateX="-150.0" translateY="-2.0"> + <font> + <Font name="System Bold" size="12.0" /> + </font> + </Label> + </children> + </StackPane> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="50.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> + <children> + <Button fx:id="foodButton" contentDisplay="TOP" mnemonicParsing="false" prefHeight="125.0" prefWidth="119.0" text="Food"> + <graphic> + <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/pizzaslice.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button fx:id="addExpenseButton" contentDisplay="TOP" mnemonicParsing="false" onAction="#switchExpenses" prefHeight="125.0" prefWidth="125.0" text="Add expense"> + <graphic> + <ImageView fitHeight="79.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/add_image.png" /> + </image> + </ImageView> + </graphic> + </Button> + <Button fx:id="overviewButton" contentDisplay="TOP" mnemonicParsing="false" onAction="#switchOverview" prefHeight="125.0" prefWidth="125.0" text="Overview"> + <graphic> + <ImageView fitHeight="63.0" fitWidth="87.0" pickOnBounds="true"> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + <image> + <Image url="@../Images/overview_stonks.png" /> + </image> + </ImageView> + </graphic> + </Button> + </children> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <padding> + <Insets top="20.0" /> + </padding> + </HBox> + </children> + </GridPane> + </center> + <bottom> + <Region prefHeight="55.0" prefWidth="600.0" BorderPane.alignment="CENTER" /> + </bottom> + <left> + <Region prefHeight="287.0" prefWidth="14.0" BorderPane.alignment="CENTER" /> + </left> + <right> + <Region prefHeight="287.0" prefWidth="12.0" BorderPane.alignment="CENTER" /> + </right> + <top> + <HBox prefHeight="51.0" prefWidth="600.0" BorderPane.alignment="CENTER"> + <children> + <Button mnemonicParsing="false" text="Return "> + <opaqueInsets> + <Insets left="100.0" /> + </opaqueInsets> + <HBox.margin> + <Insets left="10.0" top="10.0" /> + </HBox.margin> + </Button> + <Region prefHeight="70.0" prefWidth="103.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="BUDGET FEBRUARY" textAlignment="CENTER"> + <HBox.margin> + <Insets /> + </HBox.margin> + <font> + <Font size="30.0" /> + </font> + </Text> + </children> + <opaqueInsets> + <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" /> + </opaqueInsets> + </HBox> + </top> + </BorderPane> + </children> +</AnchorPane> -- GitLab From 324148ea65f43ad467f4ee6568a9826a52e4796a Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Thu, 23 Mar 2023 14:27:56 +0100 Subject: [PATCH 063/103] Fixed equals method --- src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 944fefff..41121f7a 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -138,7 +138,7 @@ public abstract class Item { public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof Item item)) return false; - return Double.compare(item.amount.get(), amount.get()) == 0 && item.recurring.get() == recurring.get() && Objects.equals(description.get(), item.description.get()) && Objects.equals(date, item.date); + return Double.compare(item.amount.get(), amount.get()) == 0 && item.recurring.get() == recurring.get() && Objects.equals(description.get(), item.description.get()) && Objects.equals(date.get(), item.date.get()); } @Override -- GitLab From d79b8e1c30dd8b9235acbb1ee2ce12dffd6ac381 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 14:42:08 +0100 Subject: [PATCH 064/103] Refactored edit method, such that expenses are not edited if cancel button is hit. Delete button will now not do anything unless expense is selected --- .../demo/controller/AddExpenseController.java | 39 ++++++++++++++----- .../demo/controller/ExpensesController.java | 8 ++-- 2 files changed, 34 insertions(+), 13 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 23682f82..0a0edcfc 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -69,22 +69,41 @@ public class AddExpenseController { } public void setExpense(Expense expense) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED - dateField.textProperty().bindBidirectional(new SimpleStringProperty(expense.getDate().toString())); - amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED + chosenExpense = new Expense(expense.getDescription(), expense.getAmount(), expense.isRecurring(), expense.getCategory(), expense.getDate()); + chosenExpense.descriptionProperty().bindBidirectional(expense.descriptionProperty()); + chosenExpense.amountProperty().bindBidirectional(expense.amountProperty()); + chosenExpense.recurringProperty().bindBidirectional(expense.recurringProperty()); + //chosenExpense.().bindBidirectional(expense.descriptionProperty()); + //chosenExpense.().bindBidirectional(expense.descriptionProperty()); + descriptionField.textProperty().set(expense.getDescription()); + amountField.textProperty().setValue(String.valueOf(expense.getAmount())); + recurringBox.setValue(expense.isRecurring()); + + + //new SimpleStringProperty(datePicker.getValue().toString()).bindBidirectional(new SimpleStringProperty(expense.getDate().toString())); + //Bind this expense's fields with the argument object's fields. If cancel is pressed - do nothing. If ok is pressed, bind the textfields with this expsense + + /*amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); //categoryBox.valueProperty().bindBidirectional(expense.getCategory()); - recurringBox.valueProperty().bindBidirectional(expense.recurringProperty()); + recurringBox.valueProperty().bindBidirectional(expense.recurringProperty());*/ } @FXML public void pressOkBtn(ActionEvent event) { - LocalDate date = LocalDate.parse(dateField.getText()); - double amount = Double.parseDouble(amountField.getText()); - String description = descriptionField.getText(); - ExpenseCategory category = getCategory(); - boolean recurring = isRecurring(); - newExpense = new Expense(description, amount, recurring, category, date); - System.out.println(date + " " + amount + " " + description + " " + category + " " + recurring); + if (newExpense == null) { + LocalDate date = datePicker.getValue(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + ExpenseCategory category = getCategory(); + boolean recurring = isRecurring(); + newExpense = new Expense(description, amount, recurring, category, date); + } + if (chosenExpense != null) { + chosenExpense.setDescription((descriptionField.getText())); + chosenExpense.setAmount(Double.parseDouble(amountField.getText())); + chosenExpense.setRecurring(recurringBox.getValue()); + } final Node source = (Node) event.getSource(); ((Stage) source.getScene().getWindow()).close(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 83a74789..9f22b340 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -160,10 +160,12 @@ public class ExpensesController { @FXML public void handleDeleteBtn(ActionEvent event) { - Optional<ButtonType> isConfirmed = showConfirmationDialog( - ); + Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); + if (chosenExpense == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { - Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); expenseRegister.removeItem(chosenExpense); refreshObservableList(); } -- GitLab From 62b6bbdf3446fcf964879ef71e2f5606e85983a0 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 17:02:48 +0100 Subject: [PATCH 065/103] Deleted testfiles --- src/main/resources/Economics/expenseRegisterTest.register | 6 ------ src/main/resources/Economics/incomeRegisterTest.register | 0 2 files changed, 6 deletions(-) delete mode 100644 src/main/resources/Economics/expenseRegisterTest.register delete mode 100644 src/main/resources/Economics/incomeRegisterTest.register diff --git a/src/main/resources/Economics/expenseRegisterTest.register b/src/main/resources/Economics/expenseRegisterTest.register deleted file mode 100644 index 84a96af7..00000000 --- a/src/main/resources/Economics/expenseRegisterTest.register +++ /dev/null @@ -1,6 +0,0 @@ -date=2023-03-03 -description=description -amount=59.900001525878906 -isReoccuring=Not reoccurring -category=CLOTHES - diff --git a/src/main/resources/Economics/incomeRegisterTest.register b/src/main/resources/Economics/incomeRegisterTest.register deleted file mode 100644 index e69de29b..00000000 -- GitLab From d1e34b3a7f0bcbd4aac3b4048be3a3819e7ff069 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 17:58:11 +0100 Subject: [PATCH 066/103] "Date inputs are now in the form of a datepicker calendar" --- src/main/resources/view/AddExpense.fxml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/resources/view/AddExpense.fxml b/src/main/resources/view/AddExpense.fxml index db41d454..4fda6662 100644 --- a/src/main/resources/view/AddExpense.fxml +++ b/src/main/resources/view/AddExpense.fxml @@ -47,8 +47,7 @@ <Insets top="20.0" /> </GridPane.margin> </HBox> - <DatePicker fx:id="datePicker" GridPane.columnIndex="2" /> - <TextField fx:id="dateField" GridPane.columnIndex="1" /> + <DatePicker fx:id="datePicker" GridPane.columnIndex="1" /> </children> </GridPane> </content> -- GitLab From 41365cc22a8baf5aaaf33bff7e519f2e9fe53941 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Wed, 22 Mar 2023 17:59:33 +0100 Subject: [PATCH 067/103] "Removed adding expense to register in initialize. Instead the register is loaded from file" --- .../ntnu/idatt1002/demo/controller/ExpensesController.java | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 9f22b340..8eb5bb07 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -87,8 +87,7 @@ public class ExpensesController { show.setItems(filter); show.setValue("All"); - Expense newExpense = new Expense(99, true, ExpenseCategory.FOOD, LocalDate.of(2023, Month.JULY, 5)); - expenseRegister.addItem(newExpense); + expenseRegister = loadDataFromFile("Expense"); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); @@ -148,9 +147,7 @@ public class ExpensesController { expenseRegister.addItem(newExpense); refreshObservableList(); } else { - System.out.println("expense is null or dialog mode is add"); } - expenseTableView.refresh(); } protected void refreshObservableList() { -- GitLab From db3fc169d07abeb0841c27a687af91494a960283 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 15:36:56 +0100 Subject: [PATCH 068/103] Added other options to filter expensetable --- .../no/ntnu/idatt1002/demo/controller/ExpensesController.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 8eb5bb07..3bba5767 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -83,7 +83,7 @@ public class ExpensesController { @FXML public void initialize() throws IOException { - ObservableList<String> filter = FXCollections.observableArrayList("All", "Other", "Food"); + ObservableList<String> filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", "Fixed expense"); show.setItems(filter); show.setValue("All"); -- GitLab From ad8afd74c368dd2dfccd4ee03799a12bf47ea50e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 15:41:11 +0100 Subject: [PATCH 069/103] Redesigned expense window button bar below table. Reordered the methods in the ExpenseController --- .../idatt1002/demo/controller/ExpensesController.java | 11 ++++++----- src/main/resources/view/Expenses.fxml | 8 ++------ 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 3bba5767..23406770 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -150,11 +150,6 @@ public class ExpensesController { } } - protected void refreshObservableList() { - this.expenses.setAll(expenseRegister.getItems()); - - } - @FXML public void handleDeleteBtn(ActionEvent event) { Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); @@ -168,6 +163,12 @@ public class ExpensesController { } } + protected void refreshObservableList() { + this.expenses.setAll(expenseRegister.getItems()); + + } + + private Optional<ButtonType> showConfirmationDialog() { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Confirm Delete"); diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 400e0084..1846356e 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -119,14 +119,10 @@ </VBox> <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button fx:id="overviewBtn" mnemonicParsing="false" onAction="#switchScene" text="Overview"> - <HBox.margin> - <Insets right="5.0" /> - </HBox.margin></Button> <Button fx:id="incomeBtn" mnemonicParsing="false" onAction="#switchScene" text="Income" /> + <Button mnemonicParsing="false" text="Budget" /> <Button disable="true" mnemonicParsing="false" text="Expenses" /> - <Button disable="true" mnemonicParsing="false" text="Savings" /> - <Button disable="true" mnemonicParsing="false" onAction="#switchScene" text="Next"> + <Button mnemonicParsing="false" onAction="#switchScene" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin></Button> -- GitLab From 89400b3069e1dcc9fe8353e9f11398e807026e11 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 16:05:13 +0100 Subject: [PATCH 070/103] Redesigned income register button bar to contain income, budget and expense --- src/main/resources/view/Income.fxml | 33 ++++++++++++----------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/src/main/resources/view/Income.fxml b/src/main/resources/view/Income.fxml index 090983df..d7ae545f 100644 --- a/src/main/resources/view/Income.fxml +++ b/src/main/resources/view/Income.fxml @@ -33,7 +33,7 @@ <top> <HBox BorderPane.alignment="CENTER"> <children> - <Button mnemonicParsing="false" onAction="#switchStartMenu" text="Return "> + <Button fx:id="returnBtn" mnemonicParsing="false" onAction="#switchScene" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addIncome" text="Add" textAlignment="CENTER"> + <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -82,7 +82,7 @@ </ImageView> </graphic> </Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> + <Button fx:id="editBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -91,7 +91,7 @@ </ImageView> </graphic> </Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> + <Button fx:id="deleteBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleDeleteBtn" text="Delete" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -122,15 +122,10 @@ </VBox> <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button mnemonicParsing="false" onAction="#switchOverview" text="Overview"> - <HBox.margin> - <Insets right="5.0" /> - </HBox.margin> - </Button> - <Button disable="true" mnemonicParsing="false" onAction="#switchIncome" text="Income" /> - <Button mnemonicParsing="false" onAction="#switchExpenses" text="Expenses" /> - <Button disable="true" mnemonicParsing="false" text="Savings" /> - <Button mnemonicParsing="false" onAction="#switchExpenses" text="Next"> + <Button disable="true" mnemonicParsing="false" text="Income" /> + <Button fx:id="budgetBtn" mnemonicParsing="false" onAction="#switchScene" text="Budget" /> + <Button mnemonicParsing="false" text="Expenses" /> + <Button mnemonicParsing="false" onAction="#switchScene" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin> @@ -140,13 +135,13 @@ <Insets top="10.0" /> </padding> </HBox> - <TableView fx:id="expenseTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> + <TableView fx:id="incomeTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> <columns> - <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> + <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> -- GitLab From eb7e87918e32761effd92ebdd7969c421e2d4add Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 16:05:36 +0100 Subject: [PATCH 071/103] Started refactoring expensecontroller into both income and expense controller --- .../demo/controller/ExpensesController.java | 27 ++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 23406770..0c069860 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -30,6 +30,8 @@ import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; import no.ntnu.idatt1002.demo.data.Economics.FileHandling; +import no.ntnu.idatt1002.demo.data.Economics.Income; +import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; enum DialogMode { ADD, EDIT, DELETE @@ -78,11 +80,18 @@ public class ExpensesController { @FXML private TableView<Expense> expenseTableView; + @FXML + private TableView<Expense> incomeTableView; + ExpenseRegister expenseRegister; ObservableList<Expense> expenses; + IncomeRegister incomeRegister; + ObservableList<Income> income; + @FXML - public void initialize() throws IOException { + public void initialize() throws IOException { //TODO SAME REGISTER FOR BOTH, BUT LOAD DIFFERENT DATA DEPENDING ON WHICH IT IS + if (expenseTableView.getColumns() != null) { ObservableList<String> filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", "Fixed expense"); show.setItems(filter); show.setValue("All"); @@ -97,6 +106,22 @@ public class ExpensesController { recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); expenseTableView.setItems(expenses); + } else { + ObservableList<String> filter = FXCollections.observableArrayList("All", "Salary", "Student loan", "Gift", "Fixed expense"); + show.setItems(filter); + show.setValue("All"); + + expenseRegister = loadDataFromFile("Income"); + expenses = FXCollections.observableArrayList(expenseRegister.getItems()); + + dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); + amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); + categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); + descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); + recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); + + expenseTableView.setItems(expenses); + } } @FXML -- GitLab From cc7401e24d32e52fff017bba40a3996e4e153ee8 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Thu, 23 Mar 2023 18:23:34 +0100 Subject: [PATCH 072/103] Attempted to make expense and income the same class. Made new dialog box for income. Made new enum to distinguish between incoem and expense --- .../demo/controller/AddExpenseController.java | 12 +- .../demo/controller/AddIncomeController.java | 124 ++++++++++++ .../demo/controller/ExpensesController.java | 184 ++++++++++++------ src/main/resources/view/AddIncome.fxml | 89 ++++----- 4 files changed, 301 insertions(+), 108 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 0a0edcfc..21d8bc4f 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -17,10 +17,18 @@ import javafx.scene.control.TextField; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import no.ntnu.idatt1002.demo.data.Economics.Income; + public class AddExpenseController { Expense newExpense = null; //the expense that is chosen when editing or the expense that is created when adding Expense chosenExpense = null; //an expense that is meant to track the old state of an expense before editing, in case cancel bugtton is clicked + + Income newIncome = null; + + Income chosenIncome = null; + + private ItemMode itemMode; @FXML private Button cancelBtn; @@ -55,9 +63,6 @@ public class AddExpenseController { ObservableList<Boolean> recurring = FXCollections.observableArrayList(true, false); recurringBox.setItems(recurring); recurringBox.setValue(false); - - System.out.print("This is in initialize"); - System.out.println(descriptionField); } public ExpenseCategory getCategory() { @@ -79,7 +84,6 @@ public class AddExpenseController { amountField.textProperty().setValue(String.valueOf(expense.getAmount())); recurringBox.setValue(expense.isRecurring()); - //new SimpleStringProperty(datePicker.getValue().toString()).bindBidirectional(new SimpleStringProperty(expense.getDate().toString())); //Bind this expense's fields with the argument object's fields. If cancel is pressed - do nothing. If ok is pressed, bind the textfields with this expsense diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java new file mode 100644 index 00000000..dc677df6 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java @@ -0,0 +1,124 @@ +package no.ntnu.idatt1002.demo.controller; + +import java.text.NumberFormat; +import java.time.LocalDate; + +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.DatePicker; +import javafx.scene.control.TextField; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Economics.Expense; +import no.ntnu.idatt1002.demo.data.Economics.Income; +import no.ntnu.idatt1002.demo.data.Economics.IncomeCategory; + +public class AddIncomeController { + + Income newIncome = null; + + Income chosenIncome = null; + + private ItemMode itemMode; + @FXML + private Button cancelBtn; + + @FXML + private Button okBtn; + + @FXML + private TextField dateField; + + @FXML + private DatePicker datePicker; + + @FXML + private TextField descriptionField; + + @FXML + private TextField amountField; + + @FXML + private ComboBox<IncomeCategory> categoryBox; + + @FXML + private ComboBox<Boolean> recurringBox; + + @FXML + public void initialize() { + ObservableList<IncomeCategory> incomeCategories = FXCollections.observableArrayList( + IncomeCategory.values()); + categoryBox.setItems(incomeCategories); + categoryBox.setValue(IncomeCategory.GIFT); + + ObservableList<Boolean> recurring = FXCollections.observableArrayList(true, false); + recurringBox.setItems(recurring); + recurringBox.setValue(false); + } + + public IncomeCategory getCategory() { + return categoryBox.getValue(); + } + + public boolean isRecurring() { + return recurringBox.getValue();//.equals("Yes"); + } + + public void setIncome(Income income) { //TODO NEED CANCEL BUTTON TO REMOVE THE CHANGES IF CANCEL IS PRESSED + chosenIncome = new Income(income.getDescription(), income.getAmount(), income.isRecurring(), income.getCategory(), income.getDate()); + chosenIncome.descriptionProperty().bindBidirectional(income.descriptionProperty()); + chosenIncome.amountProperty().bindBidirectional(income.amountProperty()); + chosenIncome.recurringProperty().bindBidirectional(income.recurringProperty()); + //chosenExpense.().bindBidirectional(income.descriptionProperty()); + //chosenExpense.().bindBidirectional(income.descriptionProperty()); + descriptionField.textProperty().set(income.getDescription()); + amountField.textProperty().setValue(String.valueOf(income.getAmount())); + recurringBox.setValue(income.isRecurring()); + + //new SimpleStringProperty(datePicker.getValue().toString()).bindBidirectional(new SimpleStringProperty(income.getDate().toString())); + //Bind this income's fields with the argument object's fields. If cancel is pressed - do nothing. If ok is pressed, bind the textfields with this expsense + + /*amountField.textProperty().bindBidirectional(income.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED + descriptionField.textProperty().bindBidirectional(income.descriptionProperty()); + //categoryBox.valueProperty().bindBidirectional(income.getCategory()); + recurringBox.valueProperty().bindBidirectional(income.recurringProperty());*/ + } + + @FXML + public void pressOkBtn(ActionEvent event) { + if (chosenIncome == null) { + LocalDate date = datePicker.getValue(); + double amount = Double.parseDouble(amountField.getText()); + String description = descriptionField.getText(); + IncomeCategory category = getCategory(); + boolean recurring = isRecurring(); + newIncome = new Income(description, amount, recurring, category, date); + } + if (chosenIncome != null) { + chosenIncome.setDescription((descriptionField.getText())); + chosenIncome.setAmount(Double.parseDouble(amountField.getText())); + chosenIncome.setRecurring(recurringBox.getValue()); + } + + final Node source = (Node) event.getSource(); + ((Stage) source.getScene().getWindow()).close(); + } + + @FXML + public void pressCancelBtn(ActionEvent event) { + final Node source = (Node) event.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + + stage.close(); + } + + public Income getNewIncome() { + return this.chosenIncome; + } +} \ No newline at end of file diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 0c069860..10828491 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -1,10 +1,7 @@ package no.ntnu.idatt1002.demo.controller; -import java.io.File; import java.io.IOException; -import java.time.LocalDate; -import java.time.Month; import java.util.Optional; import javafx.collections.FXCollections; import javafx.collections.ObservableList; @@ -22,7 +19,6 @@ import javafx.scene.control.ComboBox; import javafx.scene.control.Dialog; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; -import javafx.scene.control.TextField; import javafx.scene.control.cell.PropertyValueFactory; import javafx.stage.Modality; import javafx.stage.Stage; @@ -32,17 +28,25 @@ import no.ntnu.idatt1002.demo.data.Economics.ExpenseRegister; import no.ntnu.idatt1002.demo.data.Economics.FileHandling; import no.ntnu.idatt1002.demo.data.Economics.Income; import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; +import no.ntnu.idatt1002.demo.data.Economics.Item; +import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; enum DialogMode { ADD, EDIT, DELETE } + +enum ItemMode { + INCOME, EXPENSE +} + public class ExpensesController { /** * The mode of the dialog. NEW if new contact, EDIT if edit existing contact. */ - private DialogMode mode; + private DialogMode dialogMode; + private ItemMode itemMode; @FXML private Button addBtn; @FXML @@ -81,47 +85,40 @@ public class ExpensesController { private TableView<Expense> expenseTableView; @FXML - private TableView<Expense> incomeTableView; + private TableView<Income> incomeTableView; ExpenseRegister expenseRegister; ObservableList<Expense> expenses; + ObservableList<String> filter; + + //ItemRegister itemRegister; IncomeRegister incomeRegister; ObservableList<Income> income; @FXML public void initialize() throws IOException { //TODO SAME REGISTER FOR BOTH, BUT LOAD DIFFERENT DATA DEPENDING ON WHICH IT IS - if (expenseTableView.getColumns() != null) { - ObservableList<String> filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", "Fixed expense"); - show.setItems(filter); - show.setValue("All"); - - expenseRegister = loadDataFromFile("Expense"); - expenses = FXCollections.observableArrayList(expenseRegister.getItems()); - dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - expenseTableView.setItems(expenses); - } else { - ObservableList<String> filter = FXCollections.observableArrayList("All", "Salary", "Student loan", "Gift", "Fixed expense"); - show.setItems(filter); - show.setValue("All"); - - expenseRegister = loadDataFromFile("Income"); + if (expenseTableView != null) { + itemMode = ItemMode.EXPENSE; + filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", "Fixed expense"); + expenseRegister = loadExpenseDataFromFile("Expense"); expenses = FXCollections.observableArrayList(expenseRegister.getItems()); - - dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); - amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); - categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); - descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); - recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - expenseTableView.setItems(expenses); + } else { + itemMode = ItemMode.INCOME; + filter = FXCollections.observableArrayList("All", "Salary", "Student loan", "Gift", "Fixed expense"); + incomeRegister = loadIncomeDataFromFile("Income"); + income = FXCollections.observableArrayList(incomeRegister.getItems()); + incomeTableView.setItems(income); } + show.setItems(filter); + show.setValue("All"); } @FXML @@ -130,10 +127,16 @@ public class ExpensesController { } @FXML protected void handleEditButton(ActionEvent event) { + FXMLLoader loader = new FXMLLoader(); + if (itemMode == ItemMode.EXPENSE) { + loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); + } else { + loader.setLocation(getClass().getResource("/view/AddIncome.fxml")); + } Expense newExpense = null; + Income newIncome = null; String dialogTitle = ""; // Load the FXML file for your dialog box - FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/AddExpense.fxml")); Dialog<Expense> dialog = new Dialog<>(); dialog.initModality(Modality.APPLICATION_MODAL); @@ -146,51 +149,88 @@ public class ExpensesController { // Get the controller for the loaded FXML file AddExpenseController dialogController = loader.getController(); + AddIncomeController dialogController1 = loader.getController(); - if (event.getSource().equals(addBtn)) { - mode = DialogMode.ADD; - dialogTitle = "Add expense"; - + if (itemMode == ItemMode.EXPENSE) { + if (event.getSource().equals(addBtn)) { + dialogMode = DialogMode.ADD; + dialogTitle = "Add expense"; } else if (event.getSource().equals(editBtn) && expenseTableView.getSelectionModel().getSelectedItem() != null) { - mode = DialogMode.EDIT; - dialogTitle = "Edit expense"; - newExpense = expenseTableView.getSelectionModel().getSelectedItem(); - dialogController.setExpense(newExpense); + dialogMode = DialogMode.EDIT; + dialogTitle = "Edit expense"; + newExpense = expenseTableView.getSelectionModel().getSelectedItem(); + dialogController.setExpense(newExpense); + } else { + return; + } } else { - return; + if (event.getSource().equals(addBtn)) { + dialogMode = DialogMode.ADD; + dialogTitle = "Income"; + } else if (event.getSource().equals(editBtn) && incomeTableView.getSelectionModel().getSelectedItem() != null) { + dialogMode = DialogMode.EDIT; + dialogTitle = "Edit income"; + newIncome = incomeTableView.getSelectionModel().getSelectedItem(); + dialogController1.setIncome(newIncome); //TODO NEED TO FXI THIS TO INCOME + } else { + return; + } } dialog.setTitle(dialogTitle); // Show the Dialog and wait for the user to close it - dialog.showAndWait(); //Get the newly created expense from the dialog pane - newExpense = dialogController.getNewExpense(); - //Only add the expense to the tableview, if the expense is not null - if (newExpense != null && mode == DialogMode.ADD) { - expenseRegister.addItem(newExpense); - refreshObservableList(); + + if (itemMode == ItemMode.EXPENSE) { + newExpense = dialogController.getNewExpense(); + if (newExpense != null && dialogMode == DialogMode.ADD) { + expenseRegister.addItem(newExpense); + refreshObservableList(); + } } else { + newIncome = dialogController1.getNewIncome(); //TODO NEED TO FIX to newIncome + if (newIncome != null && dialogMode == DialogMode.ADD) { //TODO NEED TO FIX TO NEW INCOME + incomeRegister.addItem(newIncome); //TODO NEED TO FIX TO NEW INCOME + refreshObservableList(); + } + } + //Only add the expense to the tableview, if the expense is not null } @FXML public void handleDeleteBtn(ActionEvent event) { - Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); - if (chosenExpense == null) { - return; - } - Optional<ButtonType> isConfirmed = showConfirmationDialog(); - if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { - expenseRegister.removeItem(chosenExpense); - refreshObservableList(); + if (itemMode == ItemMode.EXPENSE) { + Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); + if (chosenExpense == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + expenseRegister.removeItem(chosenExpense); + refreshObservableList(); + } + } else { + Income chosenIncome = incomeTableView.getSelectionModel().getSelectedItem(); + if (chosenIncome == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + incomeRegister.removeItem(chosenIncome); + refreshObservableList(); + } } } protected void refreshObservableList() { - this.expenses.setAll(expenseRegister.getItems()); - + if (itemMode == ItemMode.EXPENSE) { + this.expenses.setAll(expenseRegister.getItems()); + } else { + this.income.setAll(incomeRegister.getItems()); + } } @@ -203,7 +243,8 @@ public class ExpensesController { return alert.showAndWait(); } - public ExpenseRegister loadDataFromFile(String fileName) throws IOException { + public ExpenseRegister loadExpenseDataFromFile(String fileName) throws IOException { + //ItemRegister<T extends Item> FileHandling fileHandling = new FileHandling(); if (fileHandling.isEmpty(fileName)) { expenseRegister = new ExpenseRegister(); @@ -217,14 +258,39 @@ public class ExpensesController { return expenseRegister; } + public IncomeRegister loadIncomeDataFromFile(String fileName) throws IOException { + FileHandling fileHandling = new FileHandling(); + if (fileHandling.isEmpty(fileName)) { + incomeRegister = new IncomeRegister(); + } else { + try { + incomeRegister = fileHandling.readIncomeRegisterFromFile(fileName); + } catch (IOException e) { + e.printStackTrace(); + } + } + return incomeRegister; + } + + + public void saveDataToFile(String fileName) throws IOException { FileHandling fileHandling = new FileHandling(); - fileHandling.writeItemRegisterToFile(expenseRegister, fileName); + if (itemMode == ItemMode.EXPENSE) { + fileHandling.writeItemRegisterToFile(expenseRegister, fileName); } + else { + fileHandling.writeItemRegisterToFile(incomeRegister, fileName); } } @FXML public void switchScene(ActionEvent event) throws IOException { - saveDataToFile("Expense"); + if (itemMode == ItemMode.EXPENSE) { + saveDataToFile("Expense"); + } + else { + saveDataToFile("Income"); + } + FXMLLoader loader = new FXMLLoader(); if (event.getSource() == incomeBtn) { loader.setLocation(SceneController.class.getResource("/view/Income.fxml")); @@ -233,10 +299,12 @@ public class ExpensesController { } else if (event.getSource() == returnBtn) { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); } + Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); stage.setScene(scene); stage.show(); + } } -} + diff --git a/src/main/resources/view/AddIncome.fxml b/src/main/resources/view/AddIncome.fxml index 097992b0..be30573f 100644 --- a/src/main/resources/view/AddIncome.fxml +++ b/src/main/resources/view/AddIncome.fxml @@ -3,55 +3,52 @@ <?import javafx.geometry.Insets?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.DatePicker?> +<?import javafx.scene.control.DialogPane?> <?import javafx.scene.control.Label?> <?import javafx.scene.control.TextField?> <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.RowConstraints?> -<GridPane hgap="10.0" vgap="10.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="11.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="189.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <padding> - <Insets bottom="10.0" left="10.0" top="20.0" /> - </padding> - <children> - <Label text="Date:" /> - <Label text="Amount:" GridPane.rowIndex="1" /> - <Label text="Description:" GridPane.rowIndex="2" /> - <Label text="Category" GridPane.rowIndex="3"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </Label> - <TextField promptText="Date" GridPane.columnIndex="1" GridPane.columnSpan="2" /> - <TextField promptText="Amount" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> - <TextField promptText="Description (optional)" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> - <ComboBox prefWidth="150.0" GridPane.columnIndex="1" GridPane.rowIndex="3"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - </ComboBox> - <Button mnemonicParsing="false" onAction="#closeButton" text="Cancel" GridPane.columnIndex="3" GridPane.rowIndex="4"> - <GridPane.margin> - <Insets left="75.0" /> - </GridPane.margin> - </Button> - <Button mnemonicParsing="false" onAction="#closeButton" text="Button" GridPane.columnIndex="3" GridPane.rowIndex="4"> - <GridPane.margin> - <Insets left="130.0" /> - </GridPane.margin> - </Button> - </children> -</GridPane> +<DialogPane expanded="true" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> + <content> + <GridPane> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <Label text="Date:" /> + <Label text="Amount:" GridPane.rowIndex="1" /> + <Label text="Description:" GridPane.rowIndex="2" /> + <Label text="Category" GridPane.rowIndex="3" /> + <Label text="Recurring" GridPane.rowIndex="4" /> + <TextField fx:id="amountField" promptText="100" GridPane.columnIndex="1" GridPane.rowIndex="1" /> + <TextField fx:id="descriptionField" promptText="(optional)" GridPane.columnIndex="1" GridPane.rowIndex="2" /> + <ComboBox fx:id="categoryBox" prefWidth="150.0" promptText="Choose" GridPane.columnIndex="1" GridPane.rowIndex="3" /> + <ComboBox fx:id="recurringBox" prefWidth="150.0" promptText="No" GridPane.columnIndex="1" GridPane.rowIndex="4" /> + <HBox alignment="BOTTOM_RIGHT" prefHeight="100.0" prefWidth="200.0" spacing="10.0" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="5"> + <children> + <Button fx:id="cancelBtn" mnemonicParsing="false" onAction="#pressCancelBtn" prefHeight="25.0" prefWidth="60.0" text="Cancel" /> + <Button fx:id="okBtn" mnemonicParsing="false" onAction="#pressOkBtn" prefHeight="25.0" prefWidth="60.0" text="OK" /> + </children> + <GridPane.margin> + <Insets top="20.0" /> + </GridPane.margin> + </HBox> + <DatePicker fx:id="datePicker" GridPane.columnIndex="1" /> + </children> + </GridPane> + </content> +</DialogPane> -- GitLab From 8058b3ef5325e61affd11966121c879c5fbf9612 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:38:07 +0100 Subject: [PATCH 073/103] Created controller for income --- .../demo/controller/IncomeController.java | 220 ++++++++++++++++++ src/main/resources/view/Income.fxml | 33 +-- 2 files changed, 239 insertions(+), 14 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java new file mode 100644 index 00000000..3bfd4aaa --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java @@ -0,0 +1,220 @@ +package no.ntnu.idatt1002.demo.controller; + +import java.io.IOException; + +import java.util.Optional; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Dialog; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Economics.Income; +import no.ntnu.idatt1002.demo.data.Economics.IncomeCategory; +import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; +import no.ntnu.idatt1002.demo.data.Economics.FileHandling; +import no.ntnu.idatt1002.demo.data.Economics.Income; +import no.ntnu.idatt1002.demo.data.Economics.IncomeRegister; +import no.ntnu.idatt1002.demo.data.Economics.Item; +import no.ntnu.idatt1002.demo.data.Economics.ItemRegister; + +public class IncomeController { + + /** + * The mode of the dialog. NEW if new contact, EDIT if edit existing contact. + */ + private DialogMode dialogMode; + @FXML + private Button addBtn; + @FXML + private Button editBtn; + + @FXML + private Button deleteBtn; + + @FXML + private ComboBox<String> show; + + @FXML + private Button expenseBtn; + + @FXML + private Button overviewBtn; + + @FXML + private Button budgetBtn; + @FXML + private Button returnBtn; + @FXML + private TableColumn<Income, Double> amountColumn; + + @FXML + private TableColumn<Income, IncomeCategory> categoryColumn; + + @FXML + private TableColumn<Income, String> dateColumn; + + @FXML + private TableColumn<Income, String> descriptionColumn; + + @FXML + private TableColumn<Income, Boolean> recurringColumn; + + @FXML + private TableView<Income> incomeTableView; + + IncomeRegister incomeRegister; + ObservableList<Income> income; + + ObservableList<String> filter; + + @FXML + public void initialize() + throws IOException { //TODO SAME REGISTER FOR BOTH, BUT LOAD DIFFERENT DATA DEPENDING ON WHICH IT IS + dateColumn.setCellValueFactory(new PropertyValueFactory<Income, String>("date")); + amountColumn.setCellValueFactory(new PropertyValueFactory<Income, Double>("amount")); + categoryColumn.setCellValueFactory( + new PropertyValueFactory<Income, IncomeCategory>("category")); + descriptionColumn.setCellValueFactory(new PropertyValueFactory<Income, String>("description")); + recurringColumn.setCellValueFactory(new PropertyValueFactory<Income, Boolean>("recurring")); + + filter = FXCollections.observableArrayList("All", "Gift", "Salary", "Student loan", + "Fixed income"); + incomeRegister = loadIncomeDataFromFile("Income"); + income = FXCollections.observableArrayList(incomeRegister.getItems()); + incomeTableView.setItems(income); + + show.setItems(filter); + show.setValue("All"); + } + + @FXML + protected void handleAddButton(ActionEvent event) { + handleEditButton(event); + } + + @FXML + protected void handleEditButton(ActionEvent event) { + FXMLLoader loader = new FXMLLoader(); + loader.setLocation(getClass().getResource("/view/AddIncome.fxml")); + Income newIncome = null; + String dialogTitle = ""; + // Load the FXML file for your dialog box + Dialog<Income> dialog = new Dialog<>(); + dialog.initModality(Modality.APPLICATION_MODAL); + + try { + // Set the Dialog's content to the loaded FXML file + dialog.getDialogPane().setContent(loader.load()); + } catch (IOException e) { + e.printStackTrace(); + } + + // Get the controller for the loaded FXML file + AddIncomeController dialogController = loader.getController(); + + if (event.getSource().equals(addBtn)) { + dialogMode = DialogMode.ADD; + dialogTitle = "Add income"; + } else if (event.getSource().equals(editBtn) + && incomeTableView.getSelectionModel().getSelectedItem() != null) { + dialogMode = DialogMode.EDIT; + dialogTitle = "Edit income"; + newIncome = incomeTableView.getSelectionModel().getSelectedItem(); + dialogController.setIncome(newIncome); + } + + dialog.setTitle(dialogTitle); + // Show the Dialog and wait for the user to close it + dialog.showAndWait(); + //Get the newly created income from the dialog pane + newIncome = dialogController.getNewIncome(); + + if (newIncome != null && dialogMode == DialogMode.ADD) { + incomeRegister.addItem(newIncome); + refreshObservableList(); + } + + } + //Only add the income to the tableview, if the income is not null + + @FXML + public void handleDeleteBtn(ActionEvent event) { + Income chosenIncome = incomeTableView.getSelectionModel().getSelectedItem(); + if (chosenIncome == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + incomeRegister.removeItem(chosenIncome); + refreshObservableList(); + } + } + + protected void refreshObservableList() { + this.income.setAll(incomeRegister.getItems()); + } + + private Optional<ButtonType> showConfirmationDialog() { + Alert alert = new Alert(AlertType.CONFIRMATION); + alert.setTitle("Confirm Delete"); + alert.setHeaderText("Delete Confirmation"); + alert.setContentText("Are you sure you would like to delete the selected entry?"); + + return alert.showAndWait(); + } + + public IncomeRegister loadIncomeDataFromFile(String fileName) throws IOException { + //ItemRegister<T extends Item> + FileHandling fileHandling = new FileHandling(); + if (fileHandling.isEmpty(fileName)) { + incomeRegister = new IncomeRegister(); + } else { + try { + incomeRegister = fileHandling.readIncomeRegisterFromFile(fileName); + } catch (IOException e) { + e.printStackTrace(); + } + } + return incomeRegister; + } + + public void saveDataToFile(String fileName) throws IOException { + FileHandling fileHandling = new FileHandling(); + fileHandling.writeItemRegisterToFile(incomeRegister, fileName); + } + + @FXML + public void switchScene(ActionEvent event) throws IOException { + saveDataToFile("Income"); + FXMLLoader loader = new FXMLLoader(); + if (event.getSource() == expenseBtn) { + loader.setLocation(SceneController.class.getResource("/view/Expenses.fxml")); + } else if (event.getSource() == overviewBtn) { + loader.setLocation(SceneController.class.getResource("/view/Overview.fxml")); + } else if (event.getSource() == returnBtn) { + loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); + } else if (event.getSource() == budgetBtn) { + loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); + + Parent root = loader.load(); + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); + } + } +} \ No newline at end of file diff --git a/src/main/resources/view/Income.fxml b/src/main/resources/view/Income.fxml index d7ae545f..090983df 100644 --- a/src/main/resources/view/Income.fxml +++ b/src/main/resources/view/Income.fxml @@ -33,7 +33,7 @@ <top> <HBox BorderPane.alignment="CENTER"> <children> - <Button fx:id="returnBtn" mnemonicParsing="false" onAction="#switchScene" text="Return "> + <Button mnemonicParsing="false" onAction="#switchStartMenu" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> + <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addIncome" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -82,7 +82,7 @@ </ImageView> </graphic> </Button> - <Button fx:id="editBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -91,7 +91,7 @@ </ImageView> </graphic> </Button> - <Button fx:id="deleteBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleDeleteBtn" text="Delete" textAlignment="CENTER"> + <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -122,10 +122,15 @@ </VBox> <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button disable="true" mnemonicParsing="false" text="Income" /> - <Button fx:id="budgetBtn" mnemonicParsing="false" onAction="#switchScene" text="Budget" /> - <Button mnemonicParsing="false" text="Expenses" /> - <Button mnemonicParsing="false" onAction="#switchScene" text="Next"> + <Button mnemonicParsing="false" onAction="#switchOverview" text="Overview"> + <HBox.margin> + <Insets right="5.0" /> + </HBox.margin> + </Button> + <Button disable="true" mnemonicParsing="false" onAction="#switchIncome" text="Income" /> + <Button mnemonicParsing="false" onAction="#switchExpenses" text="Expenses" /> + <Button disable="true" mnemonicParsing="false" text="Savings" /> + <Button mnemonicParsing="false" onAction="#switchExpenses" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin> @@ -135,13 +140,13 @@ <Insets top="10.0" /> </padding> </HBox> - <TableView fx:id="incomeTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> + <TableView fx:id="expenseTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> <columns> - <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> + <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> -- GitLab From 23f06ba018f1852860fdce8e029aa4fa102ec1bb Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:41:58 +0100 Subject: [PATCH 074/103] "Removed itemMode enums and incomes in AddExpenseController. Changed controller of AddIncome to AddIncomeController" --- .../demo/controller/AddExpenseController.java | 13 ------------- src/main/resources/view/AddIncome.fxml | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index 21d8bc4f..b1a4691a 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -24,11 +24,6 @@ public class AddExpenseController { Expense chosenExpense = null; //an expense that is meant to track the old state of an expense before editing, in case cancel bugtton is clicked - Income newIncome = null; - - Income chosenIncome = null; - - private ItemMode itemMode; @FXML private Button cancelBtn; @@ -83,14 +78,6 @@ public class AddExpenseController { descriptionField.textProperty().set(expense.getDescription()); amountField.textProperty().setValue(String.valueOf(expense.getAmount())); recurringBox.setValue(expense.isRecurring()); - - //new SimpleStringProperty(datePicker.getValue().toString()).bindBidirectional(new SimpleStringProperty(expense.getDate().toString())); - //Bind this expense's fields with the argument object's fields. If cancel is pressed - do nothing. If ok is pressed, bind the textfields with this expsense - - /*amountField.textProperty().bindBidirectional(expense.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED - descriptionField.textProperty().bindBidirectional(expense.descriptionProperty()); - //categoryBox.valueProperty().bindBidirectional(expense.getCategory()); - recurringBox.valueProperty().bindBidirectional(expense.recurringProperty());*/ } @FXML diff --git a/src/main/resources/view/AddIncome.fxml b/src/main/resources/view/AddIncome.fxml index be30573f..abdfe7c2 100644 --- a/src/main/resources/view/AddIncome.fxml +++ b/src/main/resources/view/AddIncome.fxml @@ -12,7 +12,7 @@ <?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.RowConstraints?> -<DialogPane expanded="true" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddExpenseController"> +<DialogPane expanded="true" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddIncomeController"> <content> <GridPane> <columnConstraints> -- GitLab From 953db8850a760daf345a448ff146f9d27c0cbfdd Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:44:33 +0100 Subject: [PATCH 075/103] "Removed everything that has to do with income, and moved showAndWait into if else, to prevent edit popup to appear even if nothing is selected in tableview" --- .../demo/controller/ExpensesController.java | 194 +++++------------- 1 file changed, 57 insertions(+), 137 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 10828491..26f4d319 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -35,18 +35,12 @@ enum DialogMode { ADD, EDIT, DELETE } -enum ItemMode { - INCOME, EXPENSE -} - public class ExpensesController { /** * The mode of the dialog. NEW if new contact, EDIT if edit existing contact. */ private DialogMode dialogMode; - - private ItemMode itemMode; @FXML private Button addBtn; @FXML @@ -64,6 +58,9 @@ public class ExpensesController { @FXML private Button overviewBtn; + @FXML + private Button budgetBtn; + @FXML private Button returnBtn; @FXML @@ -92,31 +89,22 @@ public class ExpensesController { ObservableList<String> filter; - //ItemRegister itemRegister; - IncomeRegister incomeRegister; - ObservableList<Income> income; - @FXML - public void initialize() throws IOException { //TODO SAME REGISTER FOR BOTH, BUT LOAD DIFFERENT DATA DEPENDING ON WHICH IT IS + public void initialize() + throws IOException { //TODO SAME REGISTER FOR BOTH, BUT LOAD DIFFERENT DATA DEPENDING ON WHICH IT IS dateColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("date")); amountColumn.setCellValueFactory(new PropertyValueFactory<Expense, Double>("amount")); - categoryColumn.setCellValueFactory(new PropertyValueFactory<Expense, ExpenseCategory>("category")); + categoryColumn.setCellValueFactory( + new PropertyValueFactory<Expense, ExpenseCategory>("category")); descriptionColumn.setCellValueFactory(new PropertyValueFactory<Expense, String>("description")); recurringColumn.setCellValueFactory(new PropertyValueFactory<Expense, Boolean>("recurring")); - if (expenseTableView != null) { - itemMode = ItemMode.EXPENSE; - filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", "Fixed expense"); - expenseRegister = loadExpenseDataFromFile("Expense"); - expenses = FXCollections.observableArrayList(expenseRegister.getItems()); - expenseTableView.setItems(expenses); - } else { - itemMode = ItemMode.INCOME; - filter = FXCollections.observableArrayList("All", "Salary", "Student loan", "Gift", "Fixed expense"); - incomeRegister = loadIncomeDataFromFile("Income"); - income = FXCollections.observableArrayList(incomeRegister.getItems()); - incomeTableView.setItems(income); - } + filter = FXCollections.observableArrayList("All", "Food", "Clothes", "Books", "Other", + "Fixed expense"); + expenseRegister = loadExpenseDataFromFile("Expense"); + expenses = FXCollections.observableArrayList(expenseRegister.getItems()); + expenseTableView.setItems(expenses); + show.setItems(filter); show.setValue("All"); } @@ -125,16 +113,12 @@ public class ExpensesController { protected void handleAddButton(ActionEvent event) { handleEditButton(event); } + @FXML protected void handleEditButton(ActionEvent event) { FXMLLoader loader = new FXMLLoader(); - if (itemMode == ItemMode.EXPENSE) { - loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); - } else { - loader.setLocation(getClass().getResource("/view/AddIncome.fxml")); - } + loader.setLocation(getClass().getResource("/view/AddExpense.fxml")); Expense newExpense = null; - Income newIncome = null; String dialogTitle = ""; // Load the FXML file for your dialog box Dialog<Expense> dialog = new Dialog<>(); @@ -149,91 +133,51 @@ public class ExpensesController { // Get the controller for the loaded FXML file AddExpenseController dialogController = loader.getController(); - AddIncomeController dialogController1 = loader.getController(); - - if (itemMode == ItemMode.EXPENSE) { - if (event.getSource().equals(addBtn)) { - dialogMode = DialogMode.ADD; - dialogTitle = "Add expense"; - } else if (event.getSource().equals(editBtn) && expenseTableView.getSelectionModel().getSelectedItem() != null) { - dialogMode = DialogMode.EDIT; - dialogTitle = "Edit expense"; - newExpense = expenseTableView.getSelectionModel().getSelectedItem(); - dialogController.setExpense(newExpense); - } else { - return; - } - } else { - if (event.getSource().equals(addBtn)) { - dialogMode = DialogMode.ADD; - dialogTitle = "Income"; - } else if (event.getSource().equals(editBtn) && incomeTableView.getSelectionModel().getSelectedItem() != null) { - dialogMode = DialogMode.EDIT; - dialogTitle = "Edit income"; - newIncome = incomeTableView.getSelectionModel().getSelectedItem(); - dialogController1.setIncome(newIncome); //TODO NEED TO FXI THIS TO INCOME - } else { - return; - } + + if (event.getSource().equals(addBtn)) { + dialogMode = DialogMode.ADD; + dialogTitle = "Add expense"; + dialog.setTitle(dialogTitle); + dialog.showAndWait(); + } else if (event.getSource().equals(editBtn) + && expenseTableView.getSelectionModel().getSelectedItem() != null) { + dialogMode = DialogMode.EDIT; + dialogTitle = "Edit expense"; + newExpense = expenseTableView.getSelectionModel().getSelectedItem(); + dialogController.setExpense(newExpense); + dialog.setTitle(dialogTitle); + dialog.showAndWait(); } - dialog.setTitle(dialogTitle); // Show the Dialog and wait for the user to close it - dialog.showAndWait(); //Get the newly created expense from the dialog pane + newExpense = dialogController.getNewExpense(); - - if (itemMode == ItemMode.EXPENSE) { - newExpense = dialogController.getNewExpense(); - if (newExpense != null && dialogMode == DialogMode.ADD) { - expenseRegister.addItem(newExpense); - refreshObservableList(); - } - } else { - newIncome = dialogController1.getNewIncome(); //TODO NEED TO FIX to newIncome - if (newIncome != null && dialogMode == DialogMode.ADD) { //TODO NEED TO FIX TO NEW INCOME - incomeRegister.addItem(newIncome); //TODO NEED TO FIX TO NEW INCOME - refreshObservableList(); - } - + if (newExpense != null && dialogMode == DialogMode.ADD) { + expenseRegister.addItem(newExpense); + refreshObservableList(); } - //Only add the expense to the tableview, if the expense is not null + } + //Only add the expense to the tableview, if the expense is not null @FXML public void handleDeleteBtn(ActionEvent event) { - if (itemMode == ItemMode.EXPENSE) { - Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); - if (chosenExpense == null) { - return; - } - Optional<ButtonType> isConfirmed = showConfirmationDialog(); - if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { - expenseRegister.removeItem(chosenExpense); - refreshObservableList(); - } - } else { - Income chosenIncome = incomeTableView.getSelectionModel().getSelectedItem(); - if (chosenIncome == null) { - return; - } - Optional<ButtonType> isConfirmed = showConfirmationDialog(); - if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { - incomeRegister.removeItem(chosenIncome); - refreshObservableList(); - } + Expense chosenExpense = expenseTableView.getSelectionModel().getSelectedItem(); + if (chosenExpense == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + expenseRegister.removeItem(chosenExpense); + refreshObservableList(); } } protected void refreshObservableList() { - if (itemMode == ItemMode.EXPENSE) { - this.expenses.setAll(expenseRegister.getItems()); - } else { - this.income.setAll(incomeRegister.getItems()); - } + this.expenses.setAll(expenseRegister.getItems()); } - private Optional<ButtonType> showConfirmationDialog() { Alert alert = new Alert(AlertType.CONFIRMATION); alert.setTitle("Confirm Delete"); @@ -248,49 +192,24 @@ public class ExpensesController { FileHandling fileHandling = new FileHandling(); if (fileHandling.isEmpty(fileName)) { expenseRegister = new ExpenseRegister(); - } else { - try{ - expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); - } catch(IOException e) { - e.printStackTrace(); - } - } - return expenseRegister; - } - - public IncomeRegister loadIncomeDataFromFile(String fileName) throws IOException { - FileHandling fileHandling = new FileHandling(); - if (fileHandling.isEmpty(fileName)) { - incomeRegister = new IncomeRegister(); } else { try { - incomeRegister = fileHandling.readIncomeRegisterFromFile(fileName); + expenseRegister = fileHandling.readExpenseRegisterFromFile(fileName); } catch (IOException e) { e.printStackTrace(); } } - return incomeRegister; + return expenseRegister; } - - public void saveDataToFile(String fileName) throws IOException { FileHandling fileHandling = new FileHandling(); - if (itemMode == ItemMode.EXPENSE) { - fileHandling.writeItemRegisterToFile(expenseRegister, fileName); } - else { - fileHandling.writeItemRegisterToFile(incomeRegister, fileName); } + fileHandling.writeItemRegisterToFile(expenseRegister, fileName); } @FXML public void switchScene(ActionEvent event) throws IOException { - if (itemMode == ItemMode.EXPENSE) { - saveDataToFile("Expense"); - } - else { - saveDataToFile("Income"); - } - + saveDataToFile("Expense"); FXMLLoader loader = new FXMLLoader(); if (event.getSource() == incomeBtn) { loader.setLocation(SceneController.class.getResource("/view/Income.fxml")); @@ -298,13 +217,14 @@ public class ExpensesController { loader.setLocation(SceneController.class.getResource("/view/Overview.fxml")); } else if (event.getSource() == returnBtn) { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); - } - - Parent root = loader.load(); - Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); - Scene scene = new Scene(root); - stage.setScene(scene); - stage.show(); + } else if (event.getSource() == budgetBtn) { + loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); + + Parent root = loader.load(); + Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); + Scene scene = new Scene(root); + stage.setScene(scene); + stage.show(); } } - +} \ No newline at end of file -- GitLab From f2ee411b10d64ac6026cfb810e2e763765b8ef53 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:45:42 +0100 Subject: [PATCH 076/103] "Switched income controller. Added id's to buttons" --- src/main/resources/view/Income.fxml | 35 +++++++++++++---------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/src/main/resources/view/Income.fxml b/src/main/resources/view/Income.fxml index 090983df..3414dfcc 100644 --- a/src/main/resources/view/Income.fxml +++ b/src/main/resources/view/Income.fxml @@ -19,7 +19,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> -<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> +<AnchorPane xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.IncomeController"> <children> <ImageView fitHeight="400.0" fitWidth="600.0" pickOnBounds="true"> <image> @@ -33,7 +33,7 @@ <top> <HBox BorderPane.alignment="CENTER"> <children> - <Button mnemonicParsing="false" onAction="#switchStartMenu" text="Return "> + <Button fx:id="returnBtn" mnemonicParsing="false" onAction="#switchScene" text="Return "> <opaqueInsets> <Insets left="100.0" /> </opaqueInsets> @@ -73,7 +73,7 @@ <children> <HBox alignment="BOTTOM_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="add" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#addIncome" text="Add" textAlignment="CENTER"> + <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -82,7 +82,7 @@ </ImageView> </graphic> </Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Edit" textAlignment="CENTER"> + <Button fx:id="editBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleEditButton" text="Edit" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -91,7 +91,7 @@ </ImageView> </graphic> </Button> - <Button alignment="TOP_CENTER" mnemonicParsing="false" text="Delete" textAlignment="CENTER"> + <Button fx:id="deleteBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleDeleteBtn" text="Delete" textAlignment="CENTER"> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -122,15 +122,10 @@ </VBox> <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> - <Button mnemonicParsing="false" onAction="#switchOverview" text="Overview"> - <HBox.margin> - <Insets right="5.0" /> - </HBox.margin> - </Button> - <Button disable="true" mnemonicParsing="false" onAction="#switchIncome" text="Income" /> - <Button mnemonicParsing="false" onAction="#switchExpenses" text="Expenses" /> - <Button disable="true" mnemonicParsing="false" text="Savings" /> - <Button mnemonicParsing="false" onAction="#switchExpenses" text="Next"> + <Button disable="true" mnemonicParsing="false" text="Income" /> + <Button fx:id="budgetBtn" mnemonicParsing="false" onAction="#switchScene" text="Budget" /> + <Button fx:id="expenseBtn" mnemonicParsing="false" onAction="#switchScene" text="Expenses" /> + <Button mnemonicParsing="false" onAction="#switchScene" text="Next"> <HBox.margin> <Insets left="170.0" /> </HBox.margin> @@ -140,13 +135,13 @@ <Insets top="10.0" /> </padding> </HBox> - <TableView fx:id="expenseTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> + <TableView fx:id="incomeTableView" prefHeight="260.0" prefWidth="485.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> <columns> - <TableColumn fx:id="date" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amount" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="category" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="description" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurring" prefWidth="75.0" text="Recurring" /> + <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> -- GitLab From 335b244dc3e4612d1ad71fc9d590bc56891bc7a9 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:46:06 +0100 Subject: [PATCH 077/103] "Added method to button" --- src/main/resources/view/Expenses.fxml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/view/Expenses.fxml b/src/main/resources/view/Expenses.fxml index 1846356e..31669e18 100644 --- a/src/main/resources/view/Expenses.fxml +++ b/src/main/resources/view/Expenses.fxml @@ -120,7 +120,7 @@ <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> <children> <Button fx:id="incomeBtn" mnemonicParsing="false" onAction="#switchScene" text="Income" /> - <Button mnemonicParsing="false" text="Budget" /> + <Button fx:id="budgetBtn" mnemonicParsing="false" onAction="#switchScene" text="Budget" /> <Button disable="true" mnemonicParsing="false" text="Expenses" /> <Button mnemonicParsing="false" onAction="#switchScene" text="Next"> <HBox.margin> -- GitLab From 1d27661ad83b95f8338f383f385ba72d5eb81d39 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 10:46:38 +0100 Subject: [PATCH 078/103] "Removed ItemMode enum. GetNewIncome returns new income, instead of chosenIncome" --- .../ntnu/idatt1002/demo/controller/AddIncomeController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java index dc677df6..76a7c042 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java @@ -24,8 +24,6 @@ public class AddIncomeController { Income newIncome = null; Income chosenIncome = null; - - private ItemMode itemMode; @FXML private Button cancelBtn; @@ -119,6 +117,6 @@ public class AddIncomeController { } public Income getNewIncome() { - return this.chosenIncome; + return this.newIncome; } } \ No newline at end of file -- GitLab From 5f209f4ba8b1b89bcbec3a59b8901c12cf2996e2 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 11:30:07 +0100 Subject: [PATCH 079/103] "Created getters for category and date properties" --- .../no/ntnu/idatt1002/demo/data/Economics/Expense.java | 3 +++ .../java/no/ntnu/idatt1002/demo/data/Economics/Income.java | 3 +++ .../java/no/ntnu/idatt1002/demo/data/Economics/Item.java | 7 +++++-- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java index 11848b02..de47509b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Expense.java @@ -45,6 +45,9 @@ public class Expense extends Item{ this.category = new SimpleObjectProperty<>(category); } + public ObjectProperty<ExpenseCategory> expenseCategoryObjectProperty() { + return this.category; + } /** * The method returns the category to which the Expense belongs as a value of the ExpenseCategory enum class. * @return The category the Expense belongs to as a value of the ExpenseCategory enum class. diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java index 904ab935..4b8eaae9 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Income.java @@ -53,6 +53,9 @@ public class Income extends Item{ } + public ObjectProperty<IncomeCategory> incomeCategoryObjectProperty() { + return this.category; + } /** * The method returns the category to which the Income belongs as a value of the IncomeCategory enum. * @return The category to which the Income belongs to as a value of the IncomeCategory enum. diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java index 41121f7a..016ccc95 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Economics/Item.java @@ -113,10 +113,13 @@ public abstract class Item { } /** - public StringProperty dateProperty() { + * The method returns the ObjectProperty of date. + * @return + */ + public ObjectProperty<LocalDate> dateProperty() { return this.date; } - */ + /** * The method returns the date of the item object (income/expense). * @return The date of the transaction. -- GitLab From 0ec6630ce7553ad76cea5e5192e924a216c7fc4b Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 11:33:11 +0100 Subject: [PATCH 080/103] "Binded category and date for edit button. Set category and datepicker values to the values of the chosen item" --- .../demo/controller/AddExpenseController.java | 9 ++++++-- .../demo/controller/AddIncomeController.java | 21 ++++++++----------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index b1a4691a..aab9183b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -73,11 +73,14 @@ public class AddExpenseController { chosenExpense.descriptionProperty().bindBidirectional(expense.descriptionProperty()); chosenExpense.amountProperty().bindBidirectional(expense.amountProperty()); chosenExpense.recurringProperty().bindBidirectional(expense.recurringProperty()); - //chosenExpense.().bindBidirectional(expense.descriptionProperty()); - //chosenExpense.().bindBidirectional(expense.descriptionProperty()); + chosenExpense.expenseCategoryObjectProperty().bindBidirectional(expense.expenseCategoryObjectProperty()); + chosenExpense.dateProperty().bindBidirectional(expense.dateProperty()); + descriptionField.textProperty().set(expense.getDescription()); amountField.textProperty().setValue(String.valueOf(expense.getAmount())); recurringBox.setValue(expense.isRecurring()); + datePicker.setValue(expense.getDate()); + categoryBox.setValue(expense.getCategory()); } @FXML @@ -94,6 +97,8 @@ public class AddExpenseController { chosenExpense.setDescription((descriptionField.getText())); chosenExpense.setAmount(Double.parseDouble(amountField.getText())); chosenExpense.setRecurring(recurringBox.getValue()); + chosenExpense.setCategory(categoryBox.getValue()); + chosenExpense.setDate(datePicker.getValue()); } final Node source = (Node) event.getSource(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java index 76a7c042..937b2eef 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java @@ -30,9 +30,6 @@ public class AddIncomeController { @FXML private Button okBtn; - @FXML - private TextField dateField; - @FXML private DatePicker datePicker; @@ -73,19 +70,17 @@ public class AddIncomeController { chosenIncome.descriptionProperty().bindBidirectional(income.descriptionProperty()); chosenIncome.amountProperty().bindBidirectional(income.amountProperty()); chosenIncome.recurringProperty().bindBidirectional(income.recurringProperty()); - //chosenExpense.().bindBidirectional(income.descriptionProperty()); - //chosenExpense.().bindBidirectional(income.descriptionProperty()); + chosenIncome.incomeCategoryObjectProperty().bindBidirectional(income.incomeCategoryObjectProperty()); + chosenIncome.dateProperty().bindBidirectional(income.dateProperty()); descriptionField.textProperty().set(income.getDescription()); amountField.textProperty().setValue(String.valueOf(income.getAmount())); recurringBox.setValue(income.isRecurring()); - //new SimpleStringProperty(datePicker.getValue().toString()).bindBidirectional(new SimpleStringProperty(income.getDate().toString())); - //Bind this income's fields with the argument object's fields. If cancel is pressed - do nothing. If ok is pressed, bind the textfields with this expsense - - /*amountField.textProperty().bindBidirectional(income.amountProperty(), NumberFormat.getNumberInstance()); //TODO AMOUNT IS STORED WITH COMMA, WHICH IS NOT ALLOWED - descriptionField.textProperty().bindBidirectional(income.descriptionProperty()); - //categoryBox.valueProperty().bindBidirectional(income.getCategory()); - recurringBox.valueProperty().bindBidirectional(income.recurringProperty());*/ + descriptionField.textProperty().set(income.getDescription()); + amountField.textProperty().setValue(String.valueOf(income.getAmount())); + recurringBox.setValue(income.isRecurring()); + datePicker.setValue(income.getDate()); + categoryBox.setValue(income.getCategory()); } @FXML @@ -102,6 +97,8 @@ public class AddIncomeController { chosenIncome.setDescription((descriptionField.getText())); chosenIncome.setAmount(Double.parseDouble(amountField.getText())); chosenIncome.setRecurring(recurringBox.getValue()); + chosenIncome.setCategory(categoryBox.getValue()); + chosenIncome.setDate(datePicker.getValue()); } final Node source = (Node) event.getSource(); -- GitLab From 7874e585e2737b61ac6a00394d866569ecf38035 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 11:34:43 +0100 Subject: [PATCH 081/103] "Added else statement for preventing edit popup if no item is selected. Moved refresh method. Moved brackets of else if which prevented screen switching" --- .../demo/controller/ExpensesController.java | 16 ++++++++-------- .../demo/controller/IncomeController.java | 8 ++++---- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 26f4d319..9f32f059 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -137,17 +137,19 @@ public class ExpensesController { if (event.getSource().equals(addBtn)) { dialogMode = DialogMode.ADD; dialogTitle = "Add expense"; - dialog.setTitle(dialogTitle); - dialog.showAndWait(); + + } else if (event.getSource().equals(editBtn) && expenseTableView.getSelectionModel().getSelectedItem() != null) { dialogMode = DialogMode.EDIT; dialogTitle = "Edit expense"; newExpense = expenseTableView.getSelectionModel().getSelectedItem(); dialogController.setExpense(newExpense); - dialog.setTitle(dialogTitle); - dialog.showAndWait(); + } else { + return; } + dialog.setTitle(dialogTitle); + dialog.showAndWait(); // Show the Dialog and wait for the user to close it //Get the newly created expense from the dialog pane @@ -155,9 +157,8 @@ public class ExpensesController { if (newExpense != null && dialogMode == DialogMode.ADD) { expenseRegister.addItem(newExpense); - refreshObservableList(); } - + refreshObservableList(); } //Only add the expense to the tableview, if the expense is not null @@ -219,7 +220,7 @@ public class ExpensesController { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); } else if (event.getSource() == budgetBtn) { loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); - + } Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); @@ -227,4 +228,3 @@ public class ExpensesController { stage.show(); } } -} \ No newline at end of file diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java index 3bfd4aaa..bca922a6 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java @@ -135,6 +135,8 @@ public class IncomeController { dialogTitle = "Edit income"; newIncome = incomeTableView.getSelectionModel().getSelectedItem(); dialogController.setIncome(newIncome); + } else { + return; } dialog.setTitle(dialogTitle); @@ -145,9 +147,8 @@ public class IncomeController { if (newIncome != null && dialogMode == DialogMode.ADD) { incomeRegister.addItem(newIncome); - refreshObservableList(); } - + refreshObservableList(); } //Only add the income to the tableview, if the income is not null @@ -209,7 +210,7 @@ public class IncomeController { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); } else if (event.getSource() == budgetBtn) { loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); - + } Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); Scene scene = new Scene(root); @@ -217,4 +218,3 @@ public class IncomeController { stage.show(); } } -} \ No newline at end of file -- GitLab From 0053e157b1a878a2b711c806fb7d2f74ca00bc1e Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 11:37:05 +0100 Subject: [PATCH 082/103] "Set default date value to today, when adding item" --- .../no/ntnu/idatt1002/demo/controller/AddExpenseController.java | 2 ++ .../no/ntnu/idatt1002/demo/controller/AddIncomeController.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java index aab9183b..b4a6ce14 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddExpenseController.java @@ -58,6 +58,8 @@ public class AddExpenseController { ObservableList<Boolean> recurring = FXCollections.observableArrayList(true, false); recurringBox.setItems(recurring); recurringBox.setValue(false); + + datePicker.setValue(LocalDate.now()); } public ExpenseCategory getCategory() { diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java index 937b2eef..d5960454 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddIncomeController.java @@ -55,6 +55,8 @@ public class AddIncomeController { ObservableList<Boolean> recurring = FXCollections.observableArrayList(true, false); recurringBox.setItems(recurring); recurringBox.setValue(false); + + datePicker.setValue(LocalDate.now()); } public IncomeCategory getCategory() { -- GitLab From 84b7fdd4b9031d2c3c105df9d7fb0a9cc1e5aab7 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 11:41:52 +0100 Subject: [PATCH 083/103] Pushing up test files to prevent test failures in gitlab --- src/main/resources/Economics/Expense.register | 18 ++++++++++++++++++ src/main/resources/Economics/Income.register | 17 +++++++++++++++++ .../Economics/expenseRegisterTest.register | 6 ++++++ .../Economics/incomeRegisterTest.register | 0 4 files changed, 41 insertions(+) create mode 100644 src/main/resources/Economics/Expense.register create mode 100644 src/main/resources/Economics/Income.register create mode 100644 src/main/resources/Economics/expenseRegisterTest.register create mode 100644 src/main/resources/Economics/incomeRegisterTest.register diff --git a/src/main/resources/Economics/Expense.register b/src/main/resources/Economics/Expense.register new file mode 100644 index 00000000..f81f9e25 --- /dev/null +++ b/src/main/resources/Economics/Expense.register @@ -0,0 +1,18 @@ +date=2023-03-01 +description=twelve +amount=12.0 +isReoccuring=Reoccurring +category=CLOTHES + +date=2023-03-11 +description=1111 +amount=121.0 +isReoccuring=Reoccurring +category=OTHER + +date=2024-03-16 +description=1112111 +amount=11111.99999 +isReoccuring=Not reoccurring +category=FOOD + diff --git a/src/main/resources/Economics/Income.register b/src/main/resources/Economics/Income.register new file mode 100644 index 00000000..f197ea42 --- /dev/null +++ b/src/main/resources/Economics/Income.register @@ -0,0 +1,17 @@ +date=2023-03-01 +amount=555.0 +isReoccuring=Not reoccurring +category=GIFT + +date=2023-03-22 +description=-bursdag +amount=1100.0 +isReoccuring=Not reoccurring +category=GIFT + +date=2023-03-01 +description=wthr +amount=22222.0 +isReoccuring=Not reoccurring +category=GIFT + diff --git a/src/main/resources/Economics/expenseRegisterTest.register b/src/main/resources/Economics/expenseRegisterTest.register new file mode 100644 index 00000000..84a96af7 --- /dev/null +++ b/src/main/resources/Economics/expenseRegisterTest.register @@ -0,0 +1,6 @@ +date=2023-03-03 +description=description +amount=59.900001525878906 +isReoccuring=Not reoccurring +category=CLOTHES + diff --git a/src/main/resources/Economics/incomeRegisterTest.register b/src/main/resources/Economics/incomeRegisterTest.register new file mode 100644 index 00000000..e69de29b -- GitLab From 3858d6c837490f82339f44bacf495e693a917656 Mon Sep 17 00:00:00 2001 From: Andreas <andreksv@ntnu.no> Date: Mon, 20 Mar 2023 14:05:44 +0100 Subject: [PATCH 084/103] Rewrote toString method in Income, Expense and Item --- src/main/resources/Economics/expenseRegisterTest.register | 6 ------ 1 file changed, 6 deletions(-) delete mode 100644 src/main/resources/Economics/expenseRegisterTest.register diff --git a/src/main/resources/Economics/expenseRegisterTest.register b/src/main/resources/Economics/expenseRegisterTest.register deleted file mode 100644 index 84a96af7..00000000 --- a/src/main/resources/Economics/expenseRegisterTest.register +++ /dev/null @@ -1,6 +0,0 @@ -date=2023-03-03 -description=description -amount=59.900001525878906 -isReoccuring=Not reoccurring -category=CLOTHES - -- GitLab From b0b4513313e5633bd351ea18f84f4a7561aab3af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Wed, 15 Mar 2023 10:37:36 +0100 Subject: [PATCH 085/103] Created Budget interface --- .../no/ntnu/idatt1002/demo/data/Budget/Budget.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java new file mode 100644 index 00000000..37e45c2d --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java @@ -0,0 +1,11 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +public interface Budget { + + void addToBudget(int AmountToAddToBudget); + void setAmountBudget(int amountBudget); + void setBudgetPeriod(int budgetPeriod); + void subtractFromBudget(int amountToSubtractFromBudget); +// public void divideBudgetInDifferentCategories(ExpenseCategory category); + +} -- GitLab From 67643829c6af8b9c4bba779e819010bf19a34d6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Wed, 15 Mar 2023 12:36:00 +0100 Subject: [PATCH 086/103] Created and implemented FoodBudget --- .../demo/data/Budget/FoodBudget.java | 128 ++++++++++++++++++ .../demo/data/Budget/FoodBudgetTest.java | 85 ++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java create mode 100644 src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java new file mode 100644 index 00000000..58b34823 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java @@ -0,0 +1,128 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +import java.sql.Date; +import java.time.Duration; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.Calendar; + +/** + * Class that represents FoodBudget and implements the Budget interface. + * + * @author Adele + */ +public class FoodBudget implements Budget { + + private int budgetAmount; + + private int budgetPeriod; + + /** + * The constructor of a new FoodBudget. + * + * @param budgetPeriod The period the budget are going to last for + * @param budgetAmount The amount that the budget consist of + * + */ + public FoodBudget(int budgetPeriod, int budgetAmount){ + this.budgetPeriod = budgetPeriod; + this.budgetAmount = budgetAmount; + } + + /** + * This method returns the amount in the foodBudget as an int + * + * @return the amount left in the foodBudget + * + */ + public int getBudgetAmount() { + return budgetAmount; + } + + /** + * + * @return + * + */ + public int getBudgetPeriod() { + return budgetPeriod; + } + + /** + * This method returns the amount of days left in the period as a string + * + * @return + * + */ + public long getDaysLeftOfBudgetPeriod() { + LocalDate today = LocalDate.now(); + LocalDate end = today.plus(Duration.ofDays(getBudgetPeriod())); + + Date todaysDate = (Date) Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date endDate = (Date) Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant()); + + Calendar cStart = Calendar.getInstance(); cStart.setTime(todaysDate); + Calendar cEnd = Calendar.getInstance(); cEnd.setTime(endDate); + + long timeLeft = ChronoUnit.DAYS.between((Temporal) cStart, (Temporal) cEnd); + + return timeLeft; + } + + /** + * + * @param amountBudget + * + */ + @Override + public void setAmountBudget(int amountBudget) { + if (amountBudget < 0) { + throw new IllegalArgumentException("Illegal amount in budget"); + } + + budgetAmount = amountBudget; + } + + /** + * + * @param periodOfTheBudget + * + */ + @Override + public void setBudgetPeriod(int periodOfTheBudget) { + if (periodOfTheBudget < 0) { + throw new IllegalArgumentException("Illegal period for the budget"); + } + + budgetPeriod = periodOfTheBudget; + } + + /** + * + * @param amountToAddToBudget + * + */ + @Override + public void addToBudget(int amountToAddToBudget) { + if (amountToAddToBudget < 0){ + throw new IllegalArgumentException("Amount to be added to the budget cant be below zero"); + } + budgetAmount += amountToAddToBudget; + } + + /** + * + * @param amountToSubtractFromBudget + * + */ + @Override + public void subtractFromBudget(int amountToSubtractFromBudget) { + if (amountToSubtractFromBudget < 0){ + throw new IllegalArgumentException("Amount to be subtracted from the budget cant be below zero"); + } + budgetAmount -= amountToSubtractFromBudget; + } + +} diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java new file mode 100644 index 00000000..22503bb9 --- /dev/null +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java @@ -0,0 +1,85 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class FoodBudgetTest { + + @Test + void set_amount_budget() { + FoodBudget foodBudget = new FoodBudget(30, 0); + int amount = 2000; + foodBudget.setAmountBudget(amount); + + assertEquals(amount, foodBudget.getBudgetAmount()); + } + + @Test + void set_amount_budget_negativ_amount() { + FoodBudget foodBudget = new FoodBudget(30, 0); + int amount = -2000; + + assertThrows(IllegalArgumentException.class, () -> foodBudget.setAmountBudget(amount)); + } + + @Test + void set_budget_period() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int period = 30; + foodBudget.setBudgetPeriod(period); + + assertEquals(period,foodBudget.getBudgetPeriod()); + } + + @Test + void set_budget_period_negative_period() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int period = -5; + + assertThrows(IllegalArgumentException.class, () -> foodBudget.setBudgetPeriod(period)); + } + + @Test + void add_to_budget_positive_number() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int beforeAdding = foodBudget.getBudgetAmount(); + int amountToBeAdded = 50; + foodBudget.addToBudget(amountToBeAdded); + + assertEquals(beforeAdding + amountToBeAdded, foodBudget.getBudgetAmount()); + + } + + @Test + void add_to_budget_negative_number() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int amountToBeAdded = -7; + + assertThrows(IllegalArgumentException.class, () -> foodBudget.addToBudget(amountToBeAdded)); + } + + @Test + void subtract_from_budget_positive_number() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int beforeSubtracting = foodBudget.getBudgetAmount(); + int amountToBeSubtracted = 50; + foodBudget.subtractFromBudget(amountToBeSubtracted); + + assertEquals(beforeSubtracting - amountToBeSubtracted, foodBudget.getBudgetAmount()); + } + + @Test + void subtract_from_budget_negative_number() { + FoodBudget foodBudget = new FoodBudget(0, 30); + int amountToBeSubtracted = -7; + + assertThrows(IllegalArgumentException.class, () -> foodBudget.subtractFromBudget(amountToBeSubtracted)); + } + + @Test + void getDaysLeftOfBudgetPeriod(){ + + } + +} \ No newline at end of file -- GitLab From 3e16cfad9455e7319a2575e386ee5b1be04bdd55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Tue, 21 Mar 2023 17:54:11 +0100 Subject: [PATCH 087/103] Refactored budget classes for easier reuse --- .../idatt1002/demo/data/Budget/Budget.java | 11 -- .../demo/data/Budget/BudgetItem.java | 27 ++++ .../demo/data/Budget/FoodBudget.java | 128 ------------------ .../demo/data/Budget/GeneralBudget.java | 99 ++++++++++++++ 4 files changed, 126 insertions(+), 139 deletions(-) delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java deleted file mode 100644 index 37e45c2d..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/Budget.java +++ /dev/null @@ -1,11 +0,0 @@ -package no.ntnu.idatt1002.demo.data.Budget; - -public interface Budget { - - void addToBudget(int AmountToAddToBudget); - void setAmountBudget(int amountBudget); - void setBudgetPeriod(int budgetPeriod); - void subtractFromBudget(int amountToSubtractFromBudget); -// public void divideBudgetInDifferentCategories(ExpenseCategory category); - -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java new file mode 100644 index 00000000..976a772a --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java @@ -0,0 +1,27 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + +public class BudgetItem { + private double budgetAmount; + private ExpenseCategory category; + private String description; + + public BudgetItem(double budgetAmount, String description, ExpenseCategory category){ + this.budgetAmount = budgetAmount; + this.description = description; + this.category = category; + } + + public double getBudgetAmount() { + return budgetAmount; + } + + public ExpenseCategory getCategory() { + return category; + } + + public String getDescription() { + return description; + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java deleted file mode 100644 index 58b34823..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudget.java +++ /dev/null @@ -1,128 +0,0 @@ -package no.ntnu.idatt1002.demo.data.Budget; - -import java.sql.Date; -import java.time.Duration; -import java.time.LocalDate; -import java.time.ZoneId; -import java.time.temporal.ChronoUnit; -import java.time.temporal.Temporal; -import java.util.Calendar; - -/** - * Class that represents FoodBudget and implements the Budget interface. - * - * @author Adele - */ -public class FoodBudget implements Budget { - - private int budgetAmount; - - private int budgetPeriod; - - /** - * The constructor of a new FoodBudget. - * - * @param budgetPeriod The period the budget are going to last for - * @param budgetAmount The amount that the budget consist of - * - */ - public FoodBudget(int budgetPeriod, int budgetAmount){ - this.budgetPeriod = budgetPeriod; - this.budgetAmount = budgetAmount; - } - - /** - * This method returns the amount in the foodBudget as an int - * - * @return the amount left in the foodBudget - * - */ - public int getBudgetAmount() { - return budgetAmount; - } - - /** - * - * @return - * - */ - public int getBudgetPeriod() { - return budgetPeriod; - } - - /** - * This method returns the amount of days left in the period as a string - * - * @return - * - */ - public long getDaysLeftOfBudgetPeriod() { - LocalDate today = LocalDate.now(); - LocalDate end = today.plus(Duration.ofDays(getBudgetPeriod())); - - Date todaysDate = (Date) Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant()); - Date endDate = (Date) Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant()); - - Calendar cStart = Calendar.getInstance(); cStart.setTime(todaysDate); - Calendar cEnd = Calendar.getInstance(); cEnd.setTime(endDate); - - long timeLeft = ChronoUnit.DAYS.between((Temporal) cStart, (Temporal) cEnd); - - return timeLeft; - } - - /** - * - * @param amountBudget - * - */ - @Override - public void setAmountBudget(int amountBudget) { - if (amountBudget < 0) { - throw new IllegalArgumentException("Illegal amount in budget"); - } - - budgetAmount = amountBudget; - } - - /** - * - * @param periodOfTheBudget - * - */ - @Override - public void setBudgetPeriod(int periodOfTheBudget) { - if (periodOfTheBudget < 0) { - throw new IllegalArgumentException("Illegal period for the budget"); - } - - budgetPeriod = periodOfTheBudget; - } - - /** - * - * @param amountToAddToBudget - * - */ - @Override - public void addToBudget(int amountToAddToBudget) { - if (amountToAddToBudget < 0){ - throw new IllegalArgumentException("Amount to be added to the budget cant be below zero"); - } - budgetAmount += amountToAddToBudget; - } - - /** - * - * @param amountToSubtractFromBudget - * - */ - @Override - public void subtractFromBudget(int amountToSubtractFromBudget) { - if (amountToSubtractFromBudget < 0){ - throw new IllegalArgumentException("Amount to be subtracted from the budget cant be below zero"); - } - budgetAmount -= amountToSubtractFromBudget; - } - -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java new file mode 100644 index 00000000..b1b86156 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -0,0 +1,99 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + +import java.sql.Date; +import java.time.Duration; +import java.time.LocalDate; +import java.time.ZoneId; +import java.time.temporal.ChronoUnit; +import java.time.temporal.Temporal; +import java.util.Calendar; +import java.util.List; + +/** + * Class that represents GeneralBudget and implements the Budget interface. + * + * @author Adele + */ +public class GeneralBudget { + + private int budgetPeriod; + private List<BudgetItem> listOfItems; + private double maxAmount; + + /** + * The constructor of a new GeneralBudget. + * + * @param budgetPeriod The period the budget are going to last for + * + */ + public GeneralBudget(int budgetPeriod, List<BudgetItem> listOfItems, double maxAmount){ + this.budgetPeriod = budgetPeriod; + this.maxAmount = maxAmount; + this.listOfItems = listOfItems; + + } + + /** + * This method returns the amount of days the budget should last for. + * + * @return amount of days the budget lasts + * + */ + public int getBudgetPeriod() { + return budgetPeriod; + } + + /** + * This method returns the amount of days left in the period as a string. + * + * @return the amount of days left in the budget period + * + */ + public long getDaysLeftOfBudgetPeriod() { + LocalDate today = LocalDate.now(); + LocalDate end = today.plus(Duration.ofDays(getBudgetPeriod())); + + Date todaysDate = (Date) Date.from(today.atStartOfDay(ZoneId.systemDefault()).toInstant()); + Date endDate = (Date) Date.from(end.atStartOfDay(ZoneId.systemDefault()).toInstant()); + + Calendar cStart = Calendar.getInstance(); cStart.setTime(todaysDate); + Calendar cEnd = Calendar.getInstance(); cEnd.setTime(endDate); + + return ChronoUnit.DAYS.between((Temporal) cStart, (Temporal) cEnd); + } + + public void addToBudget(double budgetAmount, String description, ExpenseCategory category) { + if (totalSum() + budgetAmount > maxAmount ){ + throw new IllegalArgumentException("Amount to be added goes over the max value of the budget"); + } + + if (!checksListOfItemsContainsBudgetItem(category)) { + listOfItems.add(new BudgetItem(budgetAmount, description, category)); + } else { + throw new IllegalArgumentException("There is already a budget with this category"); + } + } + + public boolean checksListOfItemsContainsBudgetItem(ExpenseCategory category){ + for (BudgetItem item : listOfItems) { + if (item.getCategory() == category) { + return true; + } + } + return false; + } + + public double totalSum(){ + double sum = 0; + for (BudgetItem budgetItem : listOfItems) { + sum += budgetItem.getBudgetAmount(); + } + return sum; + } + + public void deleteItemFromBudget(ExpenseCategory category){ + listOfItems.removeIf(item -> item.getCategory() == category); + } +} -- GitLab From 244deb09d0405cbd784360c307844fa4c7b3c6bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Wed, 22 Mar 2023 11:04:14 +0100 Subject: [PATCH 088/103] Add tests and some exception handling for GeneralBudget --- .../demo/data/Budget/GeneralBudget.java | 8 +- .../demo/data/Budget/FoodBudgetTest.java | 85 ---------------- .../demo/data/Budget/GeneralBudgetTest.java | 99 +++++++++++++++++++ 3 files changed, 106 insertions(+), 86 deletions(-) delete mode 100644 src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java create mode 100644 src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index b1b86156..d5965fae 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -29,10 +29,16 @@ public class GeneralBudget { * */ public GeneralBudget(int budgetPeriod, List<BudgetItem> listOfItems, double maxAmount){ + if (budgetPeriod < 0) { + throw new IllegalArgumentException("The period cant be under zero days"); + } + + if (maxAmount < 0) { + throw new IllegalArgumentException("The maxamount of the budget cant be under zero"); + } this.budgetPeriod = budgetPeriod; this.maxAmount = maxAmount; this.listOfItems = listOfItems; - } /** diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java deleted file mode 100644 index 22503bb9..00000000 --- a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/FoodBudgetTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package no.ntnu.idatt1002.demo.data.Budget; - -import org.junit.jupiter.api.Test; - -import static org.junit.jupiter.api.Assertions.*; - -class FoodBudgetTest { - - @Test - void set_amount_budget() { - FoodBudget foodBudget = new FoodBudget(30, 0); - int amount = 2000; - foodBudget.setAmountBudget(amount); - - assertEquals(amount, foodBudget.getBudgetAmount()); - } - - @Test - void set_amount_budget_negativ_amount() { - FoodBudget foodBudget = new FoodBudget(30, 0); - int amount = -2000; - - assertThrows(IllegalArgumentException.class, () -> foodBudget.setAmountBudget(amount)); - } - - @Test - void set_budget_period() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int period = 30; - foodBudget.setBudgetPeriod(period); - - assertEquals(period,foodBudget.getBudgetPeriod()); - } - - @Test - void set_budget_period_negative_period() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int period = -5; - - assertThrows(IllegalArgumentException.class, () -> foodBudget.setBudgetPeriod(period)); - } - - @Test - void add_to_budget_positive_number() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int beforeAdding = foodBudget.getBudgetAmount(); - int amountToBeAdded = 50; - foodBudget.addToBudget(amountToBeAdded); - - assertEquals(beforeAdding + amountToBeAdded, foodBudget.getBudgetAmount()); - - } - - @Test - void add_to_budget_negative_number() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int amountToBeAdded = -7; - - assertThrows(IllegalArgumentException.class, () -> foodBudget.addToBudget(amountToBeAdded)); - } - - @Test - void subtract_from_budget_positive_number() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int beforeSubtracting = foodBudget.getBudgetAmount(); - int amountToBeSubtracted = 50; - foodBudget.subtractFromBudget(amountToBeSubtracted); - - assertEquals(beforeSubtracting - amountToBeSubtracted, foodBudget.getBudgetAmount()); - } - - @Test - void subtract_from_budget_negative_number() { - FoodBudget foodBudget = new FoodBudget(0, 30); - int amountToBeSubtracted = -7; - - assertThrows(IllegalArgumentException.class, () -> foodBudget.subtractFromBudget(amountToBeSubtracted)); - } - - @Test - void getDaysLeftOfBudgetPeriod(){ - - } - -} \ No newline at end of file diff --git a/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java new file mode 100644 index 00000000..52b1a98e --- /dev/null +++ b/src/test/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudgetTest.java @@ -0,0 +1,99 @@ +package no.ntnu.idatt1002.demo.data.Budget; + +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; +import static org.junit.jupiter.api.Assertions.*; + +class GeneralBudgetTest { + @Test + @DisplayName("Constructor throws exception when the periodAmount is under zero") + void constructor_throws_exception_when_periodAmount_under_zero(){ + List<BudgetItem> list = new ArrayList<>(); + + assertThrows(IllegalArgumentException.class, () -> new GeneralBudget(-1, list, 1200)); + } + + @Test + @DisplayName("Constructor throws exception when maxAmount is under zero") + void constructor_throws_exception_when_maxAmount_under_zero(){ + List<BudgetItem> list = new ArrayList<>(); + + assertThrows(IllegalArgumentException.class, () -> new GeneralBudget(10, list, -12)); + } + + @Test + @DisplayName("AddToBudget throws when totalSum is higher than maxAmount ") + void add_to_budget_throws_when_totalSum_is_over_maxAmount(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + + assertThrows(IllegalArgumentException.class, () -> budget1.addToBudget(1300, "Food", ExpenseCategory.FOOD)); + } + + @Test + @DisplayName("addToBudget throws when a budget with same category already exists") + void add_to_budget_throws_when_a_budget_with_same_category_already_exists(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + budget1.addToBudget(500, "Food", ExpenseCategory.FOOD); + + assertThrows(IllegalArgumentException.class, () -> budget1.addToBudget(200, "Food", ExpenseCategory.FOOD)); + } + + @Test + @DisplayName("Adds a budget to generalBudget") + void add_budget_to_generalBudget(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + BudgetItem item1 = new BudgetItem(500, "Food", ExpenseCategory.FOOD); + budget1.addToBudget(500, "Food", ExpenseCategory.FOOD); + + assertEquals(1, list.size()); + } + + @Test + @DisplayName("Checks if the list contains a item with a given category") + void checks_if_the_list_contains_an_item_with_this_category_true(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + budget1.addToBudget(500, "Food", ExpenseCategory.FOOD); + + assertTrue(budget1.checksListOfItemsContainsBudgetItem(ExpenseCategory.FOOD)); + } + + @Test + @DisplayName("checks that the list does not contain an item with the given category") + void checks_if_the_list_contains_an_item_with_this_category_false(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + budget1.addToBudget(500, "Books", ExpenseCategory.BOOKS); + + assertFalse(budget1.checksListOfItemsContainsBudgetItem(ExpenseCategory.FOOD)); + } + + @Test + @DisplayName("Checks that the getTotalSum gives the correct number") + void get_total_sum_of_all_budgetItems_in_the_budget(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + budget1.addToBudget(500, "Books", ExpenseCategory.BOOKS); + budget1.addToBudget(300, "Food", ExpenseCategory.FOOD); + + assertEquals(800, budget1.totalSum()); + } + + @Test + @DisplayName("Checks that an item gets deleted from the budget") + void delete_from_budget(){ + List<BudgetItem> list = new ArrayList<>(); + GeneralBudget budget1 = new GeneralBudget(12, list, 1200); + budget1.addToBudget(500, "Books", ExpenseCategory.BOOKS); + budget1.deleteItemFromBudget(ExpenseCategory.BOOKS); + + assertTrue(list.isEmpty()); + } +} \ No newline at end of file -- GitLab From 4c5dad315719051c049f1d708b6159c10d03d434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Wed, 22 Mar 2023 11:24:25 +0100 Subject: [PATCH 089/103] Adds javadoc to BudgetItem and GeneralBudget --- .../demo/data/Budget/BudgetItem.java | 31 ++++++++++++++++++ .../demo/data/Budget/GeneralBudget.java | 32 ++++++++++++++++++- 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java index 976a772a..e3fa2d52 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java @@ -2,25 +2,56 @@ package no.ntnu.idatt1002.demo.data.Budget; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; +/** + * This class represents a budgetItem + * + * @author Adele + */ public class BudgetItem { private double budgetAmount; private ExpenseCategory category; private String description; + /** + * The constructor of a new Budgetitem. + * + * @param budgetAmount The amount of budget as a double + * @param description A description of the budget as a String + * @param category A category from ExpenseCategory + * + */ public BudgetItem(double budgetAmount, String description, ExpenseCategory category){ this.budgetAmount = budgetAmount; this.description = description; this.category = category; } + /** + * This method gets the BudgetAmount. + * + * @return the budgetAmount as a double + * + */ public double getBudgetAmount() { return budgetAmount; } + /** + * This method gets the category. + * + * @return the category as one of the categories in ExpenseCategory + * + */ public ExpenseCategory getCategory() { return category; } + /** + * This method gets the description. + * + * @return the description as a String + * + */ public String getDescription() { return description; } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index d5965fae..84e77940 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -12,7 +12,7 @@ import java.util.Calendar; import java.util.List; /** - * Class that represents GeneralBudget and implements the Budget interface. + * Class that represents GeneralBudget. * * @author Adele */ @@ -26,6 +26,8 @@ public class GeneralBudget { * The constructor of a new GeneralBudget. * * @param budgetPeriod The period the budget are going to last for + * @param listOfItems A list of BudgetItems + * @param maxAmount The maxAmount of the generalBudget as a double * */ public GeneralBudget(int budgetPeriod, List<BudgetItem> listOfItems, double maxAmount){ @@ -70,6 +72,14 @@ public class GeneralBudget { return ChronoUnit.DAYS.between((Temporal) cStart, (Temporal) cEnd); } + /** + * This method adds a budgetItem to the list of budgetItems in the generalBudget + * + * @param budgetAmount The amount of budget as a double + * @param description A description of the budget as a String + * @param category A category from ExpenseCategory + * + */ public void addToBudget(double budgetAmount, String description, ExpenseCategory category) { if (totalSum() + budgetAmount > maxAmount ){ throw new IllegalArgumentException("Amount to be added goes over the max value of the budget"); @@ -82,6 +92,14 @@ public class GeneralBudget { } } + /** + * This method checks if the list in the generalBudget already contains a budgetItem with a specified category. + * + * @param category A category from ExpenseCategory + * + * @return True if the list contains a budgetItem with this category and false if it does not + * + */ public boolean checksListOfItemsContainsBudgetItem(ExpenseCategory category){ for (BudgetItem item : listOfItems) { if (item.getCategory() == category) { @@ -91,6 +109,12 @@ public class GeneralBudget { return false; } + /** + * This method returns the totalSum of all budgetsItems in the list in the generalBudget + * + * @return the sum of the budgetsItems as a double + * + */ public double totalSum(){ double sum = 0; for (BudgetItem budgetItem : listOfItems) { @@ -99,6 +123,12 @@ public class GeneralBudget { return sum; } + /** + * This method deletes a budgetItem from the list in the GeneralBudget. + * + * @param category A category from ExpenseCategory + * + */ public void deleteItemFromBudget(ExpenseCategory category){ listOfItems.removeIf(item -> item.getCategory() == category); } -- GitLab From eae8e0823c499b035dd69ee07ec6852ce6351884 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Thu, 23 Mar 2023 14:02:16 +0100 Subject: [PATCH 090/103] started on budget frontend --- .../demo/controller/budgetController.java | 4 ++ src/main/resources/view/BudgetTuning.fxml | 44 +++++++++++++++++ src/main/resources/view/setBudgetPeriod.fxml | 47 +++++++++++++++++++ 3 files changed, 95 insertions(+) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java create mode 100644 src/main/resources/view/BudgetTuning.fxml create mode 100644 src/main/resources/view/setBudgetPeriod.fxml diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java new file mode 100644 index 00000000..569710ce --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java @@ -0,0 +1,4 @@ +package no.ntnu.idatt1002.demo.controller; + +public class budgetController { +} diff --git a/src/main/resources/view/BudgetTuning.fxml b/src/main/resources/view/BudgetTuning.fxml new file mode 100644 index 00000000..196b77be --- /dev/null +++ b/src/main/resources/view/BudgetTuning.fxml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.DatePicker?> +<?import javafx.scene.control.ProgressBar?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.VBox?> + + +<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> + <children> + <ImageView fitHeight="468.0" fitWidth="600.0" pickOnBounds="true"> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + </ImageView> + <GridPane prefHeight="468.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox prefHeight="200.0" prefWidth="100.0" GridPane.rowIndex="2"> + <children> + <DatePicker minWidth="200.0" /> + <Button minWidth="200.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="185.0" text="Button" /> + <Button minWidth="200.0" mnemonicParsing="false" text="Button" /> + <Button minWidth="200.0" mnemonicParsing="false" text="Button" /> + </children> + </VBox> + <ProgressBar prefHeight="20.0" prefWidth="200.0" progress="0.0" GridPane.rowIndex="1" /> + </children> + </GridPane> + </children> +</AnchorPane> diff --git a/src/main/resources/view/setBudgetPeriod.fxml b/src/main/resources/view/setBudgetPeriod.fxml new file mode 100644 index 00000000..ad3a7803 --- /dev/null +++ b/src/main/resources/view/setBudgetPeriod.fxml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.*?> +<?import javafx.scene.control.*?> +<?import javafx.scene.layout.*?> + +<GridPane hgap="10.0" prefWidth="442.0" vgap="10.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="51.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="149.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <padding> + <Insets bottom="10.0" left="10.0" top="20.0" /> + </padding> + <children> + <Label text="Budget Period:" /> + <Label text="Start Date:" GridPane.rowIndex="1" /> + <Label text="End Date:" GridPane.rowIndex="2" /> + <HBox alignment="CENTER" fillHeight="false" prefHeight="30.0" prefWidth="154.0" GridPane.columnIndex="3" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.rowSpan="2147483647"> + <GridPane.margin> + <Insets /> + </GridPane.margin> + <children> + <Button mnemonicParsing="false" onAction="#closeButton" prefHeight="25.0" prefWidth="60.0" text="Cancel" /> + <Button mnemonicParsing="false" onAction="#closeButton" prefHeight="25.0" prefWidth="60.0" text="Add" /> + </children> + <opaqueInsets> + <Insets /> + </opaqueInsets> + </HBox> + <DatePicker prefWidth="150.0" showWeekNumbers="true" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> + <DatePicker prefWidth="150.0" showWeekNumbers="true" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> + <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.columnSpan="2"> + <children> + <TextField prefWidth="125.0" promptText="Title" /> + <Button mnemonicParsing="false" prefWidth="25.0" text="?" /> + </children> + </HBox> + </children> +</GridPane> -- GitLab From 2cd7b4f0f95f867b3c3f45f427bc0fc14e5cd52c Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Thu, 23 Mar 2023 15:52:49 +0100 Subject: [PATCH 091/103] work on budget frontend --- src/main/resources/view/BudgetTuning.fxml | 60 ++++++++++++++++++----- 1 file changed, 47 insertions(+), 13 deletions(-) diff --git a/src/main/resources/view/BudgetTuning.fxml b/src/main/resources/view/BudgetTuning.fxml index 196b77be..f187271c 100644 --- a/src/main/resources/view/BudgetTuning.fxml +++ b/src/main/resources/view/BudgetTuning.fxml @@ -1,15 +1,19 @@ <?xml version="1.0" encoding="UTF-8"?> <?import javafx.scene.control.Button?> -<?import javafx.scene.control.DatePicker?> -<?import javafx.scene.control.ProgressBar?> +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.control.TextArea?> <?import javafx.scene.image.Image?> <?import javafx.scene.image.ImageView?> <?import javafx.scene.layout.AnchorPane?> <?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.FlowPane?> <?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.RowConstraints?> -<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> <AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> @@ -24,20 +28,50 @@ <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> </columnConstraints> <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="397.0" minHeight="10.0" prefHeight="61.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="397.0" minHeight="10.0" prefHeight="41.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="397.0" minHeight="10.0" prefHeight="296.0" vgrow="SOMETIMES" /> + <RowConstraints maxHeight="152.0" minHeight="10.0" prefHeight="79.0" vgrow="SOMETIMES" /> </rowConstraints> <children> - <VBox prefHeight="200.0" prefWidth="100.0" GridPane.rowIndex="2"> + <HBox alignment="TOP_CENTER" prefHeight="98.0" prefWidth="600.0" spacing="40.0" GridPane.rowIndex="3"> <children> - <DatePicker minWidth="200.0" /> - <Button minWidth="200.0" mnemonicParsing="false" prefHeight="26.0" prefWidth="185.0" text="Button" /> - <Button minWidth="200.0" mnemonicParsing="false" text="Button" /> - <Button minWidth="200.0" mnemonicParsing="false" text="Button" /> + <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Add Budget Item" /> + <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Edit/Remove" /> + <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Save/Back" /> </children> - </VBox> - <ProgressBar prefHeight="20.0" prefWidth="200.0" progress="0.0" GridPane.rowIndex="1" /> + </HBox> + <FlowPane alignment="CENTER" columnHalignment="CENTER" prefHeight="200.0" prefWidth="200.0" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER"> + <children> + <TableView prefHeight="260.0" prefWidth="540.0"> + <columns> + <TableColumn prefWidth="75.0" text="Category/Title" /> + <TableColumn prefWidth="75.0" text="Percentage" /> + <TableColumn prefWidth="75.0" text="Amount" /> + <TableColumn prefWidth="75.0" text="Description" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + </children> + </FlowPane> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Budget"> + <font> + <Font size="48.0" /> + </font> + </Text> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" GridPane.rowIndex="1"> + <children> + <TextArea fx:id="monthVariable" prefHeight="20.0" prefWidth="180.0" text="Month" /> + <TextArea fx:id="daysVariable" prefHeight="20.0" prefWidth="180.0" text="Days" /> + <TextArea fx:id="totalBudgetAmount" prefHeight="20.0" prefWidth="180.0" text="Amount" /> + </children> + </HBox> </children> </GridPane> </children> -- GitLab From 0814d6b0b4809a7262b0941ac86dde1e9639f8d2 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Thu, 23 Mar 2023 16:33:03 +0100 Subject: [PATCH 092/103] added the popup window for adding a budget item --- src/main/resources/view/addBudget.fxml | 58 ++++++++++++++++++++ src/main/resources/view/setBudgetPeriod.fxml | 47 ---------------- 2 files changed, 58 insertions(+), 47 deletions(-) create mode 100644 src/main/resources/view/addBudget.fxml delete mode 100644 src/main/resources/view/setBudgetPeriod.fxml diff --git a/src/main/resources/view/addBudget.fxml b/src/main/resources/view/addBudget.fxml new file mode 100644 index 00000000..7dbedf0e --- /dev/null +++ b/src/main/resources/view/addBudget.fxml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.Label?> +<?import javafx.scene.control.TextField?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Text?> + + +<AnchorPane prefHeight="150.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.BudgetController"> + <children> + <GridPane prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="10.0" minWidth="0.0" prefWidth="0.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="180.0" minWidth="10.0" prefWidth="150.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="180.0" minWidth="10.0" prefWidth="150.0" /> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + <ColumnConstraints hgrow="SOMETIMES" maxWidth="10.0" minWidth="10.0" prefWidth="0.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox alignment="CENTER" GridPane.columnIndex="1" GridPane.valignment="CENTER"> + <children> + <Label text="Label" /> + <ComboBox fx:id="categoryVariable" maxWidth="150.0" prefWidth="150.0" /> + </children> + </VBox> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" GridPane.columnIndex="2"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Amount" /> + <TextField fx:id="amountVariable" maxWidth="150.0" /> + </children> + </VBox> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" GridPane.columnIndex="3"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Description" /> + <TextField fx:id="descriptionVariable" /> + </children> + </VBox> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="15.0" GridPane.columnIndex="3" GridPane.rowIndex="1"> + <children> + <Button mnemonicParsing="false" onAction="#closeButton" text="Button" /> + <Button mnemonicParsing="false" onAction="#addBudget" text="Button" /> + </children> + </HBox> + </children> + </GridPane> + </children> +</AnchorPane> diff --git a/src/main/resources/view/setBudgetPeriod.fxml b/src/main/resources/view/setBudgetPeriod.fxml deleted file mode 100644 index ad3a7803..00000000 --- a/src/main/resources/view/setBudgetPeriod.fxml +++ /dev/null @@ -1,47 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> - -<?import javafx.geometry.*?> -<?import javafx.scene.control.*?> -<?import javafx.scene.layout.*?> - -<GridPane hgap="10.0" prefWidth="442.0" vgap="10.0" xmlns="http://javafx.com/javafx/17.0.2-ea" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> - <columnConstraints> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> - <ColumnConstraints hgrow="SOMETIMES" maxWidth="95.0" minWidth="10.0" prefWidth="51.0" /> - <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" maxWidth="189.0" minWidth="10.0" prefWidth="149.0" /> - </columnConstraints> - <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> - </rowConstraints> - <padding> - <Insets bottom="10.0" left="10.0" top="20.0" /> - </padding> - <children> - <Label text="Budget Period:" /> - <Label text="Start Date:" GridPane.rowIndex="1" /> - <Label text="End Date:" GridPane.rowIndex="2" /> - <HBox alignment="CENTER" fillHeight="false" prefHeight="30.0" prefWidth="154.0" GridPane.columnIndex="3" GridPane.columnSpan="2147483647" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.rowSpan="2147483647"> - <GridPane.margin> - <Insets /> - </GridPane.margin> - <children> - <Button mnemonicParsing="false" onAction="#closeButton" prefHeight="25.0" prefWidth="60.0" text="Cancel" /> - <Button mnemonicParsing="false" onAction="#closeButton" prefHeight="25.0" prefWidth="60.0" text="Add" /> - </children> - <opaqueInsets> - <Insets /> - </opaqueInsets> - </HBox> - <DatePicker prefWidth="150.0" showWeekNumbers="true" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="1" /> - <DatePicker prefWidth="150.0" showWeekNumbers="true" GridPane.columnIndex="1" GridPane.columnSpan="2" GridPane.rowIndex="2" /> - <HBox prefHeight="100.0" prefWidth="200.0" GridPane.columnIndex="1" GridPane.columnSpan="2"> - <children> - <TextField prefWidth="125.0" promptText="Title" /> - <Button mnemonicParsing="false" prefWidth="25.0" text="?" /> - </children> - </HBox> - </children> -</GridPane> -- GitLab From 20b0ee47c00666670f2680aeb178acb1285fdfdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adele=20Iren=20Westrum=20Kj=C3=B8lstad?= <aikjolst@stud.ntnu.no> Date: Thu, 23 Mar 2023 16:39:42 +0100 Subject: [PATCH 093/103] Started creating methods for addBudget button --- src/.idea/.gitignore | 8 ++++ src/.idea/checkstyle-idea.xml | 15 +++++++ src/.idea/misc.xml | 6 +++ src/.idea/modules.xml | 10 +++++ src/.idea/vcs.xml | 6 +++ .../demo/controller/budgetController.java | 41 +++++++++++++++++++ 6 files changed, 86 insertions(+) create mode 100644 src/.idea/.gitignore create mode 100644 src/.idea/checkstyle-idea.xml create mode 100644 src/.idea/misc.xml create mode 100644 src/.idea/modules.xml create mode 100644 src/.idea/vcs.xml diff --git a/src/.idea/.gitignore b/src/.idea/.gitignore new file mode 100644 index 00000000..13566b81 --- /dev/null +++ b/src/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/src/.idea/checkstyle-idea.xml b/src/.idea/checkstyle-idea.xml new file mode 100644 index 00000000..d943f1e3 --- /dev/null +++ b/src/.idea/checkstyle-idea.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="CheckStyle-IDEA" serialisationVersion="2"> + <checkstyleVersion>10.6.0</checkstyleVersion> + <scanScope>JavaOnly</scanScope> + <option name="thirdPartyClasspath" /> + <option name="activeLocationIds" /> + <option name="locations"> + <list> + <ConfigurationLocation id="bundled-sun-checks" type="BUNDLED" scope="All" description="Sun Checks">(bundled)</ConfigurationLocation> + <ConfigurationLocation id="bundled-google-checks" type="BUNDLED" scope="All" description="Google Checks">(bundled)</ConfigurationLocation> + </list> + </option> + </component> +</project> \ No newline at end of file diff --git a/src/.idea/misc.xml b/src/.idea/misc.xml new file mode 100644 index 00000000..639900d1 --- /dev/null +++ b/src/.idea/misc.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project> \ No newline at end of file diff --git a/src/.idea/modules.xml b/src/.idea/modules.xml new file mode 100644 index 00000000..31eaf94d --- /dev/null +++ b/src/.idea/modules.xml @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/main/main.iml" filepath="$PROJECT_DIR$/main/main.iml" /> + <module fileurl="file://$PROJECT_DIR$/.idea/src.iml" filepath="$PROJECT_DIR$/.idea/src.iml" /> + <module fileurl="file://$PROJECT_DIR$/test/test.iml" filepath="$PROJECT_DIR$/test/test.iml" /> + </modules> + </component> +</project> \ No newline at end of file diff --git a/src/.idea/vcs.xml b/src/.idea/vcs.xml new file mode 100644 index 00000000..6c0b8635 --- /dev/null +++ b/src/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="$PROJECT_DIR$/.." vcs="Git" /> + </component> +</project> \ No newline at end of file diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java index 569710ce..ee8b261e 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java @@ -1,4 +1,45 @@ package no.ntnu.idatt1002.demo.controller; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; +import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + +import java.awt.*; +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.List; + public class budgetController { + @FXML + private TextField categoryVariable; + @FXML + private TextField amountVariable; + @FXML + private TextField descriptionVariable; + private List<BudgetItem> listOfBudgetItems = new ArrayList<>(); + private GeneralBudget general = new GeneralBudget(31, listOfBudgetItems, 200); + + public void addBudget(ActionEvent event){ + int amount = Integer.parseInt(String.valueOf(amountVariable)); + String description = String.valueOf(descriptionVariable); + ExpenseCategory category = ExpenseCategory.valueOf(String.valueOf(categoryVariable)); + + general.addToBudget(amount,description,category); + } + + public void deleteFromBudget(ActionEvent event){ + ExpenseCategory category = ExpenseCategory.valueOf(String.valueOf(categoryVariable)); + + general.deleteItemFromBudget(category); + } + + public void closeButton(javafx.event.ActionEvent actionEvent) { + final Node source = (Node) actionEvent.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + stage.close(); + } + } -- GitLab From 579661827b1ef389caf0bf62612c791eac2062e9 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Thu, 23 Mar 2023 18:36:19 +0100 Subject: [PATCH 094/103] working list in budget window --- .../demo/controller/AddBudgetController.java | 65 ++++++++++ .../demo/controller/BudgetController.java | 118 ++++++++++++++++++ .../demo/controller/budgetController.java | 45 ------- .../demo/data/Budget/GeneralBudget.java | 5 + .../no/ntnu/idatt1002/demo/view/MyApp.java | 2 +- .../view/{addBudget.fxml => AddBudget.fxml} | 9 +- .../view/{BudgetTuning.fxml => Budget.fxml} | 19 ++- src/main/resources/view/deleteBudget.fxml | 44 +++++++ 8 files changed, 246 insertions(+), 61 deletions(-) create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java create mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java delete mode 100644 src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java rename src/main/resources/view/{addBudget.fxml => AddBudget.fxml} (89%) rename src/main/resources/view/{BudgetTuning.fxml => Budget.fxml} (77%) create mode 100644 src/main/resources/view/deleteBudget.fxml diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java new file mode 100644 index 00000000..b8cb92cc --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java @@ -0,0 +1,65 @@ +package no.ntnu.idatt1002.demo.controller; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; + +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + +public class AddBudgetController { + + BudgetItem newBudgetItem = null; + + BudgetItem chosenBudgetItem = null; + @FXML + private ComboBox<ExpenseCategory> categoryVariable; + @FXML + private TextField amountVariable; + @FXML + private TextField descriptionVariable; + + + @FXML + private Button addButton; + + @FXML + private Button cancelButton; + + @FXML + public void initialize(){ + ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList( + ExpenseCategory.values()); + categoryVariable.setItems(expenseCategories); + categoryVariable.setPromptText("What"); + } + + public ExpenseCategory getCategory(){ + return categoryVariable.getValue(); + } + + public BudgetItem getNewBudgetItem(){ + return this.newBudgetItem; + } + + @FXML + public void addBudget(ActionEvent event) { + if(newBudgetItem == null){ + ExpenseCategory category = getCategory(); + System.out.println(category.toString()); + double amount = Double.parseDouble(amountVariable.getText()); + System.out.println(amount); + String description = descriptionVariable.getText(); + newBudgetItem = new BudgetItem(amount, description, category); + } + final Node source = (Node) event.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + stage.close(); + } +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java new file mode 100644 index 00000000..71eddb46 --- /dev/null +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -0,0 +1,118 @@ +package no.ntnu.idatt1002.demo.controller; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.control.*; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.stage.Modality; +import javafx.stage.Stage; +import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; +import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; +import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; + + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +enum DialogMode{ + ADD, EDIT, DELETE +} + +public class BudgetController { + + private DialogMode dialogMode; + private List<BudgetItem> listOfBudgetItems = new ArrayList<>(); + private GeneralBudget general = new GeneralBudget(31, listOfBudgetItems, 200); + + + @FXML + private Button addBudget; + + @FXML + private TableColumn<BudgetItem, Double> amountColumn; + + @FXML + private TableView<BudgetItem> budgetTableView = new TableView<>(); + + @FXML + private TableColumn<BudgetItem, ExpenseCategory> categoryColumn; + + @FXML + private TextArea daysVariable; + + @FXML + private TableColumn<BudgetItem, String> descriptionColumn; + + @FXML + private TextArea monthVariable; + + @FXML + private TableColumn<?, ?> percentageColumn; + + @FXML + private TextArea totalBudgetAmount; + + @FXML + private ObservableList<BudgetItem> budgetList; + + + + public void initialize() throws IOException { + budgetList = FXCollections.observableArrayList(listOfBudgetItems); + budgetTableView.setItems(budgetList); + + categoryColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, ExpenseCategory>("category")); + amountColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, Double>("budgetAmount")); + descriptionColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, String>("description")); + } + @FXML + public void switchAddBudget(javafx.event.ActionEvent event) throws IOException { + BudgetItem item = null; + String dialogTitle = ""; + FXMLLoader loader = new FXMLLoader(SceneController.class.getResource("/view/AddBudget.fxml")); + Dialog<BudgetItem> dialog = new Dialog<>(); + dialog.initModality(Modality.APPLICATION_MODAL); + + try{ + dialog.getDialogPane().setContent(loader.load()); + } catch(IOException e) { + e.printStackTrace(); + } + + AddBudgetController budgetController = loader.getController(); + + if(event.getSource().equals(addBudget)){ + dialogMode = DialogMode.ADD; + dialogTitle = "New Budget"; + + } + + dialog.setTitle(dialogTitle); + dialog.showAndWait(); + + item = budgetController.getNewBudgetItem(); + if(item != null && dialogMode == DialogMode.ADD){ + System.out.println("It is too dark"); + listOfBudgetItems.add(item); + refreshObservableList(); + } + } + + @FXML + public void closeButton(javafx.event.ActionEvent actionEvent) { + final Node source = (Node) actionEvent.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + stage.close(); + } + + + protected void refreshObservableList(){ + budgetTableView.setItems(budgetList); + this.budgetList.setAll(listOfBudgetItems); + } + +} diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java deleted file mode 100644 index ee8b261e..00000000 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/budgetController.java +++ /dev/null @@ -1,45 +0,0 @@ -package no.ntnu.idatt1002.demo.controller; - -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.stage.Stage; -import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; -import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; -import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; - -import java.awt.*; -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.List; - -public class budgetController { - @FXML - private TextField categoryVariable; - @FXML - private TextField amountVariable; - @FXML - private TextField descriptionVariable; - private List<BudgetItem> listOfBudgetItems = new ArrayList<>(); - private GeneralBudget general = new GeneralBudget(31, listOfBudgetItems, 200); - - public void addBudget(ActionEvent event){ - int amount = Integer.parseInt(String.valueOf(amountVariable)); - String description = String.valueOf(descriptionVariable); - ExpenseCategory category = ExpenseCategory.valueOf(String.valueOf(categoryVariable)); - - general.addToBudget(amount,description,category); - } - - public void deleteFromBudget(ActionEvent event){ - ExpenseCategory category = ExpenseCategory.valueOf(String.valueOf(categoryVariable)); - - general.deleteItemFromBudget(category); - } - - public void closeButton(javafx.event.ActionEvent actionEvent) { - final Node source = (Node) actionEvent.getSource(); - final Stage stage = (Stage) source.getScene().getWindow(); - stage.close(); - } - -} diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index 84e77940..b5aa08ba 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -132,4 +132,9 @@ public class GeneralBudget { public void deleteItemFromBudget(ExpenseCategory category){ listOfItems.removeIf(item -> item.getCategory() == category); } + + + public List<BudgetItem> getListOfItems() { + return listOfItems; + } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java b/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java index 171e4e68..59881bea 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java +++ b/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java @@ -12,7 +12,7 @@ public class MyApp extends Application { @Override public void start(Stage stage) throws IOException { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/FirstMenu.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/Budget.fxml")); Parent root = loader.load(); Scene scene = new Scene(root); stage.setScene(scene); diff --git a/src/main/resources/view/addBudget.fxml b/src/main/resources/view/AddBudget.fxml similarity index 89% rename from src/main/resources/view/addBudget.fxml rename to src/main/resources/view/AddBudget.fxml index 7dbedf0e..90dbc58d 100644 --- a/src/main/resources/view/addBudget.fxml +++ b/src/main/resources/view/AddBudget.fxml @@ -12,8 +12,7 @@ <?import javafx.scene.layout.VBox?> <?import javafx.scene.text.Text?> - -<AnchorPane prefHeight="150.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.BudgetController"> +<AnchorPane prefHeight="150.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.AddBudgetController"> <children> <GridPane prefHeight="400.0" prefWidth="600.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> <columnConstraints> @@ -30,7 +29,7 @@ <children> <VBox alignment="CENTER" GridPane.columnIndex="1" GridPane.valignment="CENTER"> <children> - <Label text="Label" /> + <Label text="Category/Title" /> <ComboBox fx:id="categoryVariable" maxWidth="150.0" prefWidth="150.0" /> </children> </VBox> @@ -48,8 +47,8 @@ </VBox> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="15.0" GridPane.columnIndex="3" GridPane.rowIndex="1"> <children> - <Button mnemonicParsing="false" onAction="#closeButton" text="Button" /> - <Button mnemonicParsing="false" onAction="#addBudget" text="Button" /> + <Button fx:id="cancelButton" mnemonicParsing="false" text="Cancel" /> + <Button fx:id="addButton" mnemonicParsing="false" onAction="#addBudget" text="Add New Budget" /> </children> </HBox> </children> diff --git a/src/main/resources/view/BudgetTuning.fxml b/src/main/resources/view/Budget.fxml similarity index 77% rename from src/main/resources/view/BudgetTuning.fxml rename to src/main/resources/view/Budget.fxml index f187271c..fad58615 100644 --- a/src/main/resources/view/BudgetTuning.fxml +++ b/src/main/resources/view/Budget.fxml @@ -15,8 +15,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> - -<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.SceneController"> +<AnchorPane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.BudgetController"> <children> <ImageView fitHeight="468.0" fitWidth="600.0" pickOnBounds="true"> <image> @@ -36,19 +35,19 @@ <children> <HBox alignment="TOP_CENTER" prefHeight="98.0" prefWidth="600.0" spacing="40.0" GridPane.rowIndex="3"> <children> - <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Add Budget Item" /> - <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Edit/Remove" /> - <Button minHeight="80.0" minWidth="100.0" mnemonicParsing="false" onAction="#underProgress" prefWidth="100.0" text="Save/Back" /> + <Button fx:id="addBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Add Budget Item" /> + <Button minHeight="60.0" minWidth="100.0" mnemonicParsing="false" prefWidth="100.0" text="Edit/Remove" /> + <Button minHeight="60.0" minWidth="100.0" mnemonicParsing="false" prefWidth="100.0" text="Save/Back" /> </children> </HBox> <FlowPane alignment="CENTER" columnHalignment="CENTER" prefHeight="200.0" prefWidth="200.0" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER"> <children> - <TableView prefHeight="260.0" prefWidth="540.0"> + <TableView fx:id="budgetTableView" prefHeight="260.0" prefWidth="540.0"> <columns> - <TableColumn prefWidth="75.0" text="Category/Title" /> - <TableColumn prefWidth="75.0" text="Percentage" /> - <TableColumn prefWidth="75.0" text="Amount" /> - <TableColumn prefWidth="75.0" text="Description" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category/Title" /> + <TableColumn fx:id="percentageColumn" prefWidth="75.0" text="Percentage" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> </columns> <columnResizePolicy> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> diff --git a/src/main/resources/view/deleteBudget.fxml b/src/main/resources/view/deleteBudget.fxml new file mode 100644 index 00000000..21b63909 --- /dev/null +++ b/src/main/resources/view/deleteBudget.fxml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.text.Text?> + +<AnchorPane prefHeight="150.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="view.DeleteBudget"> + <children> + <GridPane layoutX="23.0" layoutY="30.0" prefHeight="150.0" prefWidth="400.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> + <columnConstraints> + <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Delete Budget Item" /> + </children> + </VBox> + <HBox alignment="CENTER" GridPane.rowIndex="1"> + <children> + <ComboBox prefWidth="150.0" /> + </children> + </HBox> + <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="15.0" GridPane.rowIndex="2"> + <children> + <Button mnemonicParsing="false" text="Cancel" /> + <Button mnemonicParsing="false" text="Delete" /> + </children> + </HBox> + </children> + </GridPane> + </children> +</AnchorPane> -- GitLab From aa2d425fc698e3aede94198fce7bbf3bd1599f73 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Fri, 24 Mar 2023 10:35:43 +0100 Subject: [PATCH 095/103] small changes to button names in budget ui --- .../no/ntnu/idatt1002/demo/controller/AddBudgetController.java | 2 +- .../no/ntnu/idatt1002/demo/controller/BudgetController.java | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java index b8cb92cc..eec2668b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java @@ -37,7 +37,7 @@ public class AddBudgetController { ObservableList<ExpenseCategory> expenseCategories = FXCollections.observableArrayList( ExpenseCategory.values()); categoryVariable.setItems(expenseCategories); - categoryVariable.setPromptText("What"); + categoryVariable.setPromptText("Category"); } public ExpenseCategory getCategory(){ diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java index 71eddb46..2c3c80c0 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -96,7 +96,6 @@ public class BudgetController { item = budgetController.getNewBudgetItem(); if(item != null && dialogMode == DialogMode.ADD){ - System.out.println("It is too dark"); listOfBudgetItems.add(item); refreshObservableList(); } -- GitLab From bd618c50fdc680286de7f73772ecbbd3904b13ae Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Fri, 24 Mar 2023 11:26:45 +0100 Subject: [PATCH 096/103] working edit and add function to budget --- .../demo/controller/AddBudgetController.java | 26 +++++++++++++ .../demo/controller/BudgetController.java | 19 +++++++-- .../demo/data/Budget/BudgetItem.java | 39 +++++++++++++------ .../demo/data/Budget/GeneralBudget.java | 4 +- src/main/resources/view/AddBudget.fxml | 2 +- src/main/resources/view/Budget.fxml | 2 +- 6 files changed, 74 insertions(+), 18 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java index eec2668b..d9281ff1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java @@ -58,8 +58,34 @@ public class AddBudgetController { String description = descriptionVariable.getText(); newBudgetItem = new BudgetItem(amount, description, category); } + if(chosenBudgetItem != null){ + chosenBudgetItem.setBudgetAmount(Double.parseDouble(amountVariable.getText())); + chosenBudgetItem.setBudgetDescription(descriptionVariable.getText()); + chosenBudgetItem.setBudgetCategory(categoryVariable.getValue()); + } final Node source = (Node) event.getSource(); final Stage stage = (Stage) source.getScene().getWindow(); stage.close(); } + + @FXML + public void setBudget(BudgetItem item){ + chosenBudgetItem = new BudgetItem(item.getBudgetAmount(), item.getBudgetDescription(), item.getBudgetCategory()); + chosenBudgetItem.getAmountProperty().bindBidirectional(item.getAmountProperty()); + chosenBudgetItem.getDescriptionProperty().bindBidirectional(item.getDescriptionProperty()); + chosenBudgetItem.getCategoryProperty().bindBidirectional(item.getCategoryProperty()); + amountVariable.textProperty().set(String.valueOf(item.getBudgetAmount())); + descriptionVariable.textProperty().set(item.getBudgetDescription()); + categoryVariable.setValue(item.getBudgetCategory()); + + + } + + + + public void closeButton(ActionEvent actionEvent) { + final Node source = (Node) actionEvent.getSource(); + final Stage stage = (Stage) source.getScene().getWindow(); + stage.close(); + } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java index 2c3c80c0..821fe2cf 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -2,6 +2,7 @@ package no.ntnu.idatt1002.demo.controller; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.Node; @@ -11,6 +12,7 @@ import javafx.stage.Modality; import javafx.stage.Stage; import no.ntnu.idatt1002.demo.data.Budget.BudgetItem; import no.ntnu.idatt1002.demo.data.Budget.GeneralBudget; +import no.ntnu.idatt1002.demo.data.Economics.Expense; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; @@ -32,6 +34,9 @@ public class BudgetController { @FXML private Button addBudget; + @FXML + private Button editBudget; + @FXML private TableColumn<BudgetItem, Double> amountColumn; @@ -65,9 +70,9 @@ public class BudgetController { budgetList = FXCollections.observableArrayList(listOfBudgetItems); budgetTableView.setItems(budgetList); - categoryColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, ExpenseCategory>("category")); + categoryColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, ExpenseCategory>("budgetCategory")); amountColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, Double>("budgetAmount")); - descriptionColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, String>("description")); + descriptionColumn.setCellValueFactory(new PropertyValueFactory<BudgetItem, String>("budgetDescription")); } @FXML public void switchAddBudget(javafx.event.ActionEvent event) throws IOException { @@ -90,6 +95,14 @@ public class BudgetController { dialogTitle = "New Budget"; } + else if (event.getSource().equals(editBudget)) { + dialogMode = DialogMode.EDIT; + dialogTitle = "Edit expense"; + item = budgetTableView.getSelectionModel().getSelectedItem(); + budgetController.setBudget(item); + } else { + return; + } dialog.setTitle(dialogTitle); dialog.showAndWait(); @@ -97,8 +110,8 @@ public class BudgetController { item = budgetController.getNewBudgetItem(); if(item != null && dialogMode == DialogMode.ADD){ listOfBudgetItems.add(item); - refreshObservableList(); } + refreshObservableList(); } @FXML diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java index e3fa2d52..0b1a38a3 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java @@ -1,5 +1,6 @@ package no.ntnu.idatt1002.demo.data.Budget; +import javafx.beans.property.*; import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; /** @@ -8,9 +9,9 @@ import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; * @author Adele */ public class BudgetItem { - private double budgetAmount; - private ExpenseCategory category; - private String description; + private DoubleProperty budgetAmount; + private ObjectProperty<ExpenseCategory> budgetCategory; + private StringProperty budgetDescription; /** * The constructor of a new Budgetitem. @@ -21,9 +22,9 @@ public class BudgetItem { * */ public BudgetItem(double budgetAmount, String description, ExpenseCategory category){ - this.budgetAmount = budgetAmount; - this.description = description; - this.category = category; + this.budgetAmount = new SimpleDoubleProperty(budgetAmount); + this.budgetDescription = new SimpleStringProperty(description); + this.budgetCategory = new SimpleObjectProperty<ExpenseCategory>(category); } /** @@ -33,7 +34,13 @@ public class BudgetItem { * */ public double getBudgetAmount() { - return budgetAmount; + return budgetAmount.get(); + } + + public DoubleProperty getAmountProperty(){ return budgetAmount;} + + public void setBudgetAmount(double amount){ + this.budgetAmount.set(amount); } /** @@ -42,8 +49,13 @@ public class BudgetItem { * @return the category as one of the categories in ExpenseCategory * */ - public ExpenseCategory getCategory() { - return category; + public ExpenseCategory getBudgetCategory() { + return budgetCategory.get(); + } + public ObjectProperty<ExpenseCategory> getCategoryProperty(){ return budgetCategory; } + + public void setBudgetCategory(ExpenseCategory category){ + this.budgetCategory.set(category); } /** @@ -52,7 +64,12 @@ public class BudgetItem { * @return the description as a String * */ - public String getDescription() { - return description; + public String getBudgetDescription() { + return budgetDescription.get(); + } + public StringProperty getDescriptionProperty(){ return budgetDescription; } + + public void setBudgetDescription(String description){ + this.budgetDescription.set(description); } } diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java index b5aa08ba..45e7216c 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/GeneralBudget.java @@ -102,7 +102,7 @@ public class GeneralBudget { */ public boolean checksListOfItemsContainsBudgetItem(ExpenseCategory category){ for (BudgetItem item : listOfItems) { - if (item.getCategory() == category) { + if (item.getBudgetCategory() == category) { return true; } } @@ -130,7 +130,7 @@ public class GeneralBudget { * */ public void deleteItemFromBudget(ExpenseCategory category){ - listOfItems.removeIf(item -> item.getCategory() == category); + listOfItems.removeIf(item -> item.getBudgetCategory() == category); } diff --git a/src/main/resources/view/AddBudget.fxml b/src/main/resources/view/AddBudget.fxml index 90dbc58d..96e52a73 100644 --- a/src/main/resources/view/AddBudget.fxml +++ b/src/main/resources/view/AddBudget.fxml @@ -47,7 +47,7 @@ </VBox> <HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0" spacing="15.0" GridPane.columnIndex="3" GridPane.rowIndex="1"> <children> - <Button fx:id="cancelButton" mnemonicParsing="false" text="Cancel" /> + <Button fx:id="cancelButton" mnemonicParsing="false" onAction="#closeButton" text="Cancel" /> <Button fx:id="addButton" mnemonicParsing="false" onAction="#addBudget" text="Add New Budget" /> </children> </HBox> diff --git a/src/main/resources/view/Budget.fxml b/src/main/resources/view/Budget.fxml index fad58615..5138187c 100644 --- a/src/main/resources/view/Budget.fxml +++ b/src/main/resources/view/Budget.fxml @@ -36,7 +36,7 @@ <HBox alignment="TOP_CENTER" prefHeight="98.0" prefWidth="600.0" spacing="40.0" GridPane.rowIndex="3"> <children> <Button fx:id="addBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Add Budget Item" /> - <Button minHeight="60.0" minWidth="100.0" mnemonicParsing="false" prefWidth="100.0" text="Edit/Remove" /> + <Button fx:id="editBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Edit/Remove" /> <Button minHeight="60.0" minWidth="100.0" mnemonicParsing="false" prefWidth="100.0" text="Save/Back" /> </children> </HBox> -- GitLab From 8d6bfec53400b3f326b3d1cdef6d7018ae086c98 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Fri, 24 Mar 2023 11:55:56 +0100 Subject: [PATCH 097/103] functioning delete button for budget window --- .../demo/controller/AddBudgetController.java | 6 ----- .../demo/controller/BudgetController.java | 23 +++++++++++++++++++ .../demo/data/Budget/BudgetItem.java | 2 +- src/main/resources/view/Budget.fxml | 5 ++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java index d9281ff1..600d4a37 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/AddBudgetController.java @@ -52,9 +52,7 @@ public class AddBudgetController { public void addBudget(ActionEvent event) { if(newBudgetItem == null){ ExpenseCategory category = getCategory(); - System.out.println(category.toString()); double amount = Double.parseDouble(amountVariable.getText()); - System.out.println(amount); String description = descriptionVariable.getText(); newBudgetItem = new BudgetItem(amount, description, category); } @@ -77,12 +75,8 @@ public class AddBudgetController { amountVariable.textProperty().set(String.valueOf(item.getBudgetAmount())); descriptionVariable.textProperty().set(item.getBudgetDescription()); categoryVariable.setValue(item.getBudgetCategory()); - - } - - public void closeButton(ActionEvent actionEvent) { final Node source = (Node) actionEvent.getSource(); final Stage stage = (Stage) source.getScene().getWindow(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java index 821fe2cf..0388343b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -19,6 +19,7 @@ import no.ntnu.idatt1002.demo.data.Economics.ExpenseCategory; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Optional; enum DialogMode{ ADD, EDIT, DELETE @@ -121,6 +122,28 @@ public class BudgetController { stage.close(); } + @FXML + public void deleteButton(ActionEvent event) { + BudgetItem item = budgetTableView.getSelectionModel().getSelectedItem(); + if (item == null) { + return; + } + Optional<ButtonType> isConfirmed = showConfirmationDialog(); + if (isConfirmed.isPresent() && isConfirmed.get() == ButtonType.OK) { + general.deleteItemFromBudget(item.getBudgetCategory()); + refreshObservableList(); + } + } + + private Optional<ButtonType> showConfirmationDialog() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("Confirm Delete"); + alert.setHeaderText("Delete Confirmation"); + alert.setContentText("Are you sure you would like to delete the selected entry?"); + + return alert.showAndWait(); + } + protected void refreshObservableList(){ budgetTableView.setItems(budgetList); diff --git a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java index 0b1a38a3..29809c24 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java +++ b/src/main/java/no/ntnu/idatt1002/demo/data/Budget/BudgetItem.java @@ -37,7 +37,7 @@ public class BudgetItem { return budgetAmount.get(); } - public DoubleProperty getAmountProperty(){ return budgetAmount;} + public DoubleProperty getAmountProperty(){ return budgetAmount;} public void setBudgetAmount(double amount){ this.budgetAmount.set(amount); diff --git a/src/main/resources/view/Budget.fxml b/src/main/resources/view/Budget.fxml index 5138187c..461014f8 100644 --- a/src/main/resources/view/Budget.fxml +++ b/src/main/resources/view/Budget.fxml @@ -35,8 +35,9 @@ <children> <HBox alignment="TOP_CENTER" prefHeight="98.0" prefWidth="600.0" spacing="40.0" GridPane.rowIndex="3"> <children> - <Button fx:id="addBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Add Budget Item" /> - <Button fx:id="editBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Edit/Remove" /> + <Button fx:id="addBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Add" /> + <Button fx:id="editBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#switchAddBudget" prefWidth="100.0" text="Edit" /> + <Button fx:id="deleteBudget" minHeight="60.0" minWidth="100.0" mnemonicParsing="false" onAction="#deleteButton" prefWidth="100.0" text="Delete" /> <Button minHeight="60.0" minWidth="100.0" mnemonicParsing="false" prefWidth="100.0" text="Save/Back" /> </children> </HBox> -- GitLab From 43164d1b6e6716e7932335ad11ebfecb12b30ac6 Mon Sep 17 00:00:00 2001 From: Anders Emil Bergan <anderebe@stud.ntnu.no> Date: Fri, 24 Mar 2023 12:02:42 +0100 Subject: [PATCH 098/103] Update src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java --- src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java b/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java index 59881bea..148b8852 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java +++ b/src/main/java/no/ntnu/idatt1002/demo/view/MyApp.java @@ -12,7 +12,7 @@ public class MyApp extends Application { @Override public void start(Stage stage) throws IOException { - FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/Budget.fxml")); + FXMLLoader loader = new FXMLLoader(getClass().getResource("/view/FirstMenu.fxml")); Parent root = loader.load(); Scene scene = new Scene(root); stage.setScene(scene); @@ -22,4 +22,4 @@ public class MyApp extends Application { public static void main(String[] args) { launch(args); } -} \ No newline at end of file +} -- GitLab From 1245d2eef011e67092d3658a3d3dd4deade04429 Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 12:28:12 +0100 Subject: [PATCH 099/103] Deleted duplicate enum. Remapped budget button to new budget window --- .../no/ntnu/idatt1002/demo/controller/BudgetController.java | 4 ---- .../no/ntnu/idatt1002/demo/controller/ExpensesController.java | 2 +- .../no/ntnu/idatt1002/demo/controller/IncomeController.java | 2 +- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java index 0388343b..7a572c1b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/BudgetController.java @@ -21,10 +21,6 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; -enum DialogMode{ - ADD, EDIT, DELETE -} - public class BudgetController { private DialogMode dialogMode; diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java index 9f32f059..abf22a6b 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/ExpensesController.java @@ -219,7 +219,7 @@ public class ExpensesController { } else if (event.getSource() == returnBtn) { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); } else if (event.getSource() == budgetBtn) { - loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); + loader.setLocation(SceneController.class.getResource("/view/Budget.fxml")); } Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); diff --git a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java index bca922a6..d5ef44d1 100644 --- a/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java +++ b/src/main/java/no/ntnu/idatt1002/demo/controller/IncomeController.java @@ -209,7 +209,7 @@ public class IncomeController { } else if (event.getSource() == returnBtn) { loader.setLocation(SceneController.class.getResource("/view/FirstMenu.fxml")); } else if (event.getSource() == budgetBtn) { - loader.setLocation(SceneController.class.getResource("/view/underProgress.fxml")); + loader.setLocation(SceneController.class.getResource("/view/Budget.fxml")); } Parent root = loader.load(); Stage stage = (Stage) ((Node) event.getSource()).getScene().getWindow(); -- GitLab From 952280fb325c4ce8eed9904dd12ff0b4559e7cca Mon Sep 17 00:00:00 2001 From: Harry Linrui XU <xulr0820@hotmail.com> Date: Fri, 24 Mar 2023 12:31:50 +0100 Subject: [PATCH 100/103] Deleted test files --- src/main/resources/Economics/incomeRegisterTest.register | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/main/resources/Economics/incomeRegisterTest.register diff --git a/src/main/resources/Economics/incomeRegisterTest.register b/src/main/resources/Economics/incomeRegisterTest.register deleted file mode 100644 index e69de29b..00000000 -- GitLab From 32a06ed78a84b1b7e7630abea3682b394a058db2 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Tue, 11 Apr 2023 12:58:26 +0200 Subject: [PATCH 101/103] finished look for duoInfo window --- src/main/resources/view/duoInfo.fxml | 174 +++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 src/main/resources/view/duoInfo.fxml diff --git a/src/main/resources/view/duoInfo.fxml b/src/main/resources/view/duoInfo.fxml new file mode 100644 index 00000000..7a0c9d8f --- /dev/null +++ b/src/main/resources/view/duoInfo.fxml @@ -0,0 +1,174 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.geometry.Insets?> +<?import javafx.scene.Cursor?> +<?import javafx.scene.control.Button?> +<?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.TableColumn?> +<?import javafx.scene.control.TableView?> +<?import javafx.scene.image.Image?> +<?import javafx.scene.image.ImageView?> +<?import javafx.scene.layout.AnchorPane?> +<?import javafx.scene.layout.ColumnConstraints?> +<?import javafx.scene.layout.GridPane?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.Pane?> +<?import javafx.scene.layout.Region?> +<?import javafx.scene.layout.RowConstraints?> +<?import javafx.scene.layout.StackPane?> +<?import javafx.scene.layout.VBox?> +<?import javafx.scene.shape.Rectangle?> +<?import javafx.scene.text.Font?> +<?import javafx.scene.text.Text?> + +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> + <children> + <ImageView fitHeight="500.0" fitWidth="750.0" pickOnBounds="true"> + <image> + <Image url="@../Images/backgroundMini.jpg" /> + </image> + <cursor> + <Cursor fx:constant="DEFAULT" /> + </cursor> + </ImageView> + <VBox layoutX="60.0" layoutY="77.0"> + <children> + <GridPane prefHeight="400.0" prefWidth="600.0"> + <columnConstraints> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="550.0" prefWidth="550.0" /> + <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="80.0" prefWidth="80.0" /> + </columnConstraints> + <rowConstraints> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + </rowConstraints> + <children> + <HBox alignment="CENTER" prefHeight="300.0" prefWidth="600.0" spacing="30.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> + <children> + <VBox> + <children> + <TableView fx:id="expenseTableView" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> + <HBox prefHeight="18.0" prefWidth="517.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Sum: "> + <HBox.margin> + <Insets left="2.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="74.0" /> + <Text fx:id="sum" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + </HBox> + </children> + </StackPane> + </children> + </VBox> + <VBox> + <children> + <TableView fx:id="expenseTableView1" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="dateColumn1" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn1" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn1" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn1" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn1" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> + <HBox prefHeight="18.0" prefWidth="517.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Sum: "> + <HBox.margin> + <Insets left="2.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="74.0" /> + <Text fx:id="sum1" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + </HBox> + </children> + </StackPane> + </children> + </VBox> + </children> + </HBox> + <HBox alignment="CENTER_LEFT" prefWidth="410.0" spacing="5.0"> + <children> + <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> + <graphic> + <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> + <image> + <Image url="@../Images/add_image.png" /> + </image> + </ImageView> + </graphic> + </Button> + </children> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <padding> + <Insets bottom="5.0" /> + </padding> + </HBox> + <VBox GridPane.rowIndex="3"> + <opaqueInsets> + <Insets left="50.0" /> + </opaqueInsets> + </VBox> + <VBox alignment="CENTER_LEFT" prefHeight="200.0" prefWidth="100.0" spacing="5.0" GridPane.columnIndex="1"> + <children> + <ComboBox fx:id="show" prefWidth="150.0" promptText="Show"> + <opaqueInsets> + <Insets /> + </opaqueInsets> + <VBox.margin> + <Insets bottom="5.0" /> + </VBox.margin> + </ComboBox> + </children> + </VBox> + <Button mnemonicParsing="false" onAction="#switchScene" prefWidth="150.0" text="Continue" GridPane.columnIndex="1" GridPane.rowIndex="3"> + <GridPane.margin> + <Insets bottom="5.0" /> + </GridPane.margin> + </Button> + <Pane GridPane.rowIndex="1"> + <children> + <Text layoutY="21.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Income" /> + <Text layoutX="330.0" layoutY="21.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Expenses" /> + </children> + </Pane> + </children> + </GridPane> + </children> + </VBox> + <Button layoutX="14.0" layoutY="14.0" mnemonicParsing="false" text="Return" /> + <Text layoutX="197.0" layoutY="60.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Income/Expenses" textAlignment="CENTER"> + <font> + <Font size="48.0" /> + </font> + </Text> + </children> +</AnchorPane> -- GitLab From d4e74dcb23e9e677dda88179a6c802e4f63a76d3 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Tue, 11 Apr 2023 15:41:11 +0200 Subject: [PATCH 102/103] gave color indication to the windows byt needs to be refined for more accessabilty --- src/main/resources/view/duoInfo.fxml | 108 +++++++++++++++++---------- 1 file changed, 70 insertions(+), 38 deletions(-) diff --git a/src/main/resources/view/duoInfo.fxml b/src/main/resources/view/duoInfo.fxml index 7a0c9d8f..80a89990 100644 --- a/src/main/resources/view/duoInfo.fxml +++ b/src/main/resources/view/duoInfo.fxml @@ -12,7 +12,6 @@ <?import javafx.scene.layout.ColumnConstraints?> <?import javafx.scene.layout.GridPane?> <?import javafx.scene.layout.HBox?> -<?import javafx.scene.layout.Pane?> <?import javafx.scene.layout.Region?> <?import javafx.scene.layout.RowConstraints?> <?import javafx.scene.layout.StackPane?> @@ -39,28 +38,38 @@ <ColumnConstraints halignment="CENTER" hgrow="SOMETIMES" minWidth="80.0" prefWidth="80.0" /> </columnConstraints> <rowConstraints> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> <RowConstraints minHeight="10.0" vgrow="SOMETIMES" /> - <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" /> + <RowConstraints minHeight="10.0" prefHeight="30.0" valignment="CENTER" vgrow="SOMETIMES" /> </rowConstraints> <children> - <HBox alignment="CENTER" prefHeight="300.0" prefWidth="600.0" spacing="30.0" GridPane.columnSpan="2" GridPane.rowIndex="2"> + <HBox alignment="CENTER" prefHeight="300.0" prefWidth="600.0" spacing="30.0" GridPane.columnSpan="2" GridPane.rowIndex="1"> <children> <VBox> <children> - <TableView fx:id="expenseTableView" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> - <columns> - <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> - </columns> - <columnResizePolicy> - <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> - </columnResizePolicy> - </TableView> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Income" /> + </children> + </StackPane> + <StackPane> + <children> + <TableView fx:id="expenseTableView" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="dateColumn" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <Rectangle arcHeight="5.0" arcWidth="5.0" disable="true" fill="LIME" height="234.0" opacity="0.1" stroke="#d9cccc" strokeType="INSIDE" translateY="13.0" width="300.0" /> + </children> + </StackPane> <StackPane> <children> <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> @@ -81,18 +90,29 @@ </VBox> <VBox> <children> - <TableView fx:id="expenseTableView1" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> - <columns> - <TableColumn fx:id="dateColumn1" prefWidth="75.0" text="Date" /> - <TableColumn fx:id="amountColumn1" prefWidth="75.0" text="Amount" /> - <TableColumn fx:id="categoryColumn1" prefWidth="75.0" text="Category" /> - <TableColumn fx:id="descriptionColumn1" prefWidth="75.0" text="Description" /> - <TableColumn fx:id="recurringColumn1" prefWidth="75.0" text="Recurring" /> - </columns> - <columnResizePolicy> - <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> - </columnResizePolicy> - </TableView> + <StackPane> + <children> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" /> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Expenses" /> + </children> + </StackPane> + <StackPane> + <children> + <TableView fx:id="expenseTableView1" minWidth="300.0" prefHeight="260.0" prefWidth="485.0"> + <columns> + <TableColumn fx:id="dateColumn1" prefWidth="75.0" text="Date" /> + <TableColumn fx:id="amountColumn1" prefWidth="75.0" text="Amount" /> + <TableColumn fx:id="categoryColumn1" prefWidth="75.0" text="Category" /> + <TableColumn fx:id="descriptionColumn1" prefWidth="75.0" text="Description" /> + <TableColumn fx:id="recurringColumn1" prefWidth="75.0" text="Recurring" /> + </columns> + <columnResizePolicy> + <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> + </columnResizePolicy> + </TableView> + <Rectangle arcHeight="5.0" arcWidth="5.0" disable="true" fill="RED" height="234.0" opacity="0.1" stroke="RED" strokeType="INSIDE" translateY="13.0" width="300.0" /> + </children> + </StackPane> <StackPane> <children> <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="18.0" stroke="#d9cccc" strokeType="INSIDE" width="300.0" StackPane.alignment="TOP_LEFT" /> @@ -132,11 +152,6 @@ <Insets bottom="5.0" /> </padding> </HBox> - <VBox GridPane.rowIndex="3"> - <opaqueInsets> - <Insets left="50.0" /> - </opaqueInsets> - </VBox> <VBox alignment="CENTER_LEFT" prefHeight="200.0" prefWidth="100.0" spacing="5.0" GridPane.columnIndex="1"> <children> <ComboBox fx:id="show" prefWidth="150.0" promptText="Show"> @@ -149,17 +164,34 @@ </ComboBox> </children> </VBox> - <Button mnemonicParsing="false" onAction="#switchScene" prefWidth="150.0" text="Continue" GridPane.columnIndex="1" GridPane.rowIndex="3"> + <Button mnemonicParsing="false" onAction="#switchScene" prefWidth="150.0" text="Continue" GridPane.columnIndex="1" GridPane.rowIndex="2"> <GridPane.margin> <Insets bottom="5.0" /> </GridPane.margin> </Button> - <Pane GridPane.rowIndex="1"> + <StackPane alignment="CENTER_LEFT" GridPane.halignment="CENTER" GridPane.rowIndex="2" GridPane.valignment="CENTER"> <children> - <Text layoutY="21.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Income" /> - <Text layoutX="330.0" layoutY="21.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Expenses" /> + <Rectangle arcHeight="5.0" arcWidth="5.0" fill="#f8f8f8" height="26.0" stroke="#d9cccc" strokeLineCap="ROUND" strokeType="INSIDE" width="550.0" StackPane.alignment="CENTER_LEFT"> + <StackPane.margin> + <Insets bottom="4.0" /> + </StackPane.margin> + </Rectangle> + <HBox alignment="CENTER_LEFT" minHeight="15.0" prefHeight="18.0" prefWidth="510.0"> + <children> + <Text strokeType="OUTSIDE" strokeWidth="0.0" text="Total Disbosable Amount: "> + <HBox.margin> + <Insets left="3.0" /> + </HBox.margin> + </Text> + <Region prefHeight="18.0" prefWidth="100.0" /> + <Text fx:id="sum11" strokeType="OUTSIDE" strokeWidth="0.0" text="Text" /> + </children> + <StackPane.margin> + <Insets bottom="4.0" /> + </StackPane.margin> + </HBox> </children> - </Pane> + </StackPane> </children> </GridPane> </children> -- GitLab From 4c8097d4495a6fb880df6e0094a05e39377c0af9 Mon Sep 17 00:00:00 2001 From: anderebe <anderebe@stud.ntnu.no> Date: Tue, 11 Apr 2023 15:47:00 +0200 Subject: [PATCH 103/103] changed button to add income and expenses --- src/main/resources/view/duoInfo.fxml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/main/resources/view/duoInfo.fxml b/src/main/resources/view/duoInfo.fxml index 80a89990..7a583661 100644 --- a/src/main/resources/view/duoInfo.fxml +++ b/src/main/resources/view/duoInfo.fxml @@ -4,6 +4,8 @@ <?import javafx.scene.Cursor?> <?import javafx.scene.control.Button?> <?import javafx.scene.control.ComboBox?> +<?import javafx.scene.control.MenuButton?> +<?import javafx.scene.control.MenuItem?> <?import javafx.scene.control.TableColumn?> <?import javafx.scene.control.TableView?> <?import javafx.scene.image.Image?> @@ -20,7 +22,7 @@ <?import javafx.scene.text.Font?> <?import javafx.scene.text.Text?> -<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1"> +<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="500.0" prefWidth="750.0" xmlns="http://javafx.com/javafx/19" xmlns:fx="http://javafx.com/fxml/1" fx:controller="no.ntnu.idatt1002.demo.controller.ExpensesController"> <children> <ImageView fitHeight="500.0" fitWidth="750.0" pickOnBounds="true"> <image> @@ -135,7 +137,11 @@ </HBox> <HBox alignment="CENTER_LEFT" prefWidth="410.0" spacing="5.0"> <children> - <Button fx:id="addBtn" alignment="TOP_CENTER" mnemonicParsing="false" onAction="#handleAddButton" text="Add" textAlignment="CENTER"> + <MenuButton mnemonicParsing="false" text="MenuButton"> + <items> + <MenuItem mnemonicParsing="false" onAction="#addIncome" text="Income" /> + <MenuItem mnemonicParsing="false" onAction="#addExpense" text="Expense" /> + </items> <graphic> <ImageView fitHeight="19.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true"> <image> @@ -143,7 +149,7 @@ </image> </ImageView> </graphic> - </Button> + </MenuButton> </children> <opaqueInsets> <Insets /> -- GitLab