diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000000..3984c94e5d --- /dev/null +++ b/build.gradle @@ -0,0 +1,37 @@ +plugins { + id 'java' + id 'application' + id 'checkstyle' + id 'org.openjfx.javafxplugin' version '0.0.7' +} + +checkstyle { + toolVersion = '8.23' +} + +group 'seedu.duke' +version '0.1.0' + +repositories { + mavenCentral() +} + +javafx { + version = "11.0.2" + modules = [ 'javafx.controls', 'javafx.fxml' ] +} + +application { + // Change this to your main class. + mainClassName = "Duke" +} + +run { + standardInput = System.in +} +dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter:5.5.0' +} +test { + useJUnitPlatform() +} \ No newline at end of file diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..b1a57ba6c0 --- /dev/null +++ b/config/checkstyle/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/tasks.txt b/data/tasks.txt new file mode 100644 index 0000000000..c300589777 --- /dev/null +++ b/data/tasks.txt @@ -0,0 +1,5 @@ +T}-}true}-}x}-} +D}-}true}-}asd}-}12/12/1234 1234 +D}-}false}-}adfadg}-}13/1/2312 1222 +T}-}true}-}x}-} + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..87b738cbd0 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..4b7e1f3d38 --- /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-5.5.1-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100644 index 0000000000..af6708ff22 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## 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"' + +# 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, switch paths to Windows format before running java +if $cygwin ; 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=$((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" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000000..6d57edc706 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@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 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" + +@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/settings.gradle b/settings.gradle new file mode 100644 index 0000000000..d1e92fe5db --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'duke' diff --git a/src/main/java/DialogBox.java b/src/main/java/DialogBox.java new file mode 100644 index 0000000000..2ded97e5c1 --- /dev/null +++ b/src/main/java/DialogBox.java @@ -0,0 +1,82 @@ +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.Insets; +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.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; +import javafx.scene.shape.Rectangle; +import javafx.scene.text.Text; + +/** + * An example of a custom control using FXML. + * This control represents a dialog box consisting of an ImageView to represent the speaker's face and a label + * containing text from the speaker. + */ +public class DialogBox extends HBox { + @FXML + private Text dialog; + @FXML + private ImageView displayPicture; + @FXML + private Background background; + + private DialogBox(String text, Image img, BackgroundFill background_fill) { + 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); + dialog.setWrappingWidth(250.0); + displayPicture.setImage(img); + final Circle clip = new Circle(49.5, 49.5, 49.5); + displayPicture.setClip(clip); + background = new Background(background_fill); + Rectangle rect = new Rectangle(385,120); + rect.widthProperty().bind(this.widthProperty()); + rect.heightProperty().bind(this.heightProperty().subtract(10)); + rect.setArcHeight(60.0); + rect.setArcWidth(60.0); + this.setClip(rect); + this.setBackground(background); + } + + /** + * Flips the dialog box such that the ImageView is on the left and text on the right. + */ + private void flip() { + ObservableList tmp = FXCollections.observableArrayList(this.getChildren()); + Collections.reverse(tmp); + getChildren().setAll(tmp); + setAlignment(Pos.TOP_LEFT); + } + + public static DialogBox getUserDialog(String text, Image img) { + BackgroundFill background_fill = new BackgroundFill(Color.LIGHTBLUE, CornerRadii.EMPTY, Insets.EMPTY); + return new DialogBox(text, img, background_fill); + } + + public static DialogBox getDukeDialog(String text, Image img) { + BackgroundFill background_fill = new BackgroundFill(Color.PINK, CornerRadii.EMPTY, Insets.EMPTY); + var db = new DialogBox(text, img, background_fill); + db.flip(); + return db; + } +} \ No newline at end of file diff --git a/src/main/java/Duke.java b/src/main/java/Duke.java index d62a957f62..3c6e7c5abe 100644 --- a/src/main/java/Duke.java +++ b/src/main/java/Duke.java @@ -1,12 +1,205 @@ -public class Duke { - public static void main(String[] args) { - String logo = " ____ _ \n" - + "| _ \\ _ _| | _____ \n" - + "| | | | | | | |/ / _ \\\n" - + "| |_| | |_| | < __/\n" - + "|____/ \\__,_|_|\\_\\___|\n"; - System.out.println("Hello from\n" + logo); - } -} - -//change +import java.io.*; + +import command.Command; +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.control.Label; +import javafx.stage.Stage; + + +import javafx.scene.control.Button; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.scene.layout.Region; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import process.DukeException; +import process.Parser; +import process.Storage; +import process.Ui; +import task.TaskList; + +import static java.lang.System.exit; + +/** + * Represents the task manager + */ +public class Duke extends Application { + private ScrollPane scrollPane; + private VBox dialogContainer; + private TextField userInput; + private Button sendButton; + private Scene scene; + private Storage storage; + private TaskList tasks; + private Ui ui; + + /** + * Create a task manager with a task list from an existing file + * @param file to create a task list from + */ + public Duke(String file) { + String directory = System.getProperty("user.home"); + directory += "\\documents\\duke\\data"; + String savefile = file; + String absolutePath = directory + File.separator + savefile; + storage = new Storage(absolutePath); + ui = new Ui(); + try { + tasks = new TaskList(storage.load()); + } catch (DukeException e) { + tasks = new TaskList(); + ui.showLoadingError(); + } + } + + /** + * Execute the CLI task manager + */ + public void run() { + ui.showWelcome(); + boolean isExit = false; + while (!isExit) { + try { + String fullCommand = ui.readCommand(); + ui.showLine(); + Command c = Parser.parse(fullCommand); + c.execute(tasks, ui, storage); + isExit = c.isExit(); + } catch (DukeException e) { + ui.showError(e.getMessage()); + } + finally { + ui.showLine(); + } + } + } + + /** + * Da shit + * @param args i have no idea what this is for + */ + public static void main(String[] args) { + new Duke("tasks.txt").run(); + exit(0); + } + + @Override + public void start(Stage stage) { + //Step 1. Setting up reqoutput += uired components + + //The container for the content of the chat to scroll. + scrollPane = new ScrollPane(); + dialogContainer = new VBox(); + scrollPane.setContent(dialogContainer); + + userInput = new TextField(); + sendButton = new Button("Send"); + + AnchorPane mainLayout = new AnchorPane(); + mainLayout.getChildren().addAll(scrollPane, userInput, sendButton); + + scene = new Scene(mainLayout); + + stage.setScene(scene); + stage.show(); + + //Step 2. Formatting the window to look as expected + stage.setTitle("Duke"); + stage.setResizable(false); + stage.setMinHeight(600.0); + stage.setMinWidth(400.0); + + mainLayout.setPrefSize(400.0, 600.0); + + scrollPane.setPrefSize(385, 535); + scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); + scrollPane.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); + + scrollPane.setVvalue(1.0); + scrollPane.setFitToWidth(true); + + // You will need to import `javafx.scene.layout.Region` for this. + dialogContainer.setPrefHeight(Region.USE_COMPUTED_SIZE); + + userInput.setPrefWidth(325.0); + + sendButton.setPrefWidth(55.0); + + AnchorPane.setTopAnchor(scrollPane, 1.0); + + AnchorPane.setBottomAnchor(sendButton, 1.0); + AnchorPane.setRightAnchor(sendButton, 1.0); + + AnchorPane.setLeftAnchor(userInput , 1.0); + AnchorPane.setBottomAnchor(userInput, 1.0); + + //Step 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + dialogContainer.getChildren().add(getDialogLabel(userInput.getText())); + userInput.clear(); + }); + + userInput.setOnAction((event) -> { + dialogContainer.getChildren().add(getDialogLabel(userInput.getText())); + userInput.clear(); + }); + //Scroll down to the end every time dialogContainer's height changes. + dialogContainer.heightProperty().addListener((observable) -> scrollPane.setVvalue(1.0)); + // more code to be added here later + //Part 3. Add functionality to handle user input. + sendButton.setOnMouseClicked((event) -> { + handleUserInput(); + }); + + userInput.setOnAction((event) -> { + handleUserInput(); + }); + } + private Image user = new Image(this.getClass().getResourceAsStream("/images/DaUser.jpg")); + private Image duke = new Image(this.getClass().getResourceAsStream("/images/DaDuke.jpg")); + /** + * Iteration 1: + * Creates a label with the specified text and adds it to the dialog container. + * @param text String containing text to add + * @return a label with the specified text that has word wrap enabled. + */ + private Label getDialogLabel(String text) { + // You will need to import `javafx.scene.control.Label`. + Label textToAdd = new Label(text); + textToAdd.setWrapText(true); + + return textToAdd; + } + /** + * Iteration 2: + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + private void handleUserInput() { + Label userText = new Label(userInput.getText()); + Label dukeText = new Label(getResponse(userInput.getText())); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(userText.getText(), new ImageView(user).getImage()), + DialogBox.getDukeDialog(dukeText.getText(), new ImageView(duke).getImage()) + ); + userInput.clear(); + } + + /** + * The response function for the GUI + * @param input user input + * @return the response + */ + String getResponse(String input) { + try { + Command c = Parser.parse(input); + String toGui = c.execute(tasks, ui, storage); + return toGui; + } catch (DukeException e) { + return ui.showError(e.getMessage()); + } + } +} \ No newline at end of file diff --git a/src/main/java/Launcher.java b/src/main/java/Launcher.java new file mode 100644 index 0000000000..6f9230b9d6 --- /dev/null +++ b/src/main/java/Launcher.java @@ -0,0 +1,11 @@ +import javafx.application.Application; + +/** + * A launcher class to workaround classpath issues. + */ +public class Launcher { + public static void main(String[] args) { +// Application.launch(Duke.class, args); + Application.launch(Main.class, args); + } +} \ No newline at end of file diff --git a/src/main/java/Main.java b/src/main/java/Main.java new file mode 100644 index 0000000000..74c683e4e3 --- /dev/null +++ b/src/main/java/Main.java @@ -0,0 +1,29 @@ +//@Override +import java.io.IOException; +import javafx.application.Application; +import javafx.fxml.FXMLLoader; +import javafx.scene.Scene; +import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; + +/** + * A GUI for Duke using FXML. + */ +public class Main extends Application { + + private Duke duke = new Duke("tasks.txt"); + + @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); + fxmlLoader.getController().setDuke(duke); + stage.show(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/MainWindow.java b/src/main/java/MainWindow.java new file mode 100644 index 0000000000..4bb9f4d28a --- /dev/null +++ b/src/main/java/MainWindow.java @@ -0,0 +1,52 @@ +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +/** + * Controller for MainWindow. Provides the layout for the other controls. + */ +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.jpg")); + private Image dukeImage = new Image(this.getClass().getResourceAsStream("/images/DaDuke.jpg")); + + @FXML + public void initialize() { + scrollPane.vvalueProperty().bind(dialogContainer.heightProperty()); + } + + public void setDuke(Duke d) { + duke = d; + } + + /** + * Creates two dialog boxes, one echoing user input and the other containing Duke's reply and then appends them to + * the dialog container. Clears the user input after processing. + */ + @FXML + private void handleUserInput() { + String input = userInput.getText(); + String response = duke.getResponse(input); + dialogContainer.getChildren().addAll( + DialogBox.getUserDialog(input, userImage), + DialogBox.getDukeDialog(response, dukeImage) + ); + userInput.clear(); + } + +} \ No newline at end of file diff --git a/src/main/java/command/AddCommand.java b/src/main/java/command/AddCommand.java new file mode 100644 index 0000000000..4597b20280 --- /dev/null +++ b/src/main/java/command/AddCommand.java @@ -0,0 +1,52 @@ +package command; +import process.*; + +import process.DukeException; +import task.Deadline; +import task.Event; +import task.TaskList; +import task.Todo; + +/** + * Represents a command that adds an item to tasks + */ +public class AddCommand extends Command { + private String description; + private String tasktype; + private String datetime; + /** + * Creates a new AddCommand object with the given type of task and description + * @param tasktype The task type + * @param description of the task + */ + public AddCommand(String tasktype, String description) { + this.description = description; + this.tasktype = tasktype; + } + /** + * Creates a new AddCommand object with the given type of task, description and date time + * @param tasktype The task type + * @param description of the task + * @param datetime the date and time + */ + public AddCommand(String tasktype, String description, String datetime) { + this.description = description; + this.tasktype = tasktype; + this.datetime = datetime; + } + /** + * Executes the AddCommand and saves changes to storage + * @param tasks the task list + * @param storage the storage file + * @param ui the user interface object + * @return ui response as a string + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + if (tasktype.equals("todo")) tasks.add(new Todo(description, false)); + else if (tasktype.equals("deadline")) tasks.add(new Deadline(description, datetime, false)); + else if (tasktype.equals("event")) tasks.add(new Event(description, datetime, false)); + else throw new DukeException("add error"); + storage.save(tasks); + return ui.showTaskAdded(tasks.get(tasks.size()-1).toString(), tasks.size()); + } +} \ No newline at end of file diff --git a/src/main/java/command/Command.java b/src/main/java/command/Command.java new file mode 100644 index 0000000000..7b9c7243eb --- /dev/null +++ b/src/main/java/command/Command.java @@ -0,0 +1,26 @@ +package command; +import process.*; +import task.TaskList; + +/** + * Represents a command registered in Duke + */ +public abstract class Command { + protected boolean is_exit = false; + /** + * Executes the command + * @param tasks The task list that you want to execute the command on + * @param ui The user interface object used to respond to the user + * @param storage The storage object used to save the changes made by the executed command + * @return the response as a string from the user interface + * @throws DukeException if command cannot be executed + */ + public abstract String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException; + /** + * Checks if the programme should exit + * @return True if the programme should exit + */ + public boolean isExit() { + return is_exit; + }; +} diff --git a/src/main/java/command/DeleteCommand.java b/src/main/java/command/DeleteCommand.java new file mode 100644 index 0000000000..c184da032d --- /dev/null +++ b/src/main/java/command/DeleteCommand.java @@ -0,0 +1,29 @@ +package command; +import process.*; + +import process.DukeException; +import task.TaskList; + +/** + * Represents a command that deletes an item from tasks + */ +public class DeleteCommand extends Command { + private int index; + /** + * Creates a new DeleteCommand object with the given index + * @param index of the task to be deleted + */ + public DeleteCommand(int index) { + this.index = index; + } + /** + * Executes the DeleteCommand and saves changes to storage + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + if (this.index >= tasks.size()) throw new DukeException("index error"); + String output = ui.showTaskDelete(tasks.get(this.index).toString(),tasks.size()-1); + tasks.deleteTask(index); + storage.save(tasks); + return output; + } +} diff --git a/src/main/java/command/DoneCommand.java b/src/main/java/command/DoneCommand.java new file mode 100644 index 0000000000..002ecdd7f4 --- /dev/null +++ b/src/main/java/command/DoneCommand.java @@ -0,0 +1,24 @@ +package command; +import process.*; +import process.DukeException; +import task.TaskList; + +/** + * Represents a command that checks items as done in tasks + */ +public class DoneCommand extends Command { + private int index; + public DoneCommand(int index) { + this.index = index; + } + /** + * Executes the DoneCommand + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + if (this.index >= tasks.size()) throw new DukeException("index error"); + tasks.doneTask(this.index); + String output = ui.showTaskDone(tasks.get(this.index).toString()); + storage.save(tasks); + return output; + } +} diff --git a/src/main/java/command/ExitCommand.java b/src/main/java/command/ExitCommand.java new file mode 100644 index 0000000000..16aad530cc --- /dev/null +++ b/src/main/java/command/ExitCommand.java @@ -0,0 +1,17 @@ +package command; +import process.*; +import task.TaskList; + +/** + * Represents a command that exits from the programme + */ +public class ExitCommand extends Command { + /** + * Executes the ExitCommand + */ + public String execute(TaskList tasks, Ui ui, Storage storage) { + super.is_exit = true; + ui.close(); + return ""; + } +} diff --git a/src/main/java/command/FindCommand.java b/src/main/java/command/FindCommand.java new file mode 100644 index 0000000000..1e323bf6cd --- /dev/null +++ b/src/main/java/command/FindCommand.java @@ -0,0 +1,24 @@ +package command; +import process.*; +import process.DukeException; +import task.TaskList; + +/** + * Represents a command that finds items from tasks + */ +public class FindCommand extends Command { + private String keyword; + /** + * Creates a new FindCommand object with the given keyword + * @param keyword used to find task + */ + public FindCommand(String keyword) { + this.keyword = keyword; + } + /** + * Executes the FindCommand + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + return ui.print_this(tasks.find(keyword)); + } +} diff --git a/src/main/java/command/ListCommand.java b/src/main/java/command/ListCommand.java new file mode 100644 index 0000000000..6e7e35583c --- /dev/null +++ b/src/main/java/command/ListCommand.java @@ -0,0 +1,15 @@ +package command; +import process.*; +import task.TaskList; + +/** + * Represents a command that list items from tasks + */ +public class ListCommand extends Command{ + /** + * Executes the ListCommand + */ + public String execute(TaskList tasks, Ui ui, Storage storage) throws DukeException { + return ui.print_this(tasks.print_list()); + } +} diff --git a/src/main/java/process/DatetimeFormatter.java b/src/main/java/process/DatetimeFormatter.java new file mode 100644 index 0000000000..e5ab32a406 --- /dev/null +++ b/src/main/java/process/DatetimeFormatter.java @@ -0,0 +1,76 @@ +package process; + +import process.DukeException; + +/** + * Represents a datetime formatter + */ +public class DatetimeFormatter { + /** + * Validates that the datetime is valid + * @param str1 the datetime in the form of a String to be checked + * @return str1 back if it is a valid datetime + * @throws DukeException if str1 is not a valid datetime + */ + public static String check(String str1) throws DukeException { + try { + str1 = str1.trim(); + String[] datetime = str1.split(" "); + String[] ddmmyyyy = datetime[0].split("/"); + String day = ddmmyyyy[0]; + String month = ddmmyyyy[1]; + String year = ddmmyyyy[2]; + String time = datetime[1]; + int[] daysinmonth = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + int month_i = Integer.parseInt(month); + int year_i = Integer.parseInt(year); + Integer day_i = Integer.parseInt(day); + String min = time.substring(2); + String hr = time.substring(0, 2); + Integer min_i = Integer.parseInt(min); + Integer hr_i = Integer.parseInt(hr); + if (hr_i > 23 || min_i > 59 || month_i > 12 || year_i < 0 || day_i > daysinmonth[month_i - 1]) + throw new DukeException("datetime"); + return str1; + } catch (ArrayIndexOutOfBoundsException e) { + throw new DukeException("datetime"); + } catch (NumberFormatException e) { + throw new DukeException("datetime"); + } + } + /** + * Formats the datetime object to match the exact format required + * @param str1 the datetime in the form of a String to be formatted + * @return the datetime in the exact format required + */ + public static String view(String str1) { + try { + str1 = str1.trim(); + String[] datetime = str1.split(" "); + String day = datetime[0]; + String[] stndrd = {"st", "nd", "rd"}; + Integer day_i = Integer.parseInt(day); + String day_x; + if (day_i >= 10 && day_i <= 20 || day_i % 10 > 3 || day_i % 10 == 0) { + day_x = day_i.toString() + "th"; + } else { + day_x = day_i.toString() + stndrd[day_i % 10 - 1]; + } + str1 = day_x + str1.substring(2); + int min_start = str1.indexOf(':'); + String min = "" + str1.charAt(min_start + 1) + str1.charAt(min_start + 2); + String ampm = str1.substring(str1.length() - 2).toLowerCase(); + if (Integer.parseInt(min) == 0) { + str1 = str1.substring(0, min_start) + ampm; + } + else { + str1 = str1.substring(0, str1.length() - 3) + ampm; + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } catch (ArrayIndexOutOfBoundsException e) { + e.printStackTrace(); + } + return str1; + } +} \ No newline at end of file diff --git a/src/main/java/process/DukeException.java b/src/main/java/process/DukeException.java new file mode 100644 index 0000000000..8bb0774283 --- /dev/null +++ b/src/main/java/process/DukeException.java @@ -0,0 +1,28 @@ +package process; + +/** + * Represents an exception thrown by Duke that may be specific to its application + */ +public class DukeException extends Exception { + private String error; + /** + * Creates a new DukeException of e + * @param e The error message + */ + public DukeException(String e) { + this.error = e; + } + /** + * Represents the exception in a String + */ + public String toString() { + return "DukeException[" + error + "]"; + } + @Override + /** + * Retrieve the message from the error + */ + public String getMessage() { + return this.error; + } +} diff --git a/src/main/java/process/Parser.java b/src/main/java/process/Parser.java new file mode 100644 index 0000000000..23eaff76a4 --- /dev/null +++ b/src/main/java/process/Parser.java @@ -0,0 +1,73 @@ +package process; + +import command.*; +import process.DukeException; + +import java.util.ArrayList; +import java.util.Arrays; +/** + * Represents a parser to make sense of user input and translate it to commands for Duke + */ +public class Parser { + /** + * Interprets user input + * @param input to be interpreted + * @return Duke commands based on user input + * @throws DukeException if Duke cannot make sense of the input + */ + public static Command parse(String input) throws DukeException { //input validation + ArrayList command_list = new ArrayList(Arrays.asList("bye", "list", "find", "delete", "done", "todo", "deadline", "event")); + String operation; + String date; + int index =-1; + String arg1; + int command_status = -1; + if (!input.isBlank()) command_status = command_list.indexOf(input.split(" ")[0]); + else { + throw new DukeException("unknown"); + } + if (command_status > -1) { //keyword 1 is accepted + String[] operation_list = input.split(" "); + operation = operation_list[0]; + if ((operation.equals("delete") || operation.equals("done"))) { + if (operation_list.length != 2) throw new DukeException("index error" + input.length()); + try { + index = Integer.parseInt(operation_list[1]) -1; + if (index < 0) throw new DukeException("index error"); + } catch (NumberFormatException e) { + throw new DukeException("index error"); + } + if (operation.equals("delete")) return new DeleteCommand(index); + else return new DoneCommand(index); //done + } else if (operation.equals("find") || operation.equals("todo")) { + if (operation_list.length == 1) throw new DukeException("arg1 error "+ operation); + arg1 = input.substring(5); + if (arg1.isBlank()) throw new DukeException("arg1 error "+ operation); + arg1.trim(); + if (operation.equals("find")) return new FindCommand(arg1); + else return new AddCommand("todo", arg1); + } else if (operation.equals("deadline")) { + int by_index = input.indexOf(" /by "); + if (by_index == -1) throw new DukeException("datetime"); + arg1 = input.substring(8, by_index).trim(); + if (arg1.isBlank()) throw new DukeException("arg1 error "+ operation); + date = input.substring(by_index + 5).trim(); + if (date.isBlank()) throw new DukeException("datetime"); + return new AddCommand("deadline", arg1, date); + } else if (operation.equals("event")) { + int at_index = input.indexOf(" /at "); + if (at_index == -1) throw new DukeException("datetime"); + arg1 = input.substring(5, at_index).trim(); + if (arg1.isBlank()) throw new DukeException("arg1 error "+ operation); + date = input.substring(at_index + 5).trim(); + if (date.isBlank()) throw new DukeException("datetime"); + return new AddCommand("event", arg1, date); + } else if (operation.equals("bye")) { + return new ExitCommand(); + } else if (operation.equals("list")) { + return new ListCommand(); + } + } + throw new DukeException("unknown"); + } +} \ No newline at end of file diff --git a/src/main/java/process/Storage.java b/src/main/java/process/Storage.java new file mode 100644 index 0000000000..09ae7d83d2 --- /dev/null +++ b/src/main/java/process/Storage.java @@ -0,0 +1,57 @@ +package process; + +import process.DukeException; +import task.Task; +import task.TaskList; + +import java.io.*; +/** + * Represents a storage file inside data + */ +public class Storage { + private String file_content; + private String file_path; + /** + * Create a storage file found at filepath + * @param filePath to be used for storage + */ + public Storage (String filePath){ + file_path = filePath; + file_content = ""; + try (BufferedReader br = new BufferedReader(new FileReader(filePath))) { + String line_X; + while ((line_X = br.readLine()) != null) { + file_content += line_X + "\n"; + } + } catch (IOException e) { + System.out.println(filePath + " file not found, creating file..."); + } + } + /** + * Laod the file from the filepath + * @return the file in the form of a string + * @throws DukeException if the file cannot be found or if there is no task in the list + */ + public String load () throws DukeException { + if (file_content.isBlank()) { + throw new DukeException("file_not_found"); + } + return file_content; + } + /** + * Save the tasklist given to the filepath + * @param tasks the tasklist to be saved + */ + public void save(TaskList tasks) { + String sep = "}-}"; + String output = ""; + for (Task i : tasks) { + output += i.save_as(sep); + } + try (PrintWriter out = new PrintWriter(file_path)) { + out.println(output); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/process/Ui.java b/src/main/java/process/Ui.java new file mode 100644 index 0000000000..28c2f96ebe --- /dev/null +++ b/src/main/java/process/Ui.java @@ -0,0 +1,129 @@ +package process; + +import java.util.Scanner; + +import static java.lang.System.*; +/** + * Represents the user interface + */ +public class Ui { + Scanner input = new Scanner(System.in); + private String line = " ____________________________________________________________"; + private String logo = " ____ _ \n" + + "| _ \\ _ _| | _____ \n" + + "| | | | | | | |/ / _ \\\n" + + "| |_| | |_| | < __/\n" + + "|____/ \\__,_|_|\\_\\___|\n"; + /** + * Shows the welcome message + */ + public void showWelcome() { + System.out.println(line); + System.out.print(logo); + System.out.println(line); + System.out.println("Hello! I'm Duke"); + System.out.println("What can I do for you?"); + System.out.println(line); + } + /** + * Shows a line + */ + public void showLine() { + System.out.println(line); + } + /** + * Shows and + * @return whatever string is passed into the function + * @param thingy to be printed + */ + public String print_this(String thingy) { + System.out.println(thingy); + return thingy; + } + /** + * Reads and + * @return user input from keyboard + */ + public String readCommand() { + return input.nextLine(); + } + /** + * Shows the goodbye message + */ + public void close() { + System.out.println("Bye. Hope to see you again soon!"); + } + /** + * Shows and returns the error message + * @param error_msg the error message to be formatted and printed + * @return the formatted error message + */ + public String showError(String error_msg) { + String out_ = ""; + if(error_msg.equals("datetime")) { + out_ = "OOPS!!! Please enter the datetime in this format: dd/mm/yyyy HHMM"; + } else if(error_msg.equals("unknown")) { + out_ = ("OOPS!!! I'm sorry, but I don't know what that means :-("); + } else if (error_msg.equals("arg1 error find")) { + out_ = "OOPS!!! Please enter a keyword or phrase for your search"; + } else if (error_msg.equals("arg1 error todo")) { + out_ = "OOPS!!! The description of a todo cannot be blank"; + } else if (error_msg.equals("arg1 error deadline")) { + out_ = "OOPS!!! The description of a deadline cannot be blank"; + } else if (error_msg.equals("arg1 error event")) { + out_ = "OOPS!!! The description of an event cannot be blank"; + } else if (error_msg.equals("index error")) { + out_ = "OOPS!!! Please enter a valid task index number"; + } else if (error_msg.equals("empty list")) { + out_ = "You have no tasks in your list"; + } else if (error_msg.equals("empty task")) { + out_ ="OOPS!!! The description of a task cannot be empty."; + } + else out_ = ("OOPS!!! " + error_msg); + System.out.println(out_); + return out_; + } + /** + * Shows and returns the loading error + * @return loading error + */ + public String showLoadingError() { + String out_ = "OOPS!!! I'm sorry, but your saved file cannot be found"; + System.out.println(out_); + return out_; + } + /** + * Shows and + * @return the given taska as done + * @param task to be displayed as done + */ + public String showTaskDone(String task) { +// String out_ = "Nice! I've marked this task as done:\n" + "[✓] " + task; + String out_ = "Nice! I've marked this task as done:\n" + task; + System.out.println(out_); + return out_; + } + /** + * Shows and + * @return the given task as deleted + * @param task to be shown as deleted + * @param size of the task list + */ + public String showTaskDelete(String task, int size) { + String out_ = "Noted. I've removed this task:" + "\n" + "\t" + task + "\n" + "Now you have " + Integer.toString(size) + " tasks in the list."; + System.out.println(out_); + return out_; + } + /** + * Shows and + * @return the given task as added + * @param task to be shown as added + * @param size of the task list + */ + public String showTaskAdded(String task, int size) { + String out_ = "Got it. I've added this task:" + "\n" + "\t" + task +"\n" + "Now you have " + Integer.toString(size) + " tasks in the list."; + System.out.println(out_); + return out_; + } + //☹ +} diff --git a/src/main/java/task/Deadline.java b/src/main/java/task/Deadline.java new file mode 100644 index 0000000000..1f6f097e08 --- /dev/null +++ b/src/main/java/task/Deadline.java @@ -0,0 +1,47 @@ +package task; +import process.*; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * Represents a Deadline-typed-task + */ +public class Deadline extends Task { + protected Date by; + DateFormat fmt = new SimpleDateFormat("dd MMMM yyyy, h:mm a", Locale.US); + /** + * Creates a Deadline object with the given description, datetime and whether it is done + * @param description of the task + * @param by the due datetime of the deadline + * @param b if the task is checked + * @throws DukeException if the deadline is not valid + */ + public Deadline(String description, String by,boolean b) throws DukeException { + super(description, b); + if (by.isBlank()) { + throw new DukeException("blank by"); + } try { + DatetimeFormatter.check(by); + SimpleDateFormat sdf = new SimpleDateFormat("dd/M/yyyy hhmm"); + by = by.trim(); + Date date = sdf.parse(by); + this.by = date; + super.tt = "D"; + super.extra = by; + } catch (ParseException e) { + throw new DukeException("datetime"); + } + } + + /** + * Represents the object of this class as a string + * @return that string + */ + @Override + public String toString() { + return "[D]" + super.toString() + " (by: " + DatetimeFormatter.view(this.fmt.format(by)) + ")"; + } +} \ No newline at end of file diff --git a/src/main/java/task/Event.java b/src/main/java/task/Event.java new file mode 100644 index 0000000000..2161428454 --- /dev/null +++ b/src/main/java/task/Event.java @@ -0,0 +1,46 @@ +package task; +import process.*; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; + +/** + * Represents an Event-typed-task + */ +public class Event extends Task{ + protected Date at; + DateFormat fmt = new SimpleDateFormat("dd MMMM yyyy, h:mm a", Locale.US); + /** + * Creates an Event object with the given description, datetime and whether it is done + * @param description of the Event + * @param b if the task is checked or not + * @param at the date and time of the event + * @throws DukeException if there is an error in creating the event + */ + public Event(String description, String at, boolean b) throws DukeException { + super(description, b); + if (at.isBlank()) { + throw new DukeException("blank at"); + } + try { + DatetimeFormatter.check(at); + SimpleDateFormat sdf = new SimpleDateFormat("dd/M/yyyy hhmm"); + Date date = sdf.parse(at); + this.at = date; + super.tt = "D"; + super.extra = at; + } catch (ParseException e){ + throw new DukeException("datetime"); + } + } + /** + * Represents the object of this class as a string + * @return that string + */ + @Override + public String toString() { + return "[E]" + super.toString() + " (at: " + DatetimeFormatter.view(this.fmt.format(at)) + ")"; + } +} diff --git a/src/main/java/task/Task.java b/src/main/java/task/Task.java new file mode 100644 index 0000000000..4f5135798f --- /dev/null +++ b/src/main/java/task/Task.java @@ -0,0 +1,48 @@ +package task; +import process.*; +/** + * Represents a Task recorded by Duke task manager + */ +public class Task { + protected String description; + protected boolean isDone; + protected String tt; + protected String extra; + /** + * Creates a Task object with the given description and whether it is done + * @param description of the task + * @param b if a task is checked + * @throws DukeException if an error has occured in constructing the task + */ + public Task(String description, boolean b) throws DukeException { + this.tt = ""; + this.extra = ""; + this.description = description; + this.isDone = b; + if (description.isBlank()) { + throw new DukeException("empty task"); + } + } + /** + * Pack the Task in the form of a string with the given separator for writing to a file + * @param sep the separator used + * @return the save form of a task + */ + public String save_as(String sep) { + return tt + sep + isDone + sep + description + sep + extra + "\n"; + } + /** + * Mark a Task as b + * @param b checked or not checked + */ + public void done(boolean b) { + this.isDone = b; + } + /** + * Represents the object of this class as a string + * @return that string + */ + public String toString() { + return (isDone ? "[Done] " : "[X] ") + this.description; + } +} \ No newline at end of file diff --git a/src/main/java/task/TaskList.java b/src/main/java/task/TaskList.java new file mode 100644 index 0000000000..b69db837de --- /dev/null +++ b/src/main/java/task/TaskList.java @@ -0,0 +1,101 @@ +package task; +import process.*; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * Represents the tasklist that duke is managing + */ +public class TaskList extends ArrayList{ + String sep = "}-}"; + /** + * Creates an empty task list + */ + public TaskList() { + } + /** + * Creates a task list from file + * @param file the file in the form of a string to be converted to the task list + */ + public TaskList(String file) { + if (file.isBlank()) { + return; + } + String[] itemlist = file.split("\n"); + int line = 0; + for (String item: itemlist) { + line ++; + String[] attributes = item.split(sep); + try { + if (attributes[0].equals("T")) { + this.add(new Todo(attributes[2], attributes[1].equals("true"))); + } else if (attributes[0].equals("D")) { + this.add(new Deadline(attributes[2], DatetimeFormatter.check(attributes[3]), attributes[1].equals("true"))); + } else if (attributes[0].equals("E")) { + this.add(new Event(attributes[2], DatetimeFormatter.check(attributes[3]), attributes[1].equals("true"))); + } else { + System.out.println("☹ OOPS!!! Line " + Integer.toString(line) + " in duke.txt is corrupted" + ", skipping..."); + } + } catch (DukeException e) { + System.out.println("☹ OOPS!!! Line " + Integer.toString(line) + " in duke.txt is corrupted[" + e + "], skipping..."); + } catch (ArrayIndexOutOfBoundsException e) { + System.out.println("☹ OOPS!!! Line " + Integer.toString(line) + " in duke.txt is corrupted[" + e + "], skipping..."); + } + } + } + /** + * Formats the task list in a readable format + * @return a task list in the form of a string + * @throws DukeException if the list is empty + */ + public String print_list() throws DukeException { + if (this.size()== 0) throw new DukeException("empty list"); + String output ="Here are the tasks in your list:"; + int x = 0; + Iterator itr=this.iterator(); + while(itr.hasNext()){ + output += "\n" + (Integer.toString(x + 1) + "."); + output += (itr.next().toString()); + x ++; + } + return output; + } + /** + * Deletes the task at given index + * @param index of the task to be deleted + */ + public void deleteTask(int index) { + this.remove(index); + } + + /** + * Marks the task at given index as done + * @param index of the task to be marked as done + */ + public void doneTask(int index) { + this.get(index).done(true); + } + + /** + * Searches for a task with the given keyword + * @param keyword to be found + * @return The list of task with the keyword + */ + public String find(String keyword) { + int x = 0; + String output = "Here are the matching tasks in your list:"; + boolean have_result= false; + for (Task task: this) { + if (task.toString().indexOf(keyword) != -1) { + output += "\n" + (Integer.toString(x + 1) + "."); + output += (task.toString()); + x ++; + have_result = true; + } + } + if (have_result == false) { + return ("Sorry, no results found"); + } + return output; + } +} diff --git a/src/main/java/task/Todo.java b/src/main/java/task/Todo.java new file mode 100644 index 0000000000..ac013c52c1 --- /dev/null +++ b/src/main/java/task/Todo.java @@ -0,0 +1,25 @@ +package task; +import process.*; +/** + * Represents a Todo-typed-task + */ +public class Todo extends Task{ + /** + * Creates a Todo object with the given description and whether it is done + * @param description of the todo task + * @param b if the task is checked or not + * @throws DukeException if the Todo task is invalid + */ + public Todo(String description, boolean b) throws DukeException { + super(description, b); + super.tt = "T"; + } + /** + * Represents the object of this class as a string + * @return that string + */ + @Override + public String toString() { + return "[T]" + super.toString(); + } +} diff --git a/src/main/resources/images/DaDuke.jpg b/src/main/resources/images/DaDuke.jpg new file mode 100644 index 0000000000..f68cbb617e Binary files /dev/null and b/src/main/resources/images/DaDuke.jpg differ diff --git a/src/main/resources/images/DaUser.jpg b/src/main/resources/images/DaUser.jpg new file mode 100644 index 0000000000..6b0a4c0f08 Binary files /dev/null and b/src/main/resources/images/DaUser.jpg differ diff --git a/src/main/resources/view/DialogBox.fxml b/src/main/resources/view/DialogBox.fxml new file mode 100644 index 0000000000..9a7d91eecc --- /dev/null +++ b/src/main/resources/view/DialogBox.fxml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml new file mode 100644 index 0000000000..1f1f505c03 --- /dev/null +++ b/src/main/resources/view/MainWindow.fxml @@ -0,0 +1,19 @@ + + + + + + + + + + + +