diff --git a/.github/workflows/gradle.yml b/.github/workflows/gradle.yml new file mode 100644 index 0000000000..391c46b4fe --- /dev/null +++ b/.github/workflows/gradle.yml @@ -0,0 +1,34 @@ +name: Java CI + +on: [push, pull_request] + +jobs: + build: + strategy: + matrix: + platform: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.platform }} + + steps: + - name: Set up repository + uses: actions/checkout@master + + - name: Set up repository + uses: actions/checkout@master + with: + ref: master + + - name: Merge to master + run: git checkout --progress --force ${{ github.sha }} + + - name: Validate Gradle Wrapper + uses: gradle/wrapper-validation-action@v1 + + - name: Setup JDK 11 + uses: actions/setup-java@v1 + with: + java-version: '11' + java-package: jdk+fx + + - name: Build and check with Gradle + run: ./gradlew check diff --git a/.gitignore b/.gitignore index 2873e189e1..a8fac67ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -13,5 +13,9 @@ src/main/resources/docs/ *.iml bin/ -/text-ui-test/ACTUAL.TXT +text-ui-test/ACTUAL.TXT text-ui-test/EXPECTED-UNIX.TXT +text-ui-test/data/* + +# jar file +*.jar diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..90129da089 --- /dev/null +++ b/build.gradle @@ -0,0 +1,62 @@ +plugins { + id 'java' + id 'application' + id 'com.github.johnrengelman.shadow' version '5.1.0' + id 'checkstyle' +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: '5.5.0' + testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: '5.5.0' + + String javaFxVersion = '11' + + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-base', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-controls', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-fxml', version: javaFxVersion, classifier: 'linux' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'win' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'mac' + implementation group: 'org.openjfx', name: 'javafx-graphics', version: javaFxVersion, classifier: 'linux' +} + +test { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed" + + showExceptions true + exceptionFormat "full" + showCauses true + showStackTraces true + showStandardStreams = false + } +} + +application { + mainClassName = "duke.Launcher" +} + +shadowJar { + archiveBaseName = "duke" + archiveClassifier = null +} + +checkstyle { + toolVersion = '10.2' +} + +run{ + standardInput = System.in + enableAssertions = true +} diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..e8ee76467b --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,429 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/config/checkstyle/suppressions.xml b/config/checkstyle/suppressions.xml new file mode 100644 index 0000000000..135ea49ee0 --- /dev/null +++ b/config/checkstyle/suppressions.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/data/todo_list.txt b/data/todo_list.txt new file mode 100644 index 0000000000..66747db7dd --- /dev/null +++ b/data/todo_list.txt @@ -0,0 +1,2 @@ +[T][X] read book +[D][ ] return book (by: June 6th) diff --git a/docs/README.md b/docs/README.md index 8077118ebe..b57eb7889d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -2,28 +2,175 @@ ## Features -### Feature-ABC +### Add Tasks -Description of the feature. +Add `Todos`, `Events`, and `Deadlines` to the `Task` list. -### Feature-XYZ +### Mark tasks as done / undone + +Mark tasks on the list as **done / undone**. + +### Delete tasks + +Delete tasks when they are no longer relevant. + +### Find tasks + +Find tasks that contain a certain keyword or key phrase. + +### Store and List tasks + +List all tasks currently stored in the task list, and show their completion status. + +### Check duplicate tasks + +Alert users when they add duplicate tasks and list all duplicates. -Description of the feature. ## Usage -### `Keyword` - Describe action +### `todo` - Add todos -Describe the action and its outcome. +Add a `todo` task to the task list. Example of usage: -`keyword (optional arguments)` +`todo borrow book` + +Expected outcome: + +The todo item of borrowing book gets added to the task list, initialized with a `T` badge and an unfinished status. + +``` +Got it. I've added this task: + [T][ ] borrow book +Now you have 1 task in the list. +``` + +### `event` - Add events + +Add an `event` with starting and finishing timings to the task list. + +Example of usage: + +`event attend talk /from 2023-02-23 18:00 /to 2023-02-23 20:00` + +Expected outcome: + +The event of attending talk is added to the list, initialized with a `E` badge and an unfinished status. +The date format also gets transformed to be more user-friendly. + +``` +Got it. I've added this task: + [E][ ] attend talk (from: Aug 30 2020, 06:00 PM to Aug 30 2020, 08:00 PM) +Now you have 2 tasks in the list. +``` + +### `deadline` - Add deadlines + +Add an `deadline` with end timing to the task list. + +Example of usage: + +`deadline submit essay /by 2023-02-23 18:00` + +Expected outcome: + +The deadline gets added, initialized with a `D` badge and an unfinished status. +The date format also gets transformed to be more user-friendly. + +``` +Got it. I've added this task: + [D][ ] submit essay (by: Aug 30 2020, 06:00 PM) +Now you have 3 tasks in the list. +``` + +### `list` - List all saved tasks + +List all tasks currently added to the list. + +Example of usage: + +`list` + +Expected outcome: + +All tasks inclusive of todos, deadlines, and events as well as their completion status get listed. + +``` +"Valid request:D. I've got this for you: + +1.[T][ ] read book +2.[D][ ] return book (by: June 6th) +5.[E][ ] project meeting (from: Mon 2pm to 4pm) +``` + +### `mark` - Mark task as done + +Mark a task as done status. + +Example of usage: + +`mark 1` + +Expected outcome: + +The first task saved on the list is marked as done. + +``` +Nice! I've marked this task as done: + [T][X] borrow book +``` + +### `unmark` - Mark task as undone + +Mark a task as undone status. The opposite of `mark` operation. + +Example of usage: + +`unmark 2` Expected outcome: -Description of the outcome. +The second task saved on the list is marked as undone. ``` -expected output +OK, I've marked this task as not done yet: + [E][ ] attend talk (from: Aug 30 2020, 06:00 PM to Aug 30 2020, 08:00 PM) ``` + +### `delete` - Delete a task + +Delete a task when it is no longer relevant. + +Example of usage: + +`delete 1` + +Expected outcome: + +The first task of borrowing book gets deleted from the list. + +``` +Noted. I've removed this task. + [T][X] borrow book +Now you have 0 tasks in the list. +``` + +### `find` - Search a task + +Search tasks that contain certain keywords or key phrases. + +Example of usage: + +`find talk` + +Expected outcome: + +All tasks that have descriptions that contain `talk` will be matched and returned. + +``` +"Valid request:D. I've got this for you: + +[E][ ] attend talk (from: Aug 30 2020, 06:00 PM to Aug 30 2020, 08:00 PM) +``` \ No newline at end of file diff --git a/docs/Ui.png b/docs/Ui.png new file mode 100644 index 0000000000..74adccff01 Binary files /dev/null and b/docs/Ui.png differ diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..f3d88b1c2f Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..b7c8c5dbf5 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000000..2fe81a7d95 --- /dev/null +++ b/gradlew @@ -0,0 +1,183 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..62bd9b9cce --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,103 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java deleted file mode 100644 index 5d313334cc..0000000000 --- a/src/main/java/Duke.java +++ /dev/null @@ -1,10 +0,0 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} diff --git a/src/main/java/duke/Duke.java b/src/main/java/duke/Duke.java new file mode 100644 index 0000000000..5f311d50b1 --- /dev/null +++ b/src/main/java/duke/Duke.java @@ -0,0 +1,73 @@ +package duke; + +import java.util.ArrayList; +import java.util.Scanner; + +import duke.exception.DukeException; +import duke.task.Task; +import duke.tool.Parser; +import duke.tool.Storage; +import duke.ui.Ui; + + +/** + * Represents a task manager bot class. + */ +public class Duke { + + private Storage storage; + private ArrayList tasks; + private Ui ui; + + /** + * Constructs a task manager Duke object. + * @param dirPath The directory to write tasks into. + * @param filePath The file to write tasks into. + */ + public Duke(String dirPath, String filePath) { + this.ui = new Ui(); + this.storage = new Storage(dirPath, filePath); + this.tasks = new ArrayList<>(100); + } + + /** + * Run the Duke object that handles input stream from command line. + */ + public void run() { + System.out.println(this.ui.print_greet_msg()); + Scanner sc = new Scanner(System.in); + Parser.process_input(this.tasks, sc, this.ui); + this.storage.save_to_file(this.tasks); + } + + /** + * Gets response in an interactive manner with extracted user input string. + * @param input User-input string. + * @return A string of reponse. + */ + public String getResponse(String input) { + String output = ""; + try { + output = Parser.switch_input(this.tasks, input, this.ui); + } catch (DukeException e) { + e.printStackTrace(); + } + if (output.isBlank()) { + return this.ui.print_empty_msg(); + } else { + return output; + } + } + + /** + * Gets ui object saved in Duke. + * @return A Ui object + */ + public Ui getUi() { + return this.ui; + } + + public static void main(String[] args) { + new Duke("", "").run(); + } +} diff --git a/src/main/java/duke/Launcher.java b/src/main/java/duke/Launcher.java new file mode 100644 index 0000000000..e4ef6b4628 --- /dev/null +++ b/src/main/java/duke/Launcher.java @@ -0,0 +1,12 @@ +package duke; + +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { + Application.launch(Main.class, args); + } +} diff --git a/src/main/java/duke/Main.java b/src/main/java/duke/Main.java new file mode 100644 index 0000000000..6d6051c7c2 --- /dev/null +++ b/src/main/java/duke/Main.java @@ -0,0 +1,32 @@ +package duke; + +import java.io.IOException; + +import duke.ui.MainWindow; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * + */ +public class Main extends Application { + private Duke duke = new Duke("", ""); + + @Override + public void start(Stage stage) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("/view/MainWindow.fxml")); + AnchorPane ap = fxmlLoader.load(); + Scene scene = new Scene(ap); + stage.setScene(scene); + stage.setTitle("DukeBro"); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/duke/exception/DukeCommandNotFoundException.java b/src/main/java/duke/exception/DukeCommandNotFoundException.java new file mode 100644 index 0000000000..2842152d96 --- /dev/null +++ b/src/main/java/duke/exception/DukeCommandNotFoundException.java @@ -0,0 +1,13 @@ +package duke.exception; + +/** + * Represents a command not found exception local to Duke task manager. + */ +public class DukeCommandNotFoundException extends DukeException { + /** + * Constructs a command not found exception. + */ + public DukeCommandNotFoundException() { + super("Command not found!!"); + } +} diff --git a/src/main/java/duke/exception/DukeEmptyTaskException.java b/src/main/java/duke/exception/DukeEmptyTaskException.java new file mode 100644 index 0000000000..0a2c639a1e --- /dev/null +++ b/src/main/java/duke/exception/DukeEmptyTaskException.java @@ -0,0 +1,13 @@ +package duke.exception; + +/** + * Represents an empty task exception local to Duke bot. + */ +public class DukeEmptyTaskException extends DukeException { + /** + * Constructs an empty task exception. + */ + public DukeEmptyTaskException() { + super("The task is empty!!"); + } +} diff --git a/src/main/java/duke/exception/DukeException.java b/src/main/java/duke/exception/DukeException.java new file mode 100644 index 0000000000..6e23de9d4b --- /dev/null +++ b/src/main/java/duke/exception/DukeException.java @@ -0,0 +1,25 @@ +package duke.exception; + +/** + * Represents the exception thrown by duke.Duke. + */ +public class DukeException extends Exception { + /** + * Constructs a duke.Duke exception. + * + * @param message message of the exception. + */ + public DukeException(String message) { + super(message); + } + + /** + * Outputs exception as a string. + * + * @return string representation of the exception. + */ + @Override + public String toString() { + return "[OOPS!!]" + this.getMessage() + "\n"; + } +} diff --git a/src/main/java/duke/task/Deadline.java b/src/main/java/duke/task/Deadline.java new file mode 100644 index 0000000000..2bf58c741c --- /dev/null +++ b/src/main/java/duke/task/Deadline.java @@ -0,0 +1,35 @@ +package duke.task; + +/** + * Represents a deadline object with a deadline timing. + */ +public class Deadline extends Task { + + protected String by; + + /** + * Constructs a deadline object. + * + * @param description title of the deadline. + * @param by deadline timing. + */ + public Deadline(String description, String by) { + super(description); + this.by = by; + } + + + public Deadline() { + super(); + } + + /** + * Represents the string written into todo_list.txt. + * + * @return A string written into the todo_list.txt. + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + by + ")"; + } +} diff --git a/src/main/java/duke/task/Event.java b/src/main/java/duke/task/Event.java new file mode 100644 index 0000000000..119fe26129 --- /dev/null +++ b/src/main/java/duke/task/Event.java @@ -0,0 +1,37 @@ +package duke.task; + +/** + * Represents an event with from and to timings. + */ +public class Event extends Task { + + private String from; + private String to; + + /** + * Constructs an event object. + * + * @param description title of the event. + * @param from time of event happening time. + * @param to expected ending time of the event. + */ + public Event(String description, String from, String to) { + super(description); + this.from = from; + this.to = to; + } + + public Event() { + super(); + } + + /** + * Represents the string written into todo_list.txt. + * + * @return A string written into the todo_list.txt. + */ + @Override + public String toString() { + return "[E]" + super.toString() + " (from: " + from + " to " + to + ")"; + } +} diff --git a/src/main/java/duke/task/Task.java b/src/main/java/duke/task/Task.java new file mode 100644 index 0000000000..e8399546cb --- /dev/null +++ b/src/main/java/duke/task/Task.java @@ -0,0 +1,75 @@ +package duke.task; + +/** + * Represents a task object to be written into a task list. + */ +public class Task { + protected String description; + protected boolean isDone; + + /** + * Constructs a task object that can be either deadlines, todos, or events. + * @param description the contents of the tasks. + */ + public Task(String description) { + this.description = description; + this.isDone = false; + } + + /** + * Constructs an empty and fake task object. + */ + public Task() { + this.description = "fake task"; + this.isDone = false; + } + + /** + * Gets descriptions string. + * @return A string object. + */ + public String getDescription() { + return description; + } + + /** + * Signifies whether this task is null and fake. + * @return An indicator of whether this task is null. + */ + public boolean isNull() { + return this.description.equals("fake task"); + } + + /** + * Gets the string representation of task status. + * @return A string of task status. + */ + public String getStatusIcon() { + return (isDone ? "X" : " "); // mark done duke.task with X + } + + /** + * Marks the task as done. + */ + public void markAsDone() { + this.isDone = true; + } + + /** + * Marks the task as undone. + */ + public void markAsNotDone() { + this.isDone = false; + } + + /** + * Gets the string representation of the task that is printer-friendly. + * @return A string object. + */ + @Override + public String toString() { + return "[" + getStatusIcon() + "] " + this.description; + } +} + + diff --git a/src/main/java/duke/task/Todo.java b/src/main/java/duke/task/Todo.java new file mode 100644 index 0000000000..433204c7d9 --- /dev/null +++ b/src/main/java/duke/task/Todo.java @@ -0,0 +1,22 @@ +package duke.task; + +/** + * Represents a todo object that inherits from Task. + */ +public class Todo extends Task { + /** + * Constructs a todo object from task description. + * @param description The description of the todo object. + */ + public Todo(String description) { + super(description); + } + + public Todo() { + super(); + } + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/java/duke/tool/Command.java b/src/main/java/duke/tool/Command.java new file mode 100644 index 0000000000..562bf4bef0 --- /dev/null +++ b/src/main/java/duke/tool/Command.java @@ -0,0 +1,121 @@ +package duke.tool; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +import duke.task.Task; +import duke.ui.Ui; + +/** + * Constructs a command class that handles all changes to the task list. + */ +public class Command { + + private ArrayList tasks; + private final Ui ui; + + /** + * Constructs a command object. + * @param tasks The list of tasks to operate upon. + * @param ui The user interface object for printing. + */ + public Command(ArrayList tasks, Ui ui) { + assert ui != null : "Unable to accept null user interface"; + this.tasks = tasks; + this.ui = ui; + } + + /** + * Marks a task as done. + * @param tid The id of the task. + * @return The successful or unsuccessful ui notification message. + */ + public String mark_as_done(int tid) { + Task task = new Task(); + try { + task = this.tasks.get(tid - 1); + task.markAsDone(); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return ui.print_done_msg(task); + } + + /** + * Marks a task as not done. + * @param tid The id of the task. + * @return The successful or unsuccessful ui notification message. + */ + public String mark_as_undone(int tid) { + Task task = new Task(); + try { + task = tasks.get(tid - 1); + task.markAsNotDone(); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return ui.print_undone_msg(task); + } + + /** + * Add the task to the task list. + * @param task The task to add. + * @return The successful or unsuccessful ui notification message. + */ + public String add_task(Task task) { + ArrayList dupTasks = check_duplicates(task); + if (!(dupTasks.isEmpty())) { + return ui.print_custom_msg("Duplicate tasks detected! \n\t") + + ui.print_task_list(dupTasks); + } + if (!(task.isNull())) { + tasks.add(task); + } + return ui.print_add_msg(task, tasks.size()); + } + + /** + * Delete the task to the task list. + * @param tid The task id to delete. + * @return The successful or unsuccessful ui notification message. + */ + public String delete_task(int tid) { + Task task = new Task(); + try { + task = tasks.get(tid - 1); + tasks.remove(task); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return ui.print_remove_msg(task, tasks.size()); + } + + /** + * Find the task that contains match string from the task list. + * @param matchStr The string to match with. + * @return The contents of tasks found. + */ + public String find_tasks(String matchStr) { + assert matchStr != null : "Please provide a match string"; + if (matchStr.isBlank()) { + return ui.print_custom_msg("Nothing found"); + } + ArrayList matchedTasks = new ArrayList<>(); + tasks.forEach((task) -> { + String description = task.getDescription(); + if (description.contains(matchStr)) { + matchedTasks.add(task); + } + }); + return ui.print_task_list(matchedTasks); + } + + private ArrayList check_duplicates(Task task) { + String taskDescr = task.getDescription(); + List dupTasks = tasks.stream().filter((curTask) -> { + return curTask.getDescription().equals(taskDescr); + }).collect(Collectors.toList()); + return new ArrayList<>(dupTasks); + } +} diff --git a/src/main/java/duke/tool/Parser.java b/src/main/java/duke/tool/Parser.java new file mode 100644 index 0000000000..83e5c6cc4a --- /dev/null +++ b/src/main/java/duke/tool/Parser.java @@ -0,0 +1,236 @@ +package duke.tool; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeParseException; +import java.time.temporal.UnsupportedTemporalTypeException; +import java.util.ArrayList; +import java.util.Scanner; + +import duke.exception.DukeCommandNotFoundException; +import duke.exception.DukeEmptyTaskException; +import duke.task.Deadline; +import duke.task.Event; +import duke.task.Task; +import duke.task.Todo; +import duke.ui.Ui; + +/** + * Constructs a (static) parser that parses user input strings. + */ +public class Parser { + private static final DateTimeFormatter read_fmt = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"); + private static final DateTimeFormatter print_fmt = DateTimeFormatter.ofPattern("MMM dd yyyy, hh:mm a"); + + /** + * Parses the date and time from input string. + * @param input User-provided input string. + * @return A string that conforms to the specified printing format. + */ + public static String parse_date(String input) { + try { + LocalDateTime lt = LocalDateTime.parse(input, read_fmt); + return lt.format(print_fmt); + } catch (DateTimeParseException | UnsupportedTemporalTypeException e) { + print("please follow the standard datetime format: yyyy-MM-dd HH:mm"); + } + return input; + } + + /** + * Parses integer from a string input. + * @param input User-provided input string. + * @return Parsed integer returned as the task id. + * @throws DukeEmptyTaskException Indicates that task id is not found + */ + public static int parse_task_id(String input) throws DukeEmptyTaskException { + int tid; + try { + tid = Integer.parseInt(input.split(" ")[1]); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + throw new DukeEmptyTaskException(); + } + return tid; + } + + /** + * Parses match string from a string input. + * @param input User-provided input string. + * @return Parsed string returned as the matching string. + * @throws DukeEmptyTaskException Indicates that match string is not found + */ + public static String parse_match_string(String input) throws DukeEmptyTaskException { + String matchStr; + try { + matchStr = input.split(" ")[1].strip(); + } catch (IndexOutOfBoundsException e) { + throw new DukeEmptyTaskException(); + } + return matchStr; + } + + /** + * Parses and constructs Todo object from a string input. + * @param trigger Task trigger, in this case, it is "todo". + * @param input User-provided input string. + * @return Constructed Todo object from the parsed contents. + */ + public static Todo parse_todo(String trigger, String input) { + if (input.split(trigger).length == 0) { + return new Todo(); + } else { + input = input.split(trigger)[1].strip(); + return new Todo(input); + } + } + + /** + * Parses and constructs Deadline object from a string input. + * @param trigger Task trigger, in this case, it is "deadline". + * @param input User-provided input string. + * @return Constructed Deadline object from the parsed contents. + */ + public static Deadline parse_deadline(String trigger, String input) { + String content; + String deadline; + try { + input = input.split(trigger)[1]; + content = input.split("/by")[0].strip(); + deadline = parse_date(input.split("/by")[1].strip()); + return new Deadline(content, deadline); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return new Deadline(); + } + + /** + * Parses and constructs Event object from a string input. + * @param trigger Task trigger, in this case, it is "event". + * @param input User-provided input string. + * @return Constructed Event object from the parsed contents. + * @throws DukeEmptyTaskException Indicates that task description is not found + */ + public static Event parse_event(String trigger, String input) throws DukeEmptyTaskException { + String content; + String fromTime; + String toTime; + if (input.split(trigger).length == 1) { + throw new DukeEmptyTaskException(); + } else { + input = input.split(trigger)[1].strip(); + } + try { + content = input.split("/from")[0].strip(); + fromTime = parse_date(input.split("/from")[1].split("/to")[0].strip()); + toTime = parse_date(input.split("/from")[1].split("/to")[1].strip()); + return new Event(content, fromTime, toTime); + } catch (IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return new Event(); + } + + + /** + * Parses task id to delete from a string input. + * @param trigger Task trigger, in this case, it is "delete". + * @param input User-provided input string. + * @return Task id to delete from. + * @throws DukeEmptyTaskException Indicates that task description is not found + */ + public static int parse_delete_id(String trigger, String input) throws DukeEmptyTaskException { + int tid = -1; + if (input.split(trigger).length == 1) { + throw new DukeEmptyTaskException(); + } + try { + tid = Integer.parseInt(input.split(trigger)[1].strip()); + } catch (NumberFormatException | IndexOutOfBoundsException e) { + e.printStackTrace(); + } + return tid; + } + + /** + * Print a string via System.in. + * @param str The string to print. + */ + public static void print(String str) { + System.out.println(str); + } + + /** + * Switch on cases where different triggering commands are met. + * @param tasks The list of tasks to operate upon. + * @param input The input user-provided instruction string. + * @param ui The user-interface object that handles printing jobs. + * @return A status string that indicates job status. + * @throws DukeCommandNotFoundException Indicates when user-provided instruction cannot be catered. + * @throws DukeEmptyTaskException Indicates when user fails to provide a concrete task description. + */ + public static String switch_input(ArrayList tasks, String input, Ui ui) + throws DukeCommandNotFoundException, DukeEmptyTaskException { + String trigger = input.split(" ")[0]; + Command command = new Command(tasks, ui); + int tid; + Task task; + String output; + switch (trigger) { + case "bye": + output = ui.print_bye_msg(); + break; + case "list": + output = ui.print_task_list(tasks); + break; + case "mark": + tid = parse_task_id(input); + output = command.mark_as_done(tid); + break; + case "unmark": + tid = parse_task_id(input); + output = command.mark_as_undone(tid); + break; + case "deadline": + task = parse_deadline(trigger, input); + output = command.add_task(task); + break; + case "event": + task = parse_event(trigger, input); + output = command.add_task(task); + break; + case "todo": + task = parse_todo(trigger, input); + output = command.add_task(task); + break; + case "delete": + tid = parse_delete_id(trigger, input); + output = command.delete_task(tid); + break; + case "find": + String matchStr = parse_match_string(input); + output = command.find_tasks(matchStr); + break; + default: + throw new DukeCommandNotFoundException(); + } + return output; + } + + /** + * Processes inputs from Scanner input stream when used without GUI. + * @param tasks The list of tasks to operate upon. + * @param sc The scanner object that accepts input streams. + * @param ui The user-interface object that handles printing jobs. + */ + public static void process_input(ArrayList tasks, Scanner sc, Ui ui) { + while (sc.hasNextLine()) { + String input = sc.nextLine(); + try { + print(switch_input(tasks, input, ui)); + } catch (Exception e) { + e.printStackTrace(); + } + } + } +} diff --git a/src/main/java/duke/tool/Storage.java b/src/main/java/duke/tool/Storage.java new file mode 100644 index 0000000000..6f84eb948f --- /dev/null +++ b/src/main/java/duke/tool/Storage.java @@ -0,0 +1,65 @@ +package duke.tool; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; + +import duke.task.Task; + +/** + * Represents a storage class that handles saving and loading to disk jobs. + */ +public class Storage { + protected File dir; + protected File file; + + /** + * Constructs a storage object. + * @param dirName The name of a local directory to write. Defaults to "data". + * @param fileName The name of a file within the directory to write into. Defaults to "tolist.txt" + */ + public Storage(String dirName, String fileName) { + assert ((dirName != null) && (fileName != null)) : "please provide a valid path to save task list"; + if (dirName.isBlank()) { + dirName = "data"; + } + if (fileName.isBlank()) { + fileName = "todo_list.txt"; + } + this.dir = new File(dirName); + this.file = new File(fileName); + } + + public static void print(String str) { + System.out.println(str); + } + + /** + * Saves list of tasks to disk. + * @param tasks The list of tasks to write. + */ + public void save_to_file(ArrayList tasks) { + if (!this.dir.exists()) { + while (!this.dir.mkdirs()) { + print(this.dir.getName() + " created\n"); + } + } + try { + if (this.file.createNewFile()) { + print(this.file.getName() + " created\n"); + } + if (tasks.isEmpty()) { + return; + } + FileWriter fw = new FileWriter(this.file, false); + for (Task t : tasks) { + String desc = t + "\n"; + fw.write(desc); + } + fw.close(); + } catch (IOException e) { + e.getStackTrace(); + } + } +} diff --git a/src/main/java/duke/ui/DialogBox.java b/src/main/java/duke/ui/DialogBox.java new file mode 100644 index 0000000000..fd537d7742 --- /dev/null +++ b/src/main/java/duke/ui/DialogBox.java @@ -0,0 +1,80 @@ +package duke.ui; + +import java.io.IOException; +import java.util.Collections; + +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Rectangle; + +/** + * Represents a dialog box that contains user and duke response texts. + */ +public class DialogBox extends HBox { + + @FXML + private Label dialog; + @FXML + private ImageView displayPicture; + + private DialogBox(String text, Image img) { + try { + FXMLLoader fxmlLoader = new FXMLLoader(MainWindow.class.getResource("/view/DialogBox.fxml")); + fxmlLoader.setController(this); + fxmlLoader.setRoot(this); + fxmlLoader.load(); + } catch (IOException e) { + e.printStackTrace(); + } + + dialog.setText(text); + Rectangle rectangle = new Rectangle( + displayPicture.getFitWidth() / 1.4, displayPicture.getFitHeight() / 1.4 + ); + rectangle.setArcWidth(100); + rectangle.setArcHeight(100); + rectangle.setStroke(Color.BLUEVIOLET); + rectangle.setStrokeWidth(6); + displayPicture.setClip(rectangle); + displayPicture.setPreserveRatio(true); + displayPicture.setImage(img); + } + + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + /** + * Gets user dialog box. + * @param text The text to be put into the box. + * @param img The image representing user. + * @return A DialogBox object that fits into a conversation. + */ + public static DialogBox getUserDialog(String text, Image img) { + return new DialogBox(text, img); + } + + /** + * Gets duke dialog box. + * @param text The text to be put into the box. + * @param img The image representing duke. + * @return A DialogBox object that fits into a conversation. + */ + public static DialogBox getDukeDialog(String text, Image img) { + var db = new DialogBox(text, img); + db.flip(); + return db; + } +} diff --git a/src/main/java/duke/ui/MainWindow.java b/src/main/java/duke/ui/MainWindow.java new file mode 100644 index 0000000000..903641c0fa --- /dev/null +++ b/src/main/java/duke/ui/MainWindow.java @@ -0,0 +1,70 @@ +package duke.ui; + +import duke.Duke; +import javafx.animation.PauseTransition; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.util.Duration; + +/** + * + */ +public class MainWindow extends AnchorPane { + + @FXML + private ScrollPane scrollPane; + @FXML + private VBox dialogContainer; + @FXML + private TextField userInput; + @FXML + private Button sendButton; + + private Duke duke; + + private Image userImage = new Image(this.getClass().getResourceAsStream("/images/DaUser.png")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.png")); + + /** + * Initializes window interface. + */ + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + dialogContainer.getChildren().add(DialogBox.getDukeDialog(duke.getUi().print_greet_msg(), dukeImage)); + } + + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + if (input.equals("bye")) { + closeDuke(); + } + userInput.clear(); + } + + /** + * Exit the programme. + */ + private void closeDuke() { + userInput.setOnAction(null); + sendButton.setOnAction(null); + PauseTransition delay = new PauseTransition(Duration.seconds(0.5)); + delay.setOnFinished(event -> System.exit(0)); + delay.play(); + } +} diff --git a/src/main/java/duke/ui/Ui.java b/src/main/java/duke/ui/Ui.java new file mode 100644 index 0000000000..b288c51981 --- /dev/null +++ b/src/main/java/duke/ui/Ui.java @@ -0,0 +1,133 @@ +package duke.ui; + +import java.util.ArrayList; + +import duke.task.Task; + +/** + * Represents the user interface class that handles all message construction and printing. + */ +public class Ui { + + /** + * Prints customized message through UI. + * @param msg Message to print. + * @return String object. + */ + public String print_custom_msg(String msg) { + return "[Custom message] " + msg; + } + + /** + * Prints message signifying empty inputs + * @return A message string. + */ + public String print_empty_msg() { + return "Nothing to say about this!!"; + } + + /** + * Prints greeting message. + * @return A message string. + */ + public String print_greet_msg() { + return "Hello! I'm Duke\n" + + " What can I do for you?"; + } + + /** + * Prints bye message. + * @return A message string. + */ + public String print_bye_msg() { + return "Bye. Hope to see you again soon!"; + } + + /** + * Prints the list of tasks. + * @param tasks The list of tasks. + * @return A message string. + */ + public String print_task_list(ArrayList tasks) { + StringBuilder output = new StringBuilder(); + if (tasks.isEmpty()) { + output.append("None yet."); + } else { + int i = 1; + for (Task t : tasks) { + String strToAppend = i + "." + t.toString() + "\n"; + output.append(strToAppend); + i++; + } + } + return "Valid request:D. I've got this for you: \n\n" + + output; + } + + /** + * Prints mark-as-done message. + * @param task The task to mark as done. + * @return A message string. + */ + public String print_done_msg(Task task) { + String output; + if (task.isNull()) { + output = "Unsuccessfully marked."; + return output; + } + output = "Nice! I've marked this task as done: \n\t" + task; + return output; + } + + /** + * Prints mark-as-not-done message. + * @param task The task to mark as not done. + * @return A message string. + */ + public String print_undone_msg(Task task) { + String output; + if (task.isNull()) { + output = "Unsuccessfully unmarked."; + return output; + } + output = "OK, I've marked this task as not done yet: \n\t" + + task; + return output; + } + + /** + * Prints adding task status message. + * @param task The task to add. + * @return A message string. + */ + public String print_add_msg(Task task, int size) { + String output; + if (task.isNull()) { + output = "Unsuccessfully added."; + return output; + } + output = "Got it. I've added this task: \n\t" + + task + + "\nNow you have " + + size + + " tasks in the list."; + return output; + } + + /** + * Prints removing task status message. + * @param task The task to remove. + * @return A message string. + */ + public String print_remove_msg(Task task, int size) { + String output; + if (task.isNull()) { + output = "Unsuccessfully removed."; + return output; + } + output = "Noted. I've removed this task: \n\t" + + task + + "\nNow you have " + size + " tasks in the list."; + return output; + } +} diff --git a/src/main/resources/images/DaDuke.png b/src/main/resources/images/DaDuke.png new file mode 100644 index 0000000000..d893658717 Binary files /dev/null and b/src/main/resources/images/DaDuke.png differ diff --git a/src/main/resources/images/DaUser.png b/src/main/resources/images/DaUser.png new file mode 100644 index 0000000000..3c82f45461 Binary files /dev/null and b/src/main/resources/images/DaUser.png differ diff --git a/src/main/resources/images/bkg.jpg b/src/main/resources/images/bkg.jpg new file mode 100644 index 0000000000..066c35e2f3 Binary files /dev/null and b/src/main/resources/images/bkg.jpg differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..928b250154 --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..37270617de --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,20 @@ + + + + + + + + + + + + +