diff --git a/README.md b/README.md index c481c2784cb6..69c764924f7e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ [![Build Status](https://travis-ci.org/CS2103AUG2016-T14-C2/main.svg?branch=master)](https://travis-ci.org/CS2103AUG2016-T14-C2/main) [![Coverage Status](https://coveralls.io/repos/github/CS2103AUG2016-T14-C2/main/badge.svg?branch=master)](https://coveralls.io/github/CS2103AUG2016-T14-C2/main?branch=master) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/91232e72b4c645dc9d988942651ca906)](https://www.codacy.com/app/xefrog/main?utm_source=github.com&utm_medium=referral&utm_content=CS2103AUG2016-T14-C2/main&utm_campaign=Badge_Grade) # Savvy Tasker @@ -22,6 +23,7 @@ * Some parts of this sample application were inspired by the excellent [Java FX tutorial](http://code.makery.ch/library/javafx-8-tutorial/) by *Marco Jakob*. * This is a sample project created by [SE-EDU](https://githubcom/se-edu/) initiative. +* [Natty](http://natty.joestelmach.com/) for parsing user input dates #### Licence : [MIT](LICENSE) diff --git a/collated/docs/A0138431L.md b/collated/docs/A0138431L.md new file mode 100644 index 000000000000..02e7cee1bff4 --- /dev/null +++ b/collated/docs/A0138431L.md @@ -0,0 +1,63 @@ +# A0138431L +###### \DeveloperGuide.md +``` md +### UI component + +
+ +**API** : [`Ui.java`](../src/main/java/seedu/savvytasker/ui/Ui.java) + +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`, `UpcomingPanel`, `DailyPanel`, `FloatingPanel`, +`StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class +and they can be loaded using the `UiPartLoader`. + +The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files + that are in the `src/main/resources/view` folder.
+ For example, the layout of the [`MainWindow`](../src/main/java/seedu/savvytasker/ui/MainWindow.java) is specified in + [`MainWindow.fxml`](../src/main/resources/view/MainWindow.fxml) + +The `UI` component, +* Executes user commands using the `Logic` component. +* Binds itself to some data in the `Model` so that the UI can auto-update when data in the `Model` change. +* Responds to events raised from various parts of the App and updates the UI accordingly. + +The cursor will be focus to the `CommandBox` by default as the `CommandBox` carries out numerous keyboard shortcuts to make the app more user-friendly. + +``` +###### \UserGuide.md +``` md + +#### Command stack history +UP: Return last user input command in command box
+DOWN: Return (if any) next user input command in command box
+> Note that DOWN is only allowed after at least an UP is being entered + + +#### Week Selection +Ctrl + LEFT: Display previous week’s daily task list
+Ctrl + RIGHT: Display next week’s daily task list + +``` +###### \UserGuide.md +``` md + +## Keyboard Shortcuts + +Key Codes | Function | Command Box Input +-------- | :-------- | :-------- +Esc | Toggle to show/hide a list of keyboard shortcuts | - +Ctrl + D | [Clear](#clearing-all-entries--clear) all entries | `clear` +Ctrl + Q | [Exit](#exiting-the-program--exit) | `exit` +Ctrl + L | [List](#listing-all-tasks-list) all unmarked task by date, earliest task first | `list` +Ctrl + A | [List](#listing-all-tasks-list) archived tasks | `list archived` +Ctrl + P | [List](#listing-all-tasks-list) all unmarked task by priority level, high priority first | `list priorityLevel` +Ctrl + I | [List](#listing-all-tasks-list) all alias keys | `list alias` +Ctrl + H | [Help](#viewing-help--help) | `help` +Ctrl + S | Popups a directory chooser dialog box to choose a new filepath | `storage NEW_FILEPATH` +Ctrl + Z | [Undo](#undo-the-most-recent-operation--undo) | `undo` +Ctrl + Y | [Redo](#redo-the-most-recent-undo-operation--redo) | `redo` +Ctrl + UP | Return [last user input](#command-stack-history) command in command box | - +Ctrl + DOWN | Return (if any) [next user input](#command-stack-history) command in command box | - +Ctrl + LEFT | Displays [previous week’s](#week-selection) daily task list | - +Ctrl + RIGHT | Display [next week’s](#week-selection) daily task list | - +``` diff --git a/collated/docs/A0139915W.md b/collated/docs/A0139915W.md index d3102f52e3e7..bf3949d34116 100644 --- a/collated/docs/A0139915W.md +++ b/collated/docs/A0139915W.md @@ -172,13 +172,17 @@ Format: `modify INDEX [t/TASK_NAME] [s/START_DATE] [e/END_DATE] [l/LOCATION] [p/ #### Change storage location : `storage` Changes the storage location of Savvy Tasker.
-Format: `storage PATH` +Format: `storage NEW_FILEPATH` > Parameters | Description > -------- | :-------- > PATH | `Mandatory` Specifies the path where Savvy Tasker's task list is saved at. >
-> If the new storage location specified by `PATH` is not accessible by Savvy Tasker, no change will be made to the existing path. +> If the new storage location specified by `NEW_FILEPATH` is not accessible by Savvy Tasker, no change will be made to the existing path. + +``` +###### \UserGuide.md +``` md ``` ###### \UserGuide.md @@ -198,9 +202,10 @@ Command | Format [Help](#viewing-help--help) | `help` [Modify](#modifies-a-task--modify) | `modify INDEX [t/TASK_NAME] [s/START_DATE] [e/END_DATE] [l/LOCATION] [p/PRIORITY_LEVEL] [r/RECURRING_TYPE] [n/NUMBER_OF_RECURRENCE] [c/CATEGORY] [d/DESCRIPTION]`
Example: `modify 2 t/Wednesday Weekly Milestone s/wed d/Project Meeting and Finalization` [Mark](#mark-a-task-as-done--mark) | `mark INDEX [MORE_INDEX]`
Example: `mark 1 2 3` -[Storage](#change-storage-location--storage) | `storage PATH`
Example: `storage data/savvytasker.xml` +[Storage](#change-storage-location--storage) | `storage NEW_FILEPATH`
Example: `storage data/savvytasker.xml` [Unmark](#unmark-a-task-as-done--unmark) | `unmark INDEX [MORE_INDEX]`
Example: `unmark 1 2 3` [Undo](#undo-the-most-recent-operation--undo) | `undo` [Redo](#redo-the-most-recent-undo-operation--redo) | `redo` [Unalias](#unalias-a-keyword--unalias) | `unalias s/SHORT_KEYWORD`
Example: `unalias s/mss` + ``` diff --git a/collated/docs/A0139916U.md b/collated/docs/A0139916U.md index 79fcc57373bc..1f2e1869055d 100644 --- a/collated/docs/A0139916U.md +++ b/collated/docs/A0139916U.md @@ -56,8 +56,8 @@ Format: `alias k/KEYWORD r/REPRESENTATION` > Parameters | Description > -------- | :-------- -> KEYWORD | Specifies the keyword that will be replaced when met in a command, must be a single word -> REPRESENTATION | Specifies the text that will replace the keyword +> KEYWORD | Specifies the keyword that will be replaced when met in a command, must be a single word. +> REPRESENTATION | Specifies the text that will replace the keyword. Cannot contain slashes. Examples: * `alias k/pjm r/Project Meeting`
diff --git a/collated/main/A0097627N.md b/collated/main/A0097627N.md index 58bc1d6cc7f0..71f0fe56738c 100644 --- a/collated/main/A0097627N.md +++ b/collated/main/A0097627N.md @@ -687,8 +687,6 @@ public static final String MESSAGE_REDO_ACKNOWLEDGEMENT = "Last command redone"; - public RedoCommand() {} - @Override public CommandResult execute() { return new CommandResult(MESSAGE_REDO_ACKNOWLEDGEMENT); @@ -899,8 +897,6 @@ public static final String MESSAGE_UNDO_ACKNOWLEDGEMENT = "Last command undone"; - public UndoCommand() {} - @Override public CommandResult execute() { return new CommandResult(MESSAGE_UNDO_ACKNOWLEDGEMENT); @@ -1070,7 +1066,4 @@ } } else if (command.canUndo()){ - undoStack.push(command); - redoStack.clear(); - } ``` diff --git a/collated/main/A0138431L.md b/collated/main/A0138431L.md new file mode 100644 index 000000000000..c09bfabfb133 --- /dev/null +++ b/collated/main/A0138431L.md @@ -0,0 +1,2386 @@ +# A0138431L +###### \java\seedu\savvytasker\commons\events\storage\DataSavingLocationChangedEvent.java +``` java +/** + * Indicates a change in location of the storage + */ +public class DataSavingLocationChangedEvent extends BaseEvent { + + public final ReadOnlySavvyTasker data; + public final String newPath; + + public DataSavingLocationChangedEvent(ReadOnlySavvyTasker data, String newPath) { + this.data = data; + this.newPath = newPath; + } + + @Override + public String toString() { + return "number of tasks " + data.getReadOnlyListOfTasks().size() + + " new path " + this.newPath; + } + +} +``` +###### \java\seedu\savvytasker\commons\events\ui\ShowCheatsheetEvent.java +``` java + +package seedu.savvytasker.commons.events.ui; + +import seedu.savvytasker.commons.events.BaseEvent; + +/** Indicates cheatsheet display has been toggled */ +public class ShowCheatsheetEvent extends BaseEvent { + @Override + public String toString() { + return "Cheatsheet display has been toggled"; + } + +} +``` +###### \java\seedu\savvytasker\commons\events\ui\WeekSelectionChangedEvent.java +``` java + +package seedu.savvytasker.commons.events.ui; + +import seedu.savvytasker.commons.events.BaseEvent; + +/** Indicates the SavvyTasker in the model has changed*/ + +public class WeekSelectionChangedEvent extends BaseEvent { + + @Override + public String toString() { + return "Selected week has been changed"; + } + +} +``` +###### \java\seedu\savvytasker\logic\commands\StorageAndModelRequiringCommand.java +``` java +/** + * Represents a command which requires the Storage class as a dependency. + * Commands should inherit this class if they only require dependency for + * storage and model components +*/ +public abstract class StorageAndModelRequiringCommand extends Command { + protected Storage storage; + protected Model model; + + public void setStorage(Storage storage) { + this.storage = storage; + } + + public void setModel(Model model) { + this.model = model; + } +} +``` +###### \java\seedu\savvytasker\logic\commands\StorageCommand.java +``` java +/** + * Changes the storage location of Savvy Tasker + */ +public class StorageCommand extends StorageAndModelRequiringCommand { + + public final String path; + + public static final String COMMAND_WORD = "storage"; + + public static final String MESSAGE_USAGE = COMMAND_WORD + + ": Sets the storage path to the path specified.\n" + + "Parameters: PATH\n" + + "Example: " + COMMAND_WORD + " data/savvytasker.xml"; + + public static final String MESSAGE_CHANGE_LOCATION_SUCCESS = "Changed storage location to: %1$s"; + public static final String MESSAGE_CHANGE_LOCATION_FAILED = "Failed to change storage location to: %1$s"; + + public StorageCommand(String path) { + this.path = path; + } + + @Override + public CommandResult execute() { + if (storage.setSavvyTaskerFilePath(path)) { + ReadOnlySavvyTasker savvyTasker = model.getSavvyTasker(); + EventsCenter.getInstance().post(new DataSavingLocationChangedEvent(savvyTasker, path)); + return new CommandResult(String.format(MESSAGE_CHANGE_LOCATION_SUCCESS, path)); + } else { + return new CommandResult(String.format(MESSAGE_CHANGE_LOCATION_FAILED, path)); + } + } + +``` +###### \java\seedu\savvytasker\logic\LogicManager.java +``` java + + @Override + public ObservableList getFilteredFloatingTasks() { + return model.getFilteredFloatingTasks(); + } + + @Override + public ObservableList getFilteredDailyTasks(int i, Date date) { + return model.getFilteredDailyTasks(i, date); + } + + @Override + public ObservableList getFilteredUpcomingTasks(Date date) { + return model.getFilteredUpcomingTasks(date); + } +``` +###### \java\seedu\savvytasker\logic\parser\TaskFieldParser.java +``` java + public String parsefilePath(String filePathText) throws ParseException { + if (filePathText == null) { + return null; + } + return filePathText.trim(); + } + + public String parsefileName(String fileNameText) throws ParseException { + if (fileNameText == null) { + return null; + } + return fileNameText.trim(); + } +``` +###### \java\seedu\savvytasker\MainApp.java +``` java + @Subscribe + public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { + try { + String configPath = getApplicationParameter("config"); + if(configPath == null) { + configPath = Config.DEFAULT_CONFIG_FILE; + } + config.setSavvyTaskerFilePath(dslce.newPath); + ConfigUtil.saveConfig(config, configPath); + } catch (IOException e) { + logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); + } + } +``` +###### \java\seedu\savvytasker\model\Model.java +``` java + + /** Returns the filtered task list of floating task as an {@code UnmodifiableObservableList} */ + UnmodifiableObservableList getFilteredFloatingTasks(); + + /** Returns the filtered task list of daily task as an {@code UnmodifiableObservableList} + * as of expected date */ + UnmodifiableObservableList getFilteredDailyTasks(int dayOfWeek, Date date); + + /** Returns the filtered task list of upcoming task as an {@code UnmodifiableObservableList} + * as of expected date */ + UnmodifiableObservableList getFilteredUpcomingTasks(Date date); + + /** Updates the filter of the filtered task list to show all floating tasks */ + void updateFilteredListToShowFloating(); + + /** Updates the filter of the filtered task list to show all tasks of the selected week*/ + void updateFilteredListToShowDaily(int i); + + /** Updates the filter of the filtered task list to show all upcoming tasks after the selected week*/ + void updateFilteredListToShowUpcoming(); + +``` +###### \java\seedu\savvytasker\model\ModelManager.java +``` java + private final SavvyTasker savvyTasker; + private final FilteredList filteredTasks; + private final SortedList sortedAndFilteredTasks; + private final FilteredList filteredFloatingTasks; + private final SortedList sortedAndFilteredFloatingTasks; + private final FilteredList filteredDay1Tasks; + private final SortedList sortedAndFilteredDay1Tasks; + private final FilteredList filteredDay2Tasks; + private final SortedList sortedAndFilteredDay2Tasks; + private final FilteredList filteredDay3Tasks; + private final SortedList sortedAndFilteredDay3Tasks; + private final FilteredList filteredDay4Tasks; + private final SortedList sortedAndFilteredDay4Tasks; + private final FilteredList filteredDay5Tasks; + private final SortedList sortedAndFilteredDay5Tasks; + private final FilteredList filteredDay6Tasks; + private final SortedList sortedAndFilteredDay6Tasks; + private final FilteredList filteredDay7Tasks; + private final SortedList sortedAndFilteredDay7Tasks; + private final FilteredList filteredUpcomingTasks; + private final SortedList sortedAndFilteredUpcomingTasks; + + /** + * Initializes a ModelManager with the given SavvyTasker + * and its variables should not be null + */ + public ModelManager(SavvyTasker src) { + super(); + assert src != null; + + logger.fine("Initializing with savvy tasker: " + src); + + savvyTasker = new SavvyTasker(src); + filteredTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredTasks = new SortedList<>(filteredTasks, new TaskSortedByDefault()); + + filteredFloatingTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredFloatingTasks = new SortedList<>(filteredFloatingTasks, new TaskSortedByDefault()); + + filteredDay1Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay1Tasks = new SortedList<>(filteredDay1Tasks, new TaskSortedByDefault()); + filteredDay2Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay2Tasks = new SortedList<>(filteredDay2Tasks, new TaskSortedByDefault()); + filteredDay3Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay3Tasks = new SortedList<>(filteredDay3Tasks, new TaskSortedByDefault()); + filteredDay4Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay4Tasks = new SortedList<>(filteredDay4Tasks, new TaskSortedByDefault()); + filteredDay5Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay5Tasks = new SortedList<>(filteredDay5Tasks, new TaskSortedByDefault()); + filteredDay6Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay6Tasks = new SortedList<>(filteredDay6Tasks, new TaskSortedByDefault()); + filteredDay7Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay7Tasks = new SortedList<>(filteredDay7Tasks, new TaskSortedByDefault()); + + filteredUpcomingTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredUpcomingTasks = new SortedList<>(filteredUpcomingTasks, new TaskSortedByDefault()); + + updateFilteredListToShowActive(); // shows only active tasks on start + } + + public ModelManager() { + this(new SavvyTasker()); + } + + public ModelManager(ReadOnlySavvyTasker initialData) { + savvyTasker = new SavvyTasker(initialData); + filteredTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredTasks = new SortedList<>(filteredTasks, new TaskSortedByDefault()); + + filteredFloatingTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredFloatingTasks = new SortedList<>(filteredFloatingTasks, new TaskSortedByDefault()); + + filteredDay1Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay1Tasks = new SortedList<>(filteredDay1Tasks, new TaskSortedByDefault()); + filteredDay2Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay2Tasks = new SortedList<>(filteredDay2Tasks, new TaskSortedByDefault()); + filteredDay3Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay3Tasks = new SortedList<>(filteredDay3Tasks, new TaskSortedByDefault()); + filteredDay4Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay4Tasks = new SortedList<>(filteredDay4Tasks, new TaskSortedByDefault()); + filteredDay5Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay5Tasks = new SortedList<>(filteredDay5Tasks, new TaskSortedByDefault()); + filteredDay6Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay6Tasks = new SortedList<>(filteredDay6Tasks, new TaskSortedByDefault()); + filteredDay7Tasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredDay7Tasks = new SortedList<>(filteredDay7Tasks, new TaskSortedByDefault()); + + filteredUpcomingTasks = new FilteredList<>(savvyTasker.getTasks()); + sortedAndFilteredUpcomingTasks = new SortedList<>(filteredUpcomingTasks, new TaskSortedByDefault()); + + updateFilteredListToShowActive(); // shows only active tasks on start + } +``` +###### \java\seedu\savvytasker\model\ModelManager.java +``` java + /** + * Qualifier for checking if {@link Task} is an overdue task + * + * A overdue task is a deadline or event task with end dateTime after current dateTime + * + * @return true if the task is overdue + * + */ + private class TaskIsOverdueQualifier implements Qualifier { + + @Override + public boolean run(ReadOnlyTask task) { + + Date today = new Date(); + + boolean isOverdue = false; + + if (task.getEndDateTime() != null) { + + Date endDateTime = task.getEndDateTime(); + + if (endDateTime.compareTo(today)<0 && task.isArchived() == false) { + + isOverdue = true; + } + + } + + return isOverdue; + + } + + @Override + public String toString() { + return "isOverdue=true"; + } + } + + /** + * Qualifier for checking if {@link Task} is a floating task + * + * A floating task do not have start or end time + * + * @return true if the task falls on the date specified. else return false + * + */ + private class TaskIsFloatingQualifier implements Qualifier { + + @Override + public boolean run(ReadOnlyTask task) { + + boolean isFloating = false; + + if(task.getStartDateTime() == null && task.getEndDateTime() == null && task.isArchived() == false) { + + isFloating = true; + + } + + return isFloating; + + } + + @Override + public String toString() { + return "isFloating=true"; + } + } + + /** + * Qualifier for checking if {@link Task} falls on the selected date + * + * Check whether the task is on the date specified (for deadline tasks) + * Check whether the date specified is within the range of date the task (for event tasks) + * Includes task that are completed. + * + * @return true if the task falls on the date specified. else return false + * + */ + private class TaskIsOnDateQualifier implements Qualifier { + + @Override + public boolean run(ReadOnlyTask task) { + + Date expectedDate = onDate; + + boolean isOnDate = false; + + //Archived Task + if(task.isArchived() == true){ + + isOnDate = false; + + } + //Deadline Task + else if(task.getStartDateTime() == null && task.getEndDateTime() != null) { + + Date endDateTime = task.getEndDateTime(); + + if (DateUtils.isSameDay(endDateTime, expectedDate)) { + + isOnDate = true; + + } + + } + //Event Task + else if(task.getStartDateTime() != null && task.getEndDateTime() != null) { + + Date startDateTime = task.getStartDateTime(); + Date endDateTime = task.getEndDateTime(); + + if (DateUtils.isSameDay(startDateTime, expectedDate)) { + + isOnDate = true; + + } else if (DateUtils.isSameDay(endDateTime, expectedDate)) { + + isOnDate = true; + + } else if (startDateTime.compareTo(expectedDate)<0 && expectedDate.compareTo(endDateTime)<0) { + + isOnDate = true; + + } + } + + return isOnDate; + } + + @Override + public String toString() { + return "isOnDate=true"; + } + } + + /** + * Qualifier for checking if {@link Task} task is upcoming + * + * A upcoming task is a task that will happen after the last day, 2359 of selected week + * + * @return true if the task is a upcoming task + * + */ + private class TaskIsUpcomingQualifier implements Qualifier { + + @Override + public boolean run(ReadOnlyTask task) { + + Date lastDateOfExpectedWeek = firstDayOfSelectedWeek; + + //convert date object to calendar object and add 7 days, last day of the selected week + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(lastDateOfExpectedWeek); + calendarExpectedDate.add(Calendar.DAY_OF_MONTH, 7); + calendarExpectedDate.set(Calendar.HOUR_OF_DAY,23); + calendarExpectedDate.set(Calendar.MINUTE,59); + calendarExpectedDate.set(Calendar.SECOND,59); + + //convert calendar object back to date object + lastDateOfExpectedWeek = calendarExpectedDate.getTime(); + + boolean isUpcoming = true; + + //Archived Task + if(task.isArchived() == true){ + + isUpcoming = false; + + } + + //Floating Task + else if(task.getStartDateTime() == null && task.getEndDateTime() == null) { + + isUpcoming = false; + + } + //Deadline Task + else if(task.getStartDateTime() == null && task.getEndDateTime() != null) { + + + if (task.getEndDateTime().compareTo(lastDateOfExpectedWeek)<0) { + + isUpcoming = false; + + } + + } + //Event Task + else { + + if (task.getStartDateTime().compareTo(lastDateOfExpectedWeek)<0) { + + isUpcoming = false; + + } + + } + + return isUpcoming; + } + + @Override + public String toString() { + return "isUpcoming=true"; + } +``` +###### \java\seedu\savvytasker\model\task\ReadOnlyTask.java +``` java + static final String EMPTY_FIELD = " "; + + static String DATE_PATTERN = "dd MMM yy"; + static String TIME_PATTERN = "hh:mm a"; + + // String format for deadline tasks dateTime format + static String DEADLINE_FORMAT = "by %1$s, %2$s"; + + // String format for event tasks dateTime format + static String EVENT_DIFF_START_END_DATE_FORMAT = "%1$s, %2$s to %3$s, %4$s"; + static String EVENT_SAME_START_END_DATE_FORMAT = "%1$s, %2$s to %3$s"; + + static Date lastDayOfSelectedWeek = new Date(); + + /** + * Generates the DateTime Format for all tasks with time. + * + * @param task the task to have its DateTime Format generated + * + * @return DateTime Format (e.g. (31 Oct 16, 10:00PM) + **/ + default String generateDateTime(Date start, Date end) { + String dateTimeFormat; + //Floating Task + if(start == null && end == null) { + dateTimeFormat = EMPTY_FIELD; + } + //Deadline Task + else if(start == null && end != null) { + dateTimeFormat = generateDeadlineDateTime(end); + //Event Task + }else { + dateTimeFormat = generateEventDateTime(start, end); + } + + return dateTimeFormat; + + } + + /** + * Generates the dateTimeFormat for deadline tasks + * + * @param task the task to have its dateTimeFormat generated + * + * @return dateTimeFormat (e.g. 30 Oct 16, 10:00PM) + */ + default String generateDeadlineDateTime(Date end) { + + String dateTimeFormat; + + SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + SimpleDateFormat timeFormatter = new SimpleDateFormat(TIME_PATTERN); + + String taskEndDateFormat = dateFormatter.format(end.getTime()); + String taskEndTimeFormat = timeFormatter.format(end.getTime()); + + dateTimeFormat = String.format(DEADLINE_FORMAT, taskEndDateFormat, taskEndTimeFormat); + + return dateTimeFormat; + + } + + /** + * Generates the dateTimeFormat for ranged tasks + * + * @param task the task to have its dateTimeFormat generated + * + * @return dateTimeFormat (e.g. 30 Oct 16, 8:00AM to 9:00PM) + */ + default String generateEventDateTime(Date start, Date end) { + + String dateTimeFormat; + + SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + SimpleDateFormat timeFormatter = new SimpleDateFormat(TIME_PATTERN); + + String taskStartDateFormat = dateFormatter.format(start.getTime()); + String taskStartTimeFormat = timeFormatter.format(start.getTime()); + + String taskEndDateFormat = dateFormatter.format(end.getTime()); + String taskEndTimeFormat = timeFormatter.format(end.getTime()); + + if(DateUtils.isSameDay(start, end) == false) { + + dateTimeFormat = String.format(EVENT_DIFF_START_END_DATE_FORMAT, taskStartDateFormat, taskStartTimeFormat, taskEndDateFormat, taskEndTimeFormat); + + } else { + + dateTimeFormat = String.format(EVENT_SAME_START_END_DATE_FORMAT, taskEndDateFormat, taskStartTimeFormat, taskEndTimeFormat); + + } + + return dateTimeFormat; + + } +} +``` +###### \java\seedu\savvytasker\storage\ConfigStorage.java +``` java +package seedu.savvytasker.storage; + +import java.io.IOException; +import java.util.Optional; + +import seedu.savvytasker.commons.core.Config; +import seedu.savvytasker.commons.exceptions.DataConversionException; + +/** + * Represents a storage for {@link seedu.savvytasker.commons.core.Config}. + */ +public interface ConfigStorage { + /** + * Returns Config data from storage. + * Returns {@code Optional.empty()} if storage file is not found. + * @throws DataConversionException if the data in storage is not in the expected format. + * @throws IOException if there was any problem when reading from the storage. + */ + Optional readConfigFile() throws DataConversionException, IOException; + + /** + * Saves the given {@link seedu.savvytasker.commons.core.Config} to the storage. + * @param config cannot be null. + * @throws IOException if there was any problem writing to the file. + */ + void saveConfigFile(Config config) throws IOException; + +} +``` +###### \java\seedu\savvytasker\storage\JsonConfigStorage.java +``` java + +package seedu.savvytasker.storage; + +import java.io.File; +import java.io.IOException; +import java.util.Optional; +import java.util.logging.Logger; + +import seedu.savvytasker.commons.core.Config; +import seedu.savvytasker.commons.core.LogsCenter; +import seedu.savvytasker.commons.exceptions.DataConversionException; +import seedu.savvytasker.commons.util.FileUtil; + +/** + * A class to access Config stored in the hard disk as a json file + */ +public class JsonConfigStorage implements ConfigStorage { + + private static final Logger logger = LogsCenter.getLogger(JsonConfigStorage.class); + + private String filePath; + + public JsonConfigStorage(String filePath) { + + this.filePath = filePath; + } + + @Override + public Optional readConfigFile() throws DataConversionException, IOException { + + return readConfig(filePath); + } + + @Override + public void saveConfigFile(Config config) throws IOException { + + saveConfig(config, filePath); + } + + /** + * Similar to {@link #readConfigFile()} + * @param configFilePath location of the data. Cannot be null. + * @throws DataConversionException if the file format is not as expected. + */ + public Optional readConfig(String configFilePath) throws DataConversionException { + + assert configFilePath != null; + + File configFile = new File(configFilePath); + + if (!configFile.exists()) { + + logger.info("Config file " + configFile + " not found"); + + return Optional.empty(); + + } + + Config config; + + try { + + config = FileUtil.deserializeObjectFromJsonFile(configFile, Config.class); + + } catch (IOException e) { + + logger.warning("Error reading from config file " + configFile + ": " + e); + + throw new DataConversionException(e); + + } + + return Optional.of(config); + } + + /** + * Similar to {@link #saveConfigFile(Config)} + * @param configFilePath location of the data. Cannot be null. + */ + private void saveConfig(Config config, String configFilePath) throws IOException { + + assert config != null; + assert configFilePath != null; + assert !configFilePath.isEmpty(); + + FileUtil.serializeObjectToJsonFile(new File(configFilePath), config); + } + +} +``` +###### \java\seedu\savvytasker\storage\StorageManager.java +``` java + @Override + @Subscribe + public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { + logger.info(LogsCenter.getEventHandlingLogMessage(dslce, "Local storage location changed.")); + try { + saveSavvyTasker(dslce.data); + } catch (IOException e) { + raise(new DataSavingExceptionEvent(e)); + } + } +``` +###### \java\seedu\savvytasker\ui\CommandBox.java +``` java +package seedu.savvytasker.ui; + +import java.io.File; +import java.util.Calendar; +import java.util.Date; +import java.util.Stack; +import java.util.logging.Logger; + +import com.google.common.eventbus.Subscribe; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.control.SplitPane; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyCodeCombination; +import javafx.scene.input.KeyCombination; +import javafx.scene.input.KeyEvent; +import javafx.scene.layout.AnchorPane; +import javafx.stage.DirectoryChooser; +import javafx.stage.Stage; +import seedu.savvytasker.commons.core.LogsCenter; +import seedu.savvytasker.commons.events.model.SavvyTaskerChangedEvent; +import seedu.savvytasker.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.savvytasker.commons.events.ui.ShowCheatsheetEvent; +import seedu.savvytasker.commons.events.ui.WeekSelectionChangedEvent; +import seedu.savvytasker.commons.util.FxViewUtil; +import seedu.savvytasker.logic.Logic; +import seedu.savvytasker.logic.commands.CommandResult; + +public class CommandBox extends UiPart { + private final Logger logger = LogsCenter.getLogger(CommandBox.class); + private static final String FXML = "CommandBox.fxml"; + + private AnchorPane placeHolderPane; + private AnchorPane commandPane; + private ResultDisplay resultDisplay; + String previousCommandTest; + private Date date = new Date(); + private static int DAYS_OF_WEEK = 7; + + private final String UNDO_COMMAND = "undo"; + private final String REDO_COMMAND = "redo"; + private final String HELP_COMMAND = "help"; + private final String EXIT_COMMAND = "exit"; + private final String LIST_COMMAND = "list"; + private final String LIST_ARCHIVED_COMMAND = "list archived"; + private final String LIST_PRIORITY_COMMAND = "list priorityLevel"; + private final String LIST_ALIAS_COMMAND = "list alias"; + private final String CLEAR_COMMAND = "clear"; + private final String STORAGE_COMMAND = "storage ."; + + KeyCombination saveKey = new KeyCodeCombination(KeyCode.S, KeyCombination.META_DOWN); + KeyCombination undoKey = new KeyCodeCombination(KeyCode.Z, KeyCombination.META_DOWN); + KeyCombination redoKey = new KeyCodeCombination(KeyCode.Y, KeyCombination.META_DOWN); + KeyCombination helpKey = new KeyCodeCombination(KeyCode.H, KeyCombination.META_DOWN); + KeyCombination exitKey = new KeyCodeCombination(KeyCode.Q, KeyCombination.META_DOWN); + KeyCombination listKey = new KeyCodeCombination(KeyCode.L, KeyCombination.META_DOWN); + KeyCombination listArchivedKey = new KeyCodeCombination(KeyCode.A, KeyCombination.META_DOWN); + KeyCombination listPriorityKey = new KeyCodeCombination(KeyCode.P, KeyCombination.META_DOWN); + KeyCombination listAliasKey = new KeyCodeCombination(KeyCode.I, KeyCombination.META_DOWN); + KeyCombination clearKey = new KeyCodeCombination(KeyCode.D, KeyCombination.META_DOWN); + KeyCombination leftKey = new KeyCodeCombination(KeyCode.LEFT, KeyCombination.META_DOWN); + KeyCombination rightKey = new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.META_DOWN); + + // stack to store commands history + private static Stack COMMAND_HISTORY_STACK = new Stack(); + private static Stack COMMAND_FUTURE_STACK = new Stack(); + + private Logic logic; + + @FXML + private TextField commandTextField; + private CommandResult mostRecentResult; + + public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, + ResultDisplay resultDisplay, Logic logic) { + CommandBox commandBox = UiPartLoader.loadUiPart(primaryStage, commandBoxPlaceholder, new CommandBox()); + commandBox.configure(resultDisplay, logic); + commandBox.addToPlaceholder(); + return commandBox; + } + + public void configure(ResultDisplay resultDisplay, Logic logic) { + this.resultDisplay = resultDisplay; + this.logic = logic; + registerAsAnEventHandler(this); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(commandTextField); + FxViewUtil.applyAnchorBoundaryParameters(commandPane, 0.0, 0.0, 0.0, 0.0); + FxViewUtil.applyAnchorBoundaryParameters(commandTextField, 0.0, 0.0, 0.0, 0.0); + } + + @Override + public void setNode(Node node) { + commandPane = (AnchorPane) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + @Override + public void setPlaceholder(AnchorPane pane) { + this.placeHolderPane = pane; + } + + + @FXML + private void handleCommandInputChanged() { + //Take a copy of the command text + previousCommandTest = commandTextField.getText(); + + COMMAND_HISTORY_STACK.push(previousCommandTest); + /* We assume the command is correct. If it is incorrect, the command box will be changed accordingly + * in the event handling code {@link #handleIncorrectCommandAttempted} + */ + setStyleToIndicateCorrectCommand(); + mostRecentResult = logic.execute(previousCommandTest); + resultDisplay.postMessage(mostRecentResult.feedbackToUser); + logger.info("Result: " + mostRecentResult.feedbackToUser); + } + + + /** + * Sets the command box style to indicate a correct command. + */ + private void setStyleToIndicateCorrectCommand() { + commandTextField.getStyleClass().remove("error"); + commandTextField.setText(""); + } + + @Subscribe + private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event){ + logger.info(LogsCenter.getEventHandlingLogMessage(event,"Invalid command: " + previousCommandTest)); + setStyleToIndicateIncorrectCommand(); + restoreCommandText(); + } + + /** + * Restores the command box text to the previously entered command + */ + private void restoreCommandText() { + commandTextField.setText(""); + } + + /** + * Sets the command box style to indicate an error + */ + private void setStyleToIndicateIncorrectCommand() { + commandTextField.getStyleClass().add("error"); + } + + public Node getCommandTextField() { + return commandTextField; + } + + //==================== Keyboard Shortcuts Code ================================================================= + /** + * Key pressed handler for text box. + * + * @param keyEvent key event for the button that is being pressed. + */ + public void commandTextFieldOnKeyPressedHandler(KeyEvent keyEvent) { + + String userInput = commandTextField.getText().trim(); + + try { + + KeyCode keyCode = keyEvent.getCode(); + + if(saveKey.match(keyEvent)) { + + processSave(); + + }else if (keyCode == KeyCode.ESCAPE) { + + showCheatsheet(); + + } else if (keyCode == KeyCode.UP) { + + processUp(userInput); + + } else if (keyCode == KeyCode.DOWN) { + + processDown(userInput); + + } else if (leftKey.match(keyEvent)) { + + processDate(-1); + + } else if (rightKey.match(keyEvent)) { + + processDate(1); + + } else if (undoKey.match(keyEvent)) { + + executeCommand(UNDO_COMMAND); + + } else if (redoKey.match(keyEvent)) { + + executeCommand(REDO_COMMAND); + + } else if (helpKey.match(keyEvent)) { + + executeCommand(HELP_COMMAND); + + } else if (exitKey.match(keyEvent)) { + + executeCommand(EXIT_COMMAND); + + } else if (listKey.match(keyEvent)) { + + executeCommand(LIST_COMMAND); + + } else if (listArchivedKey.match(keyEvent)) { + + executeCommand(LIST_ARCHIVED_COMMAND); + + } else if (listPriorityKey.match(keyEvent)) { + + executeCommand(LIST_PRIORITY_COMMAND); + + } else if (listAliasKey.match(keyEvent)) { + + executeCommand(LIST_ALIAS_COMMAND); + + } else if (clearKey.match(keyEvent)) { + + executeCommand(CLEAR_COMMAND); + + } + + } catch (IllegalArgumentException e) { + + commandTextField.setText(""); + + COMMAND_HISTORY_STACK.add(userInput); + + this.logger.info("Illegal Argument has been entered."); + + } catch (Exception e) { + + e.printStackTrace(); + commandTextField.setText(""); + + this.logger.info("Illegal Argument has been entered."); + + } + + } + + /** + * Process the event that occurs after the user presses the ctrl-S button. + * + * @param userInput the command keyed in by the user. + */ + public void processSave() { + + //Execute the save command + DirectoryChooser directoryChooser = new DirectoryChooser(); + File selectedFile = directoryChooser.showDialog(primaryStage); + String filepath = selectedFile.getAbsolutePath(); + executeCommand(STORAGE_COMMAND + filepath + "/savvytasker.xml"); + + } + + /** + * Process the event that occurs after the user presses the [UP] button. + * + * @param userInput the command keyed in by the user. + */ + public void processUp(String userInput) { + + if (!COMMAND_HISTORY_STACK.isEmpty()) { + + String previousCommand = COMMAND_HISTORY_STACK.pop(); + + if (!userInput.equals("")) { + + COMMAND_FUTURE_STACK.push(userInput); + + } + + commandTextField.setText(previousCommand); + + } + + commandTextField.positionCaret(commandTextField.getText().length()); + + } + + /** + * Process the event that occurs after the user presses the down button. + * + * @param userInput the command keyed in by the user. + */ + public void processDown(String userInput) { + + if (!COMMAND_FUTURE_STACK.isEmpty()) { + + String nextCommand = COMMAND_FUTURE_STACK.pop(); + + COMMAND_HISTORY_STACK.push(userInput); + commandTextField.setText(nextCommand); + + } else if (!userInput.equals("")) { + + COMMAND_HISTORY_STACK.push(userInput); + commandTextField.setText(""); + + } + + commandTextField.positionCaret(commandTextField.getText().length()); + + } + + /** + * Process the event that occurs after the user presses the left or right button. + * + * @param numbers of week to be added to the current selected week to be displayed in the daily task list view + */ + public void processDate(int numberOfWeek) { + + date = addWeek(numberOfWeek, date); + indicateWeekSelectionChanged(); + } + + /** Raises an event to indicate the week to be displayed has changed */ + private void indicateWeekSelectionChanged() { + raise(new WeekSelectionChangedEvent()); + } + + /** Raises an event to indicate the week to be displayed has changed */ + private void showCheatsheet() { + raise(new ShowCheatsheetEvent()); + } + + /** + * Execute commands + * + * @param command to be executed + */ + public void executeCommand(String commandInput) { + CommandResult commandResult = logic.execute(commandInput); + resultDisplay.postMessage(commandResult.feedbackToUser); + logger.info("Result: " + commandResult.feedbackToUser); + } + + private Date addWeek(int numberOfWeek, Date date) { + + //convert date object to calendar object and add 1 days + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(date); + + calendarExpectedDate.add(Calendar.DATE, (numberOfWeek*DAYS_OF_WEEK)); + + //convert calendar object back to date object + date = calendarExpectedDate.getTime(); + + return date; + } + + public Date getDate() { + return date; + } +} + +``` +###### \java\seedu\savvytasker\ui\DailyPanel.java +``` java + +/** + * Panel containing the list overdue task. + * @author A0138431L + * + */ +public class DailyPanel extends UiPart { + + private static String TODAY_TITLE = "Today"; + private static String TOMORROW_TITLE = "Tomorrow"; + private static String DAY_PATTERN = "EEEE"; + private static String DATE_PATTERN = "dd MMM yy"; + private static String DAY_DATE_FORMAT = "%1$s, %2$s"; + + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + private static final String FXML = "DailyList.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + + @FXML + private TextField header; + + @FXML + private ListView taskListView; + + public DailyPanel() { + super(); + } + + @Override + public void setNode(Node node) { + panel = (VBox) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + @Override + public void setPlaceholder(AnchorPane pane) { + this.placeHolderPane = pane; + } + + public static DailyPanel load(Stage primaryStage, AnchorPane DailyListPlaceholder, + ObservableList taskList, int dayOfTheWeek, Date date) { + DailyPanel dailyPanel = + UiPartLoader.loadUiPart(primaryStage, DailyListPlaceholder, new DailyPanel()); + dailyPanel.configure(taskList, dayOfTheWeek, date); + return dailyPanel; + } + + private void configure(ObservableList taskList, int dayOfTheWeek, Date date) { + + String dateHeader = generateHeader(dayOfTheWeek, date); + Date today = new Date(); + if(date == today) { + placeHolderPane.setStyle("-fx-background-color:#FF0000"); + header.setStyle("-fx-text-fill:#FF0000"); + } + setConnections(taskList, dateHeader); + addToPlaceholder(); + + } + + private void setConnections(ObservableList taskList, String dateHeader) { + header.clear(); + header.setText(dateHeader); + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in daily task list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + private String generateHeader(int dayOfTheWeek, Date date) { + + SimpleDateFormat dayFormatter = new SimpleDateFormat(DAY_PATTERN); + SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + + Date today = new Date(); + Date tomorrow = new Date(); + tomorrow = addDay(1, tomorrow); + + String day; + + if(DateUtils.isSameDay(date, today)) { + + day = TODAY_TITLE; + + } else if (DateUtils.isSameDay(date, tomorrow)) { + + day = TOMORROW_TITLE; + + } else { + + day = dayFormatter.format(date); + + } + String header = String.format(DAY_DATE_FORMAT, day, dateFormatter.format(date)); + + return header; + } + + private Date addDay(int i, Date date) { + + //convert date object to calendar object and add 1 days + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(date); + + calendarExpectedDate.add(Calendar.DATE, i); + + //convert calendar object back to date object + date = calendarExpectedDate.getTime(); + + return date; + } + + class TaskListViewCell extends ListCell { + + public TaskListViewCell() { + } + + @Override + protected void updateItem(ReadOnlyTask task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(TaskCard.load(task, 0, false).getLayout()); + } + } + } + +} +``` +###### \java\seedu\savvytasker\ui\FloatingPanel.java +``` java + +/** + * Panel containing the list floating task. + * @author A0138431L + * + */ +public class FloatingPanel extends UiPart { + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + private static final String FXML = "FloatingList.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + @FXML + private ListView taskListView; + + public FloatingPanel() { + super(); + } + + @Override + public void setNode(Node node) { + panel = (VBox) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + @Override + public void setPlaceholder(AnchorPane pane) { + this.placeHolderPane = pane; + } + + public static FloatingPanel load(Stage primaryStage, AnchorPane floatingListPlaceholder, + ObservableList taskList) { + FloatingPanel floatingPanel = + UiPartLoader.loadUiPart(primaryStage, floatingListPlaceholder, new FloatingPanel()); + floatingPanel.configure(taskList); + return floatingPanel; + } + + private void configure(ObservableList taskList) { + setConnections(taskList); + addToPlaceholder(); + } + + private void setConnections(ObservableList taskList) { + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in floating task list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + class TaskListViewCell extends ListCell { + + public TaskListViewCell() { + } + + @Override + protected void updateItem(ReadOnlyTask task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(TaskCard.load(task, 0, false).getLayout()); + } + } + } + +} +``` +###### \java\seedu\savvytasker\ui\MainWindow.java +``` java + +package seedu.savvytasker.ui; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; + +import com.google.common.eventbus.Subscribe; + +import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.Scene; +import javafx.scene.control.MenuItem; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCombination; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +import seedu.savvytasker.commons.core.Config; +import seedu.savvytasker.commons.core.GuiSettings; +import seedu.savvytasker.commons.core.LogsCenter; +import seedu.savvytasker.commons.events.model.SavvyTaskerChangedEvent; +import seedu.savvytasker.commons.events.ui.ExitAppRequestEvent; +import seedu.savvytasker.commons.events.ui.ShowCheatsheetEvent; +import seedu.savvytasker.commons.events.ui.WeekSelectionChangedEvent; +import seedu.savvytasker.logic.Logic; +import seedu.savvytasker.model.UserPrefs; +import seedu.savvytasker.model.task.ReadOnlyTask; + +/** + * The Main Window. Provides the basic application layout containing + * a sorting and filtered list that display the result of the user command + * on the left and a week's view of the task + * + * The week's view contains 4 lists, namely the floating list, + * days of the week list and upcoming list + * + * Floating list contains task without start and end dateTime + * Days of the week list contains task that falls on the respective day of the selected week + * Upcoming list contains task with start date after the last day of selected week + * + * @author A0138431L + * + */ +public class MainWindow extends UiPart { + + private static final String ICON = "/images/savvytasker-icon.png"; + private static final Image image = new Image(MainWindow.class.getResourceAsStream(ICON)); + private static final String CHEATSHEET = "/images/cheatsheet.png"; + private static final Image imageOverlay = new Image(MainWindow.class.getResourceAsStream(CHEATSHEET)); + private static final String FXML = "MainWindow.fxml"; + public static final int MIN_HEIGHT = 700; + public static final int MIN_WIDTH = 1150; + + private Logic logic; + Date firstDayOfSelectedWeek = new Date(); + private static int DAYS_OF_WEEK = 7; + private boolean isShown = false; + + // Independent Ui parts residing in this Ui container + //private BrowserPanel browserPanel; + private TaskListPanel taskListPanel; + private AliasSymbolListPanel aliasSymbolListPanel; + private ResultDisplay resultDisplay; + private StatusBarFooter statusBarFooter; + private CommandBox commandBox; + private Config config; + private UserPrefs userPrefs; + @FXML + private FloatingPanel floatingPanel; + @FXML + private DailyPanel dailyPanel; + @FXML + private UpcomingPanel upcomingPanel; + + // Handles to elements of this Ui container + private VBox rootLayout; + private Scene scene; + + private String addressBookName; + + @FXML + private AnchorPane browserPlaceholder; + + @FXML + private AnchorPane commandBoxPlaceholder; + + @FXML + private ImageView imageIcon; + + @FXML + private ImageView cheatsheet; + + @FXML + private AnchorPane taskListPanelPlaceholder; + + @FXML + private AnchorPane aliasSymbolListPanelPlaceholder; + + @FXML + private AnchorPane resultDisplayPlaceholder; + + @FXML + private AnchorPane statusbarPlaceholder; + + @FXML + private VBox listPanel; + + @FXML + private AnchorPane floatingPanelPlaceholder; + + @FXML + private AnchorPane day1PanelPlaceholder; + @FXML + private AnchorPane day2PanelPlaceholder; + @FXML + private AnchorPane day3PanelPlaceholder; + @FXML + private AnchorPane day4PanelPlaceholder; + @FXML + private AnchorPane day5PanelPlaceholder; + @FXML + private AnchorPane day6PanelPlaceholder; + @FXML + private AnchorPane day7PanelPlaceholder; + + @FXML + private AnchorPane upcomingPanelPlaceholder; + + public MainWindow() { + super(); + } + + @Override + public void setNode(Node node) { + rootLayout = (VBox) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + public static MainWindow load(Stage primaryStage, Config config, UserPrefs prefs, Logic logic) { + + MainWindow mainWindow = UiPartLoader.loadUiPart(primaryStage, new MainWindow()); + mainWindow.configure(config.getAppTitle(), config.getSavvyTaskerListName(), config, prefs, logic); + return mainWindow; + } + + private void configure(String appTitle, String addressBookName, Config config, UserPrefs prefs, + Logic logic) { + + //Set dependencies + this.logic = logic; + this.addressBookName = addressBookName; + this.config = config; + this.userPrefs = prefs; + registerAsAnEventHandler(this); + + //Configure the UI + setTitle(appTitle); + setIcon(ICON); + setWindowMinSize(); + setWindowDefaultSize(prefs); + scene = new Scene(rootLayout); + primaryStage.setScene(scene); + + } + + void fillInnerParts() { + imageIcon.setImage(image); + taskListPanel = TaskListPanel.load(primaryStage, getTaskListPlaceholder(), logic.getFilteredTaskList()); + aliasSymbolListPanel = AliasSymbolListPanel.load(primaryStage, getAliasSymbolPlaceholder(), logic.getAliasSymbolList()); + setDefaultView(); + resultDisplay = ResultDisplay.load(primaryStage, getResultDisplayPlaceholder()); + statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getSavvyTaskerFilePath()); + commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), resultDisplay, logic); + commandBox.getCommandTextField().requestFocus(); + floatingPanel = FloatingPanel.load(primaryStage, getFloatingPanelPlaceholder(), logic.getFilteredFloatingTasks()); + loadDailyPanel(); + upcomingPanel = UpcomingPanel.load(primaryStage, getUpcomingPanelPlaceholder(), logic.getFilteredUpcomingTasks(firstDayOfSelectedWeek)); + cheatsheet.setImage(imageOverlay); + } + + private void loadDailyPanel() { + firstDayOfSelectedWeek = commandBox.getDate(); + for (int i = 0; i < DAYS_OF_WEEK; i++) { + Date onDate = new Date(); + onDate.setTime(firstDayOfSelectedWeek.getTime()); + onDate = addDay(i, onDate); + dailyPanel = DailyPanel.load(primaryStage, getDailyPanelPlaceholder(i), + logic.getFilteredDailyTasks(i, onDate), i, onDate); + } + } + + /** + * Removes all the children in the taskPanel VBox + * Shows the default list, which is the list of tasks + */ + private void setDefaultView() { + getListPanel().getChildren().remove(getAliasSymbolPlaceholder()); + getListPanel().getChildren().remove(getTaskListPlaceholder()); + getListPanel().getChildren().add(getTaskListPlaceholder()); + } + + /** + * Set to true to show the list of tasks. Set to false to show the list of alias + * @param isShown + */ + public void showTaskList(boolean isShown) { + getListPanel().getChildren().remove(getAliasSymbolPlaceholder()); + getListPanel().getChildren().remove(getTaskListPlaceholder()); + if (isShown) { + getListPanel().getChildren().add(getTaskListPlaceholder()); + } else { + getListPanel().getChildren().add(getAliasSymbolPlaceholder()); + } + } + + private VBox getListPanel() { + return listPanel; + } + + private VBox getRootLayout() { + return rootLayout; + } + + private AnchorPane getCommandBoxPlaceholder() { + return commandBoxPlaceholder; + } + + private AnchorPane getStatusbarPlaceholder() { + return statusbarPlaceholder; + } + + private AnchorPane getResultDisplayPlaceholder() { + return resultDisplayPlaceholder; + } + + public AnchorPane getTaskListPlaceholder() { + return taskListPanelPlaceholder; + } + + public AnchorPane getAliasSymbolPlaceholder() { + return aliasSymbolListPanelPlaceholder; + } + + private AnchorPane getFloatingPanelPlaceholder() { + return floatingPanelPlaceholder; + } + + private AnchorPane getDailyPanelPlaceholder(int index) { + + switch(index) { + + case 0: + + return day1PanelPlaceholder; + + case 1: + + return day2PanelPlaceholder; + + case 2: + + return day3PanelPlaceholder; + + case 3: + + return day4PanelPlaceholder; + + case 4: + + return day5PanelPlaceholder; + + case 5: + + return day6PanelPlaceholder; + + case 6: + default: + + return day7PanelPlaceholder; + + } + + } + + private AnchorPane getUpcomingPanelPlaceholder() { + return upcomingPanelPlaceholder; + } + + private Date addDay(int i, Date date) { + + //convert date object to calendar object and add 1 days + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(date); + + calendarExpectedDate.add(Calendar.DATE, i); + + //convert calendar object back to date object + date = calendarExpectedDate.getTime(); + + return date; + } + + public void hide() { + primaryStage.hide(); + } + + private void setTitle(String appTitle) { + primaryStage.setTitle(appTitle); + } + + /** + * Sets the default size based on user preferences. + */ + protected void setWindowDefaultSize(UserPrefs prefs) { + primaryStage.setHeight(prefs.getGuiSettings().getWindowHeight()); + primaryStage.setWidth(prefs.getGuiSettings().getWindowWidth()); + if (prefs.getGuiSettings().getWindowCoordinates() != null) { + primaryStage.setX(prefs.getGuiSettings().getWindowCoordinates().getX()); + primaryStage.setY(prefs.getGuiSettings().getWindowCoordinates().getY()); + } + } + + private void setWindowMinSize() { + primaryStage.setMinHeight(MIN_HEIGHT); + primaryStage.setMinWidth(MIN_WIDTH); + } + + /** + * Returns the current size and the position of the main Window. + */ + public GuiSettings getCurrentGuiSetting() { + return new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(), + (int) primaryStage.getX(), (int) primaryStage.getY()); + } + + @FXML + public void handleHelp() { + HelpWindow helpWindow = HelpWindow.load(primaryStage); + helpWindow.show(); + } + + public void hideHelp() { + HelpWindow helpWindow = HelpWindow.load(primaryStage); + helpWindow.hide(); + } + + public void show() { + primaryStage.show(); + } + + /** + * Closes the application. + */ + @FXML + private void handleExit() { + raise(new ExitAppRequestEvent()); + } + + public AliasSymbolListPanel getAliasSymbolListPanel() { + return this.aliasSymbolListPanel; + } + + public TaskListPanel getTaskListPanel() { + return this.taskListPanel; + } + + public void loadPersonPage(ReadOnlyTask task) { + //feature removed + //browserPanel.loadPersonPage(task); + } + + public void releaseResources() { + //feature removed + //browserPanel.freeResources(); + } + + @Subscribe + public void handleSavvyTaskerChangedEvent(SavvyTaskerChangedEvent stce) { + loadDailyPanel(); + } + + @Subscribe + public void handleWeekSelectionChangedEvent(WeekSelectionChangedEvent stce) { + loadDailyPanel(); + } + + @Subscribe + public void handleCheatsheetDisplayToggledEvent(ShowCheatsheetEvent stce) { + + if(isShown == false) { + cheatsheet.setVisible(true); + isShown = true; + } else { + cheatsheet.setVisible(false); + isShown = false; + } + } + +} +``` +###### \java\seedu\savvytasker\ui\StatusBarFooter.java +``` java + @Subscribe + public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { + setSaveLocation(dslce.newPath); + } +``` +###### \java\seedu\savvytasker\ui\TaskCard.java +``` java + +package seedu.savvytasker.ui; + +import java.util.Date; + +import javafx.fxml.FXML; +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 seedu.savvytasker.model.task.ReadOnlyTask; + +public class TaskCard extends UiPart{ + + private static final String FXML = "TaskListCard.fxml"; + + private static final String ICON = "/images/overdue.png"; + private static final Image OVERDUE_IMAGE = new Image(MainWindow.class.getResourceAsStream(ICON)); + + public static final String LOW_PRIORITY_BACKGROUND = "-fx-background-color:#CEFFDC"; + public static final String MEDIUM_PRIORITY_BACKGROUND = "-fx-background-color:#FFFED8"; + public static final String HIGH_PRIORITY_BACKGROUND = "-fx-background-color:#FF8180"; + + @FXML + private HBox cardPane; + @FXML + private Label taskName; + @FXML + private Label id; + @FXML + private Label details; + @FXML + private ImageView overdueIcon; + + private boolean isShowingIndex; + private ReadOnlyTask task; + private int displayedIndex; + + public TaskCard(boolean isShowingIndex){ + this.isShowingIndex = isShowingIndex; + } + + public static TaskCard load(ReadOnlyTask task, int displayedIndex, boolean isShowingIndex){ + TaskCard card = new TaskCard(isShowingIndex); + card.task = task; + card.displayedIndex = displayedIndex; + return UiPartLoader.loadUiPart(card); + } + + @FXML + public void initialize() { + + taskName.setText(task.getTaskName()); + if (isShowingIndex) { + id.setText(displayedIndex + ". "); + } + details.setText(task.getTextForUi()); + setCardBackground(); + setOverdue(); + + } + + public HBox getLayout() { + return cardPane; + } + + private void setOverdue() { + + Date today = new Date(); + + if (task.getEndDateTime() != null) { + + Date endDateTime = task.getEndDateTime(); + + if (endDateTime.compareTo(today)<0 && task.isArchived() == false) { + + overdueIcon.setImage(OVERDUE_IMAGE); + } + } + + } + private void setCardBackground() { + + if (task.getPriority().toString().equals("High")) { + + cardPane.setStyle(HIGH_PRIORITY_BACKGROUND); + + } else if (task.getPriority().toString().equals("Medium")) { + + cardPane.setStyle(MEDIUM_PRIORITY_BACKGROUND); + + } else if (task.getPriority().toString().equals("Low")) { + + cardPane.setStyle(LOW_PRIORITY_BACKGROUND); + + } + + } + + + @Override + public void setNode(Node node) { + cardPane = (HBox)node; + } + + @Override + public String getFxmlPath() { + return FXML; + } +} +``` +###### \java\seedu\savvytasker\ui\UpcomingPanel.java +``` java + +/** +* Panel containing the list overdue task. +* @author A0138431L +* +*/ +public class UpcomingPanel extends UiPart { + private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); + private static final String FXML = "UpcomingList.fxml"; + private VBox panel; + private AnchorPane placeHolderPane; + + @FXML + private ListView taskListView; + + public UpcomingPanel() { + super(); + } + + @Override + public void setNode(Node node) { + panel = (VBox) node; + } + + @Override + public String getFxmlPath() { + return FXML; + } + + @Override + public void setPlaceholder(AnchorPane pane) { + this.placeHolderPane = pane; + } + + public static UpcomingPanel load(Stage primaryStage, AnchorPane UpcomingListPlaceholder, + ObservableList taskList) { + UpcomingPanel upcomingPanel = + UiPartLoader.loadUiPart(primaryStage, UpcomingListPlaceholder, new UpcomingPanel()); + upcomingPanel.configure(taskList); + return upcomingPanel; + } + + private void configure(ObservableList taskList) { + setConnections(taskList); + addToPlaceholder(); + } + + private void setConnections(ObservableList taskList) { + taskListView.setItems(taskList); + taskListView.setCellFactory(listView -> new TaskListViewCell()); + setEventHandlerForSelectionChangeEvent(); + } + + private void addToPlaceholder() { + SplitPane.setResizableWithParent(placeHolderPane, false); + placeHolderPane.getChildren().add(panel); + } + + private void setEventHandlerForSelectionChangeEvent() { + taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { + if (newValue != null) { + logger.fine("Selection in upcoming task list panel changed to : '" + newValue + "'"); + raise(new TaskPanelSelectionChangedEvent(newValue)); + } + }); + } + + public void scrollTo(int index) { + Platform.runLater(() -> { + taskListView.scrollTo(index); + taskListView.getSelectionModel().clearAndSelect(index); + }); + } + + class TaskListViewCell extends ListCell { + + public TaskListViewCell() { + } + + @Override + protected void updateItem(ReadOnlyTask task, boolean empty) { + super.updateItem(task, empty); + + if (empty || task == null) { + setGraphic(null); + setText(null); + } else { + setGraphic(TaskCard.load(task, 0, false).getLayout()); + } + } + } + +} +``` +###### \resources\view\DarkTheme.css +``` css +.background { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.label { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: #555555; + -fx-opacity: 0.9; +} + +.label-bright { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.label-header { + -fx-font-size: 32pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.text-field { + -fx-font-size: 12pt; + -fx-font-family: "Segoe UI Semibold"; +} + +.tab-pane { + -fx-padding: 0 0 0 1; +} + +.tab-pane .tab-header-area { + -fx-padding: 0 0 0 0; + -fx-min-height: 0; + -fx-max-height: 0; +} + +.table-view { + -fx-base: #1d1d1d; + -fx-control-inner-background: #1d1d1d; + -fx-background-color: #1d1d1d; + -fx-table-cell-border-color: transparent; + -fx-table-header-border-color: transparent; + -fx-padding: 5; +} + +.table-view .column-header-background { + -fx-background-color: transparent; +} + +.table-view .column-header, .table-view .filler { + -fx-size: 35; + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-border-color: + transparent + transparent + derive(-fx-base, 80%) + transparent; + -fx-border-insets: 0 10 1 0; +} + +.table-view .column-header .label { + -fx-font-size: 20pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-alignment: center-left; + -fx-opacity: 1; +} + +.table-view:focused .table-row-cell:filled:focused:selected { + -fx-background-color: -fx-focus-color; +} + +.split-pane:horizontal .split-pane-divider { + -fx-border-color: transparent #1d1d1d transparent #1d1d1d; + -fx-background-color: transparent, derive(#1d1d1d, 10%); +} + +.split-pane { + -fx-border-radius: 1; + -fx-border-width: 1; + -fx-background-color: derive(#1d1d1d, 20%); +} + +.list-cell { + -fx-label-padding: 0 0 0 0; + -fx-graphic-text-gap : 0; + -fx-padding: 0 0 0 0; +} + +.list-cell .label { + -fx-text-fill: #010504; + -fx-font-family:'Helvetica Condensed'; +} + +.cell_big_label { + -fx-font-size: 12px; + -fx-text-fill: #010504; + -fx-font-family:'Helvetica Condensed'; +} + +.cell_small_label { + -fx-font-size: 10px; + -fx-text-fill: #696969; +} + +.anchor-pane { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.anchor-pane-with-border { + -fx-background-color: derive(#1d1d1d, 20%); + -fx-border-color: derive(#1d1d1d, 10%); + -fx-border-top-width: 1px; +} + +.status-bar { + -fx-background-color: derive(#1d1d1d, 20%); + -fx-text-fill: black; +} + +.result-display { + -fx-background-color: transparent; +} + +.result-display .content { + -fx-background-color: #383838; +} + +.result-display .label { + -fx-text-fill: black !important; +} + +.status-bar .label { + -fx-text-fill: white; +} + +.status-bar-with-border { + -fx-background-color: derive(#1d1d1d, 30%); + -fx-border-color: derive(#1d1d1d, 25%); + -fx-border-width: 1px; +} + +.status-bar-with-border .label { + -fx-text-fill: white; +} + +.grid-pane { + -fx-background-color: derive(#1d1d1d, 30%); + -fx-border-color: derive(#1d1d1d, 30%); + -fx-border-width: 1px; +} + +.grid-pane .anchor-pane { + -fx-background-color: derive(#1d1d1d, 30%); +} + +.context-menu { + -fx-background-color: derive(#1d1d1d, 50%); +} + +.context-menu .label { + -fx-text-fill: white; +} + +.menu-bar { + -fx-background-color: derive(#1d1d1d, 20%); +} + +.menu-bar .label { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 0.9; +} + +.menu .left-container { + -fx-background-color: black; +} + +/* + * Metro style Push Button + * Author: Pedro Duque Vieira + * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ + */ +.button { + -fx-padding: 5 22 5 22; + -fx-border-color: #e2e2e2; + -fx-border-width: 2; + -fx-background-radius: 0; + -fx-background-color: #1d1d1d; + -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-size: 11pt; + -fx-text-fill: #d8d8d8; + -fx-background-insets: 0 0 0 0, 0, 1, 2; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.button:pressed, .button:default:hover:pressed { + -fx-background-color: white; + -fx-text-fill: #1d1d1d; +} + +.button:focused { + -fx-border-color: white, white; + -fx-border-width: 1, 1; + -fx-border-style: solid, segments(1, 1); + -fx-border-radius: 0, 0; + -fx-border-insets: 1 1 1 1, 0; +} + +.button:disabled, .button:default:disabled { + -fx-opacity: 0.4; + -fx-background-color: #1d1d1d; + -fx-text-fill: white; +} + +.button:default { + -fx-background-color: -fx-focus-color; + -fx-text-fill: #ffffff; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color, 30%); +} + +.dialog-pane { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.button-bar > *.container { + -fx-background-color: #1d1d1d; +} + +.dialog-pane > *.label.content { + -fx-font-size: 14px; + -fx-font-weight: bold; + -fx-text-fill: white; +} + +.dialog-pane:header *.header-panel { + -fx-background-color: derive(#1d1d1d, 25%); +} + +.dialog-pane:header *.header-panel *.label { + -fx-font-size: 18px; + -fx-font-style: italic; + -fx-fill: white; + -fx-text-fill: white; +} + +.scroll-bar .thumb { + -fx-background-color: derive(#1d1d1d, 50%); + -fx-background-insets: 3; +} + +.scroll-bar .increment-button, .scroll-bar .decrement-button { + -fx-background-color: transparent; + -fx-padding: 0 0 0 0; +} + +.scroll-bar .increment-arrow, .scroll-bar .decrement-arrow { + -fx-shape: " "; +} + +.scroll-bar:vertical .increment-arrow, .scroll-bar:vertical .decrement-arrow { + -fx-padding: 1 8 1 8; +} + +.scroll-bar:horizontal .increment-arrow, .scroll-bar:horizontal .decrement-arrow { + -fx-padding: 8 1 8 1; +} + +.cardPane { + -fx-border-color: #ffffff; + -fx-border-radius: 5px; +} + +#commandTypeLabel { + -fx-font-size: 11px; + -fx-text-fill: #F70D1A; +} + +#filterField, #personListPanel, #personWebpage { + -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +} + +#taskListView { + -fx-background-color: transparent; +} + +#header { + fx-font-size: 11px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Condensed'; +} + +/*------------------------------------------ FloatingPanel Styling ------------------------------------------*/ + +.floating-scrollpane, .floating-panel { + -fx-background-color:#ACEDFF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; +} + +.floating-scrollpane { + -fx-border-radius: 15px; +} + +.floating-scrollpane > .scroll-bar:horizontal .thumb, +.floating-scrollpane > .scroll-bar:vertical .thumb { + -fx-background-color:#406C7F; +} + +.floating-panel .title, .floating-panel .taskname { + -fx-text-fill:#000000; +} + +.floating-panel .subtitle, .floating-panel .timestamp { + -fx-text-fill:#5997BS2; +} + +/*------------------------------------------ DailyPanel Styling ------------------------------------------*/ + +.daily-scrollpane, .daily-panel { + -fx-background-color:#99D3FF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; +} + +.daily-scrollpane > .scroll-bar:horizontal .thumb, +.daily-scrollpane > .scroll-bar:vertical .thumb { + -fx-background-color:#7E7E45; +} + +.daily-panel .title, .daily-panel .taskname { + -fx-text-fill:#5D5D33; +} + +.daily-panel .subtitle, .daily-panel .timestamp { + -fx-text-fill:#B2B262; +} + +/*------------------------------------------ UpcomingPanel Styling ------------------------------------------*/ +.upcoming-scrollpane, .upcoming-panel { + -fx-background-color:#D1E0FF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; +} + +.upcoming-scrollpane > .scroll-bar:horizontal .thumb, +.upcoming-scrollpane > .scroll-bar:vertical .thumb { + -fx-background-color:#657E47; +} + +.upcoming-panel .check-box > .box { + -fx-border-color:#657E47; +} + +.upcoming-panel .check-box { + -fx-font-family:'Helvetica'; + -fx-text-fill:#485A33; + -fx-font-size:10; + -fx-font-weight:bold; +} + +.upcoming-panel .title, .upcoming-panel .taskname { + -fx-text-fill:#485A33; +} + +.upcoming-panel .subtitle, .upcoming-panel .timestamp { + -fx-text-fill:#8EB264; +} + +/*------------------------------------------ ArchivedPanel Styling ------------------------------------------*/ + +.archived-panel .title { + -fx-font-family:'Helvetica Condensed'; + -fx-font-size:24; + -fx-font-weight:normal; +} + +.archived-panel .taskname, .archived-panel .timestamp { + -fx-text-fill:#FFFFFF; +} +``` diff --git a/collated/main/A0139915W.md b/collated/main/A0139915W.md index 150582cad2c3..e81b39e96c3d 100644 --- a/collated/main/A0139915W.md +++ b/collated/main/A0139915W.md @@ -1,27 +1,4 @@ # A0139915W -###### \java\seedu\savvytasker\commons\events\storage\DataSavingLocationChangedEvent.java -``` java -/** - * Indicates a change in location of the storage - */ -public class DataSavingLocationChangedEvent extends BaseEvent { - - public final ReadOnlySavvyTasker data; - public final String newPath; - - public DataSavingLocationChangedEvent(ReadOnlySavvyTasker data, String newPath) { - this.data = data; - this.newPath = newPath; - } - - @Override - public String toString() { - return "number of tasks " + data.getReadOnlyListOfTasks().size() + - " new path " + this.newPath; - } - -} -``` ###### \java\seedu\savvytasker\commons\events\ui\TaskPanelSelectionChangedEvent.java ``` java @@ -57,7 +34,6 @@ public class TaskPanelSelectionChangedEvent extends BaseEvent { ``` java /** * Helper functions for handling dates. - * @author A0139915W */ public class SmartDefaultDates { @@ -77,41 +53,37 @@ public class SmartDefaultDates { calendar = Calendar.getInstance(); today = Calendar.getInstance(); today.setTime(new Date()); - if (startDateTime == null && endDateTime == null) { - // dates not being supplied, nothing to parse - } else if (startDateTime == null && endDateTime != null) { + if (startDateTime == null && endDateTime != null) { // apply smart default for endDateTime only parseEnd(endDateTime); } else if (startDateTime != null && endDateTime == null) { // apply smart default for startDateTime only parseStart(startDateTime); - } else { + } else if (startDateTime != null && endDateTime != null) { parseStartAndEnd(startDateTime, endDateTime); } } /** - * Gets the smart default for end date + * Gets the smart defaults for end date. + * + * If the date is not supplied, the date will default to today. + * If the time is not supplied, the time will default to 2359:59 on the specified date. + * If both date and time are not supplied, the date returned will be null. * @param today the time now * @param endDateTime the end time to parse - * @return */ public Date getEnd(InferredDate endDateTime) { if (endDateTime == null) return null; calendar.setTime(endDateTime.getInferredDateTime()); if (endDateTime.isDateInferred() && endDateTime.isTimeInferred()) { - // user didn't specify anything // remove date field return null; } else if (endDateTime.isDateInferred()) { - // date not supplied - // defaults to today calendar.set(Calendar.DATE, today.get(Calendar.DATE)); calendar.set(Calendar.MONTH, today.get(Calendar.MONTH)); calendar.set(Calendar.YEAR, today.get(Calendar.YEAR)); } else if (endDateTime.isTimeInferred()) { - // time not supplied - // defaults to 2359 calendar.set(Calendar.HOUR_OF_DAY, 23); calendar.set(Calendar.MINUTE, 59); calendar.set(Calendar.SECOND, 59); @@ -146,7 +118,11 @@ public class SmartDefaultDates { /** - * Gets the smart default for start date + * Gets the smart default for start date. + * + * If the date is not supplied, the date will default to today. + * If the time is not supplied, the time will default to 0000:00 on the specified date. + * If both date and time are not supplied, the date returned will be null. * @param today the time now * @param startDateTime the start time to parse * @return @@ -159,14 +135,10 @@ public class SmartDefaultDates { // remove date field return null; } else if (startDateTime.isDateInferred()) { - // date not supplied - // defaults to today calendar.set(Calendar.DATE, today.get(Calendar.DATE)); calendar.set(Calendar.MONTH, today.get(Calendar.MONTH)); calendar.set(Calendar.YEAR, today.get(Calendar.YEAR)); } else if (startDateTime.isTimeInferred()) { - // time not supplied - // defaults to 0000 calendar.set(Calendar.HOUR_OF_DAY, 0); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); @@ -197,8 +169,12 @@ public class SmartDefaultDates { /** * Sets the starting and ending date/time based on defaults for providing both - * start and end times + * start and end times. + * + * Note that this method has no restrictions on the starting and ending date/time. + * i.e. the starting time is later than the ending time. * @param startDateTime start time supplied + * @param endDateTime end time supplied */ private void parseStartAndEnd(InferredDate startDateTime, InferredDate endDateTime) { assert endDateTime.getInferredDateTime() != null; @@ -288,11 +264,10 @@ public class SmartDefaultDates { addToListOfTasksAdded(tasksAdded.toArray(new Task[tasksAdded.size()])); } + // always >= 0 unless this is being run without UI. int targetIndex = getIndexOfTask(taskAdded); if (targetIndex >= 0) { EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex)); - } else { - // GUI should never ever get here } return new CommandResult(String.format(MESSAGE_SUCCESS, toAdd)); } catch (InvalidDateException ex) { @@ -370,7 +345,6 @@ public class SmartDefaultDates { ``` java /** * Creates the List command to list the specified tasks - * @author A0139915W * @param commandModel Arguments for the List command, must not be null */ public ListCommand(ListType listType) { @@ -389,9 +363,6 @@ public class SmartDefaultDates { // specifies to show the alias switch (_listType) { - case DueDate: - model.updateFilteredListToShowActiveSortedByDueDate(); - break; case PriorityLevel: model.updateFilteredListToShowActiveSortedByPriorityLevel(); break; @@ -401,10 +372,13 @@ public class SmartDefaultDates { case Alias: EventsCenter.getInstance().post(new ChangeListRequestEvent(DisplayedList.Alias)); break; - default: - // nothing to do + case DueDate: + // fall through. + default: // shows lists sorted by due date by default + model.updateFilteredListToShowActiveSortedByDueDate(); break; } + if (_listType != ListType.Alias) { EventsCenter.getInstance().post(new ChangeListRequestEvent(DisplayedList.Task)); return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())); @@ -446,7 +420,7 @@ public class SmartDefaultDates { } ReadOnlyTask taskToModify = lastShownList.get(index - 1); - replacement = new Task(taskToModify, taskName, startDateTime, + Task replacement = new Task(taskToModify, taskName, startDateTime, endDateTime, location, priority, recurringType, numberOfRecurrence, category, description); @@ -454,11 +428,11 @@ public class SmartDefaultDates { try { originalTask = (Task)taskToModify; Task taskModified = model.modifyTask(taskToModify, replacement); + + // GUI will always get index >= 0; int targetIndex = getIndexOfTask(taskModified); if (targetIndex >= 0) { EventsCenter.getInstance().post(new JumpToListRequestEvent(targetIndex)); - } else { - // GUI should never ever get here } } catch (TaskNotFoundException e) { assert false : "The target task cannot be missing"; @@ -479,37 +453,16 @@ public class SmartDefaultDates { return lastShownList.indexOf(task); } ``` -###### \java\seedu\savvytasker\logic\commands\StorageAndModelRequiringCommand.java -``` java -/** - * Represents a command which requires the Storage class as a dependency. - * Commands should inherit this class if they only require dependency for - * storage and model components -*/ -public abstract class StorageAndModelRequiringCommand extends Command { - protected Storage storage; - protected Model model; - - public void setStorage(Storage storage) { - this.storage = storage; - } - - public void setModel(Model model) { - this.model = model; - } -} -``` ###### \java\seedu\savvytasker\logic\LogicManager.java ``` java @Override public ObservableList getFilteredTaskList() { return model.getFilteredTaskList(); } - @Override public ObservableList getAliasSymbolList() { return parser.getAliasSymbolList(); - } + } ``` ###### \java\seedu\savvytasker\logic\parser\FindCommandParser.java ``` java @@ -527,22 +480,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { throw new ParseException(commandText, String.format(Messages.MESSAGE_INVALID_COMMAND_FORMAT, getRequiredFormat())); } ``` -###### \java\seedu\savvytasker\MainApp.java -``` java - @Subscribe - public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { - try { - String configPath = getApplicationParameter("config"); - if(configPath == null) { - configPath = Config.DEFAULT_CONFIG_FILE; - } - config.setSavvyTaskerFilePath(dslce.newPath); - ConfigUtil.saveConfig(config, configPath); - } catch (IOException e) { - logger.warning("Failed to save config file : " + StringUtil.getDetails(e)); - } - } -``` ###### \java\seedu\savvytasker\model\Model.java ``` java /** @@ -593,39 +530,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { void updateFilteredTaskList(FindType findType, String[] keywords); ``` ###### \java\seedu\savvytasker\model\ModelManager.java -``` java - private final SavvyTasker savvyTasker; - private final FilteredList filteredTasks; - private final SortedList sortedAndFilteredTasks; - - /** - * Initializes a ModelManager with the given SavvyTasker - * and its variables should not be null - */ - public ModelManager(SavvyTasker src) { - super(); - assert src != null; - - logger.fine("Initializing with savvy tasker: " + src); - - savvyTasker = new SavvyTasker(src); - filteredTasks = new FilteredList<>(savvyTasker.getTasks()); - sortedAndFilteredTasks = new SortedList<>(filteredTasks, new TaskSortedByDueDate()); - updateFilteredListToShowActive(); // shows only active tasks on start - } - - public ModelManager() { - this(new SavvyTasker()); - } - - public ModelManager(ReadOnlySavvyTasker initialData) { - savvyTasker = new SavvyTasker(initialData); - filteredTasks = new FilteredList<>(savvyTasker.getTasks()); - sortedAndFilteredTasks = new SortedList<>(filteredTasks, new TaskSortedByDueDate()); - updateFilteredListToShowActive(); // shows only active tasks on start - } -``` -###### \java\seedu\savvytasker\model\ModelManager.java ``` java @Override public synchronized Task deleteTask(ReadOnlyTask target) throws TaskNotFoundException { @@ -678,55 +582,56 @@ public abstract class StorageAndModelRequiringCommand extends Command { public void updateFilteredListToShowActive() { updateFilteredTaskList(new PredicateExpression(new TaskIsActiveQualifier())); } - private void updateFilteredListToShowActive(Comparator comparator) { - updateFilteredTaskList( - new PredicateExpression(new TaskIsActiveQualifier()), - comparator); - } - - @Override - public void updateFilteredListToShowArchived() { - updateFilteredTaskList(new PredicateExpression(new TaskIsArchivedQualifier())); - } - - @Override - public void updateFilteredTaskList(FindType findType, String[] keywords) { - assert findType != null; - Qualifier qualifier = null; - switch (findType) - { - case Partial: - qualifier = new TaskNamePartialMatchQualifier(keywords); - break; - case Full: - qualifier = new TaskNameFullMatchQualifier(keywords); - break; - case Exact: - qualifier = new TaskNameExactMatchQualifier(keywords); - break; - case Category: - qualifier = new CategoryPartialMatchQualifier(keywords); - break; - default: - assert false; // should never get here. - } - updateFilteredTaskList(new PredicateExpression(qualifier)); - } - private void updateFilteredTaskList(Expression expression) { - updateFilteredTaskList(expression, new TaskSortedByDefault()); - } - - private void updateFilteredTaskList(Expression expression, Comparator comparator) { - filteredTasks.setPredicate(expression::satisfies); - sortedAndFilteredTasks.setComparator(comparator); - } + private void updateFilteredListToShowActive(Comparator comparator) { + updateFilteredTaskList( + new PredicateExpression(new TaskIsActiveQualifier()), + comparator); + } + + @Override + public void updateFilteredListToShowArchived() { + updateFilteredTaskList(new PredicateExpression(new TaskIsArchivedQualifier())); + } + + @Override + public void updateFilteredTaskList(FindType findType, String[] keywords) { + assert findType != null; + Qualifier qualifier = null; + switch (findType) + { + case Partial: + qualifier = new TaskNamePartialMatchQualifier(keywords); + break; + case Full: + qualifier = new TaskNameFullMatchQualifier(keywords); + break; + case Exact: + qualifier = new TaskNameExactMatchQualifier(keywords); + break; + case Category: + qualifier = new CategoryPartialMatchQualifier(keywords); + break; + default: + assert false; // should never get here. + break; + } + updateFilteredTaskList(new PredicateExpression(qualifier)); + } + + private void updateFilteredTaskList(Expression expression) { + updateFilteredTaskList(expression, new TaskSortedByDefault()); + } + + private void updateFilteredTaskList(Expression expression, Comparator comparator) { + filteredTasks.setPredicate(expression::satisfies); + sortedAndFilteredTasks.setComparator(comparator); + } ``` ###### \java\seedu\savvytasker\model\ModelManager.java ``` java /** * Qualifier matching a partial word from the set of keywords - * @author A0139915W */ private class CategoryPartialMatchQualifier implements Qualifier { private Set keyWordsToMatch; @@ -751,7 +656,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Qualifier matching a partial word from the set of keywords - * @author A0139915W */ private class TaskNamePartialMatchQualifier implements Qualifier { private Set keyWordsToMatch; @@ -776,7 +680,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Qualifier matching a full word from the set of keywords - * @author A0139915W */ private class TaskNameFullMatchQualifier implements Qualifier { private Set keyWordsToMatch; @@ -801,7 +704,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Qualifier matching a exactly from the set of keywords - * @author A0139915W */ private class TaskNameExactMatchQualifier implements Qualifier { private Set keyWordsToMatch; @@ -845,14 +747,13 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Qualifier for checking if {@link Task} is active. Tasks that are not archived are active. - * @author A0139915W * */ private class TaskIsActiveQualifier implements Qualifier { @Override public boolean run(ReadOnlyTask task) { - return task.isArchived() == false; + return !task.isArchived(); } @Override @@ -863,54 +764,59 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Qualifier for checking if {@link Task} is archived - * @author A0139915W * */ private class TaskIsArchivedQualifier implements Qualifier { @Override public boolean run(ReadOnlyTask task) { - return task.isArchived() == true; + return task.isArchived(); } @Override public String toString() { return "isArchived=true"; } - } - - //========== Inner classes/interfaces used for sorting ================================================== +``` +###### \java\seedu\savvytasker\model\ModelManager.java +``` java + + //========== Inner classes/interfaces used for sorting ================================================== /** * Compares {@link Task} by their default field, id - * @author A0139915W - * */ private class TaskSortedByDefault implements Comparator { @Override public int compare(Task task1, Task task2) { - if (task1 == null && task2 == null) return 0; - else if (task1 == null) return 1; - else if (task2 == null) return -1; - else return task1.getId() - task2.getId(); + if (task1 == null && task2 == null) { + return 0; + } else if (task1 == null) { + return 1; + } else if (task2 == null) { + return -1; + } else { + return task1.getId() - task2.getId(); + } } } /** * Compares {@link Task} by their DueDate - * @author A0139915W - * */ private class TaskSortedByDueDate implements Comparator { @Override public int compare(Task task1, Task task2) { - if (task1 == null && task2 == null) return 0; - else if (task1 == null) return 1; - else if (task2 == null) return -1; - else { + if (task1 == null && task2 == null) { + return 0; + } else if (task1 == null) { + return 1; + } else if (task2 == null) { + return -1; + } else { // End dates can be nulls (floating tasks) // Check for existence of endDateTime before comparing if (task1.getEndDateTime() == null && @@ -930,8 +836,6 @@ public abstract class StorageAndModelRequiringCommand extends Command { /** * Compares {@link Task} by their PriorityLevel - * @author A0139915W - * */ private class TaskSortedByPriorityLevel implements Comparator { @@ -1093,8 +997,10 @@ public abstract class StorageAndModelRequiringCommand extends Command { } break; case None: + //fall through default: assert false; // should not come here + break; } t.setStartDateTime(startDate); t.setEndDateTime(endDate); @@ -1212,8 +1118,10 @@ public interface ReadOnlyTask { } if (getDescription() != null && !getDescription().isEmpty()) { builder.append(" Description: ") - . append(getDescription()); + .append(getDescription()); } + builder.append(" Archived: ") + .append(isArchived()); return builder.toString(); } @@ -1223,14 +1131,8 @@ public interface ReadOnlyTask { */ default String getTextForUi() { final StringBuilder builder = new StringBuilder(); - if (getStartDateTime() != null) { - builder.append(" Start: ") - .append(getStartDateTime()) - .append("\n"); - } - if (getEndDateTime() != null) { - builder.append(" End: ") - .append(getEndDateTime()) + if (getStartDateTime() != null || getEndDateTime() != null) { + builder.append(generateDateTime(getStartDateTime(), getEndDateTime())) .append("\n"); } if (getLocation() != null && !getLocation().isEmpty()) { @@ -1238,9 +1140,6 @@ public interface ReadOnlyTask { .append(getLocation()) .append("\n"); } - builder.append(" Priority: ") - .append(getPriority()) - .append("\n"); if (getCategory() != null && !getCategory().isEmpty()) { builder.append(" Category: ") .append(getCategory()) @@ -1251,12 +1150,8 @@ public interface ReadOnlyTask { .append(getDescription()) .append("\n"); } - builder.append(" Archived: ") - .append(isArchived()); return builder.toString(); } - -} ``` ###### \java\seedu\savvytasker\model\task\Task.java ``` java @@ -1387,10 +1282,7 @@ public class Task implements ReadOnlyTask { } private void setStartDate(InferredDate inferredDate) { - if (inferredDate == null) { - // user didn't specify s/ - // keep existing start date - } else { + if (inferredDate != null) { if (inferredDate.isDateInferred() && inferredDate.isTimeInferred()) { // user specified s/ but with nothing tagged to it // remove existing start date @@ -1405,10 +1297,7 @@ public class Task implements ReadOnlyTask { } private void setEndDate(InferredDate inferredDate) { - if (inferredDate == null) { - // user didn't specify e/ - // keep existing end date - } else { + if (inferredDate != null) { if (inferredDate.isDateInferred() && inferredDate.isTimeInferred()) { // user specified e/ but with nothing tagged to it // remove existing end date @@ -1532,8 +1421,11 @@ public class Task implements ReadOnlyTask { } public void setArchived(boolean isArchived) { - if (isArchived) mark(); - else unmark(); + if (isArchived) { + mark(); + } else { + unmark(); + } } /** @@ -1598,6 +1490,12 @@ public class Task implements ReadOnlyTask { */ public class TaskList implements Iterable { + private final ObservableList internalList = FXCollections.observableArrayList(); + private int nextId = 0; + private boolean isNextIdInitialized = false; + private int nextGroupId = 0; + private boolean isNextGroupIdInitialized = false; + /** * Signals that an operation would have violated the 'end time earlier than start time' property of the list. */ @@ -1639,17 +1537,6 @@ public class TaskList implements Iterable { */ private static final long serialVersionUID = -7591982407764643511L; } - - private final ObservableList internalList = FXCollections.observableArrayList(); - private int nextId = 0; - private boolean isNextIdInitialized = false; - private int nextGroupId = 0; - private boolean isNextGroupIdInitialized = false; - - /** - * Constructs empty TaskList. - */ - public TaskList() {} /** * Gets the next available id for uniquely identifying a task in @@ -1710,11 +1597,8 @@ public class TaskList implements Iterable { */ public boolean isValidStartEnd(ReadOnlyTask toCheck) { assert toCheck != null; - if (toCheck.getStartDateTime() != null && toCheck.getEndDateTime() != null && - toCheck.getStartDateTime().compareTo(toCheck.getEndDateTime()) >= 0) { - return false; - } - return true; + return toCheck.getStartDateTime() == null || toCheck.getEndDateTime() == null || + toCheck.getStartDateTime().compareTo(toCheck.getEndDateTime()) < 0; } /** @@ -1812,19 +1696,6 @@ public class TaskList implements Iterable { return savvyTaskerStorage.setSavvyTaskerFilePath(path); } ``` -###### \java\seedu\savvytasker\storage\StorageManager.java -``` java - @Override - @Subscribe - public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { - logger.info(LogsCenter.getEventHandlingLogMessage(dslce, "Local storage location changed.")); - try { - saveSavvyTasker(dslce.data); - } catch (IOException e) { - raise(new DataSavingExceptionEvent(e)); - } - } -``` ###### \java\seedu\savvytasker\storage\XmlAdaptedTask.java ``` java /** @@ -1918,10 +1789,3 @@ public class XmlAdaptedTask { return false; } ``` -###### \java\seedu\savvytasker\ui\StatusBarFooter.java -``` java - @Subscribe - public void handleSavvyTaskerSaveLocationChangedEvent(DataSavingLocationChangedEvent dslce) { - setSaveLocation(dslce.newPath); - } -``` diff --git a/collated/main/A0139916U.md b/collated/main/A0139916U.md index 466daa605bbd..9d12c61e67f8 100644 --- a/collated/main/A0139916U.md +++ b/collated/main/A0139916U.md @@ -195,6 +195,15 @@ public class UnaliasCommand extends ModelRequiringCommand { ``` ###### \java\seedu\savvytasker\logic\LogicManager.java +``` java + undoDeque.addLast(command); + if (undoDeque.size() > MAX_UNDO_REDO_QUEUE_SIZE) { + undoDeque.removeFirst(); + } + redoDeque.clear(); + } +``` +###### \java\seedu\savvytasker\logic\LogicManager.java ``` java private void registerAllDefaultCommandParsers() { parser.registerCommandParser(new AddCommandParser()); @@ -221,26 +230,34 @@ public class UnaliasCommand extends ModelRequiringCommand { } } + /** + * Undo last command and add it to the redo deque. + * @return true if undone successfully, false otherwise + */ private boolean undo() { boolean undone = false; - if (!undoStack.isEmpty()) { - Command command = undoStack.pop(); + if (!undoDeque.isEmpty()) { + Command command = undoDeque.removeLast(); command.undo(); - redoStack.push(command); + redoDeque.addLast(command); undone = true; } return undone; } + /** + * Redo last command and add it to undone deque. + * @return true if redone successfully, false otherwise + */ private boolean redo() { boolean redone = false; - if (!redoStack.isEmpty()) { - Command command = redoStack.pop(); + if (!redoDeque.isEmpty()) { + Command command = redoDeque.removeLast(); command.redo(); - undoStack.push(command); + undoDeque.addLast(command); redone = true; } @@ -294,6 +311,7 @@ public class UnaliasCommand extends ModelRequiringCommand { ``` ###### \java\seedu\savvytasker\logic\parser\AddCommandParser.java ``` java +// Please see CommandParser interface for documentation for many of the overridden methods package seedu.savvytasker.logic.parser; import java.util.regex.Matcher; @@ -415,9 +433,10 @@ public class AliasCommandParser implements CommandParser { private String parseRepresentation(String originalText) throws ParseException { String trimmedText = originalText.trim(); - if (trimmedText.isEmpty()) + if (trimmedText.isEmpty()) { throw new ParseException(trimmedText, "REPRESENTATION: Needs to be at least one character!"); - + } + return trimmedText; } @@ -607,16 +626,16 @@ public class DateParser { List dateGroups = this.nattyParser.parse(input); int totalDates = countDates(dateGroups); - if (totalDates == 0) + if (totalDates == 0) { throw new ParseException(input, "Failed to understand given date."); + } - if (totalDates > 1) + if (totalDates > 1) { throw new ParseException(input, "Too many dates entered."); + } DateGroup group = dateGroups.get(0); - - return new InferredDate( group.getDates().get(0), group.isDateInferred(), @@ -818,15 +837,17 @@ public class IndexParser { try { index = Integer.parseInt(trimmedIndexText); - if (index <= 0) + if (index <= 0) { parseError = true; + } } catch (NumberFormatException ex) { parseError = true; } - if (parseError) + if (parseError) { throw new ParseException(trimmedIndexText, "Must be a positive whole number."); - + } + return index; } @@ -858,9 +879,10 @@ public class IndexParser { parseError = true; } - if (parseError) + if (parseError) { throw new ParseException(trimmedIndicesText, INDEX_MUST_BE_POSITIVE); - + } + return indices; } } @@ -910,8 +932,9 @@ public class ListCommandParser implements CommandParser { } private ListType parseListType(String listTypeText) throws ParseException { - if (listTypeText == null) + if (listTypeText == null) { return null; + } try { listTypeText = listTypeText.trim(); @@ -1127,8 +1150,9 @@ public class MasterParser { String spaces = matcher.group(2); // Preserves the amount of spaces as that may be what user wants AliasSymbol symbol = aliasingSymbols.get(keyword); - if (symbol != null) + if (symbol != null) { keyword = symbol.getRepresentation(); + } builder.append(keyword); builder.append(spaces); @@ -1154,10 +1178,12 @@ public class MasterParser { public boolean registerCommandParser(CommandParser commandParser) { assert commandParser != null; - if (commandParsers.containsKey(commandParser.getHeader())) + if (commandParsers.containsKey(commandParser.getHeader())) { return false; - if (aliasingSymbols.containsKey(commandParser.getHeader())) + } + if (aliasingSymbols.containsKey(commandParser.getHeader())) { return false; + } commandParsers.put(commandParser.getHeader(), commandParser); return true; @@ -1197,10 +1223,12 @@ public class MasterParser { public boolean addAliasSymbol(AliasSymbol symbol) { assert symbol != null; - if (aliasingSymbols.containsKey(symbol.getKeyword())) + if (aliasingSymbols.containsKey(symbol.getKeyword())) { return false; - if (isCommandParserRegistered(symbol.getKeyword())) + } + if (isCommandParserRegistered(symbol.getKeyword())) { return false; + } aliasList.add(symbol); aliasingSymbols.put(symbol.getKeyword(), symbol); @@ -1479,7 +1507,8 @@ import seedu.savvytasker.model.task.RecurrenceType; /** * This class contains common parsing methods for parsing Task fields. * Each of the parse method takes in a string which can be null, and return - * the respective parsed object. + * the respective parsed object. If null is provided to each of the parse methods, + * null will be returned. */ public abstract class TaskFieldParser implements CommandParser { /* @@ -1502,8 +1531,9 @@ public abstract class TaskFieldParser implements CommandParse } protected String parseTaskName(String taskNameText) throws ParseException { - if (taskNameText == null) + if (taskNameText == null) { return null; + } return taskNameText.trim(); } @@ -1516,9 +1546,9 @@ public abstract class TaskFieldParser implements CommandParse } private InferredDate parseDate(String dateText, String errorField) throws ParseException { - if (dateText == null) + if (dateText == null) { return null; - + } String trimmedDateText = dateText.trim(); try { return dateParser.parseSingle(trimmedDateText); @@ -1528,14 +1558,16 @@ public abstract class TaskFieldParser implements CommandParse } protected String parseLocation(String locationText) throws ParseException { - if (locationText == null) + if (locationText == null) { return null; + } return locationText.trim(); } protected PriorityLevel parsePriorityLevel(String priorityLevelText) throws ParseException { - if (priorityLevelText == null) + if (priorityLevelText == null) { return null; + } String trimmedPriorityLevelText = priorityLevelText.trim(); try { @@ -1546,8 +1578,9 @@ public abstract class TaskFieldParser implements CommandParse } protected RecurrenceType parseRecurrenceType(String recurrenceTypeText) throws ParseException { - if (recurrenceTypeText == null) + if (recurrenceTypeText == null) { return null; + } String trimmedRecurrenceTypeText = recurrenceTypeText.trim(); try { @@ -1558,8 +1591,9 @@ public abstract class TaskFieldParser implements CommandParse } protected Integer parseNumberOfRecurrence(String numRecurrenceText) throws ParseException { - if (numRecurrenceText == null) + if (numRecurrenceText == null) { return null; + } String trimmedNumRecurrenceText = numRecurrenceText.trim(); int numRecurrence = 0; @@ -1567,30 +1601,33 @@ public abstract class TaskFieldParser implements CommandParse try { numRecurrence = Integer.parseInt(trimmedNumRecurrenceText); - if (numRecurrence < 0) + if (numRecurrence < 0) { parseError = true; + } } catch (NumberFormatException ex) { parseError = true; } - if (parseError) + if (parseError) { throw new ParseException(trimmedNumRecurrenceText, "NUMBER_OF_RECURRENCE: Must be a nonnegative whole number!"); + } return numRecurrence; } protected String parseCategory(String categoryText) throws ParseException { - if (categoryText == null) + if (categoryText == null) { return null; + } return categoryText.trim(); } protected String parseDescription(String descriptionText) throws ParseException { - if (descriptionText == null) + if (descriptionText == null) { return null; + } return descriptionText.trim(); } -} ``` ###### \java\seedu\savvytasker\logic\parser\UnaliasCommandParser.java ``` java @@ -1987,8 +2024,9 @@ public enum ListType { */ public static ListType valueOfIgnoreCase(String name) { for (ListType type : ListType.values()) { - if (type.toString().equalsIgnoreCase(name)) + if (type.toString().equalsIgnoreCase(name)) { return type; + } } throw new IllegalArgumentException("Unknown list type: " + name); @@ -1998,13 +2036,13 @@ public enum ListType { ###### \java\seedu\savvytasker\model\ModelManager.java ``` java - private void indicateAliasSymbolAdded(AliasSymbol symbol) { - raise(new AliasSymbolChangedEvent(symbol, AliasSymbolChangedEvent.Action.Added)); - } - - private void indicateAliasSymbolRemoved(AliasSymbol symbol) { - raise(new AliasSymbolChangedEvent(symbol, AliasSymbolChangedEvent.Action.Removed)); - } + private void indicateAliasSymbolAdded(AliasSymbol symbol) { + raise(new AliasSymbolChangedEvent(symbol, AliasSymbolChangedEvent.Action.Added)); + } + + private void indicateAliasSymbolRemoved(AliasSymbol symbol) { + raise(new AliasSymbolChangedEvent(symbol, AliasSymbolChangedEvent.Action.Removed)); + } ``` ###### \java\seedu\savvytasker\model\ModelManager.java ``` java @@ -2118,8 +2156,9 @@ public enum FindType { */ public static FindType valueOfIgnoreCase(String name) { for (FindType type : FindType.values()) { - if (type.toString().equalsIgnoreCase(name)) + if (type.toString().equalsIgnoreCase(name)) { return type; + } } throw new IllegalArgumentException("Unknown find type: " + name); @@ -2145,8 +2184,9 @@ public enum PriorityLevel { */ public static PriorityLevel valueOfIgnoreCase(String name) { for (PriorityLevel type : PriorityLevel.values()) { - if (type.toString().equalsIgnoreCase(name)) + if (type.toString().equalsIgnoreCase(name)) { return type; + } } throw new IllegalArgumentException("Unknown priority level: " + name); @@ -2189,8 +2229,9 @@ public enum RecurrenceType { */ public static RecurrenceType valueOfIgnoreCase(String name) { for (RecurrenceType type : RecurrenceType.values()) { - if (type.toString().equalsIgnoreCase(name)) + if (type.toString().equalsIgnoreCase(name)) { return type; + } } throw new IllegalArgumentException("Unknown recurrence type: " + name); diff --git a/collated/test/A0097627N.md b/collated/test/A0097627N.md new file mode 100644 index 000000000000..72053ece4fe2 --- /dev/null +++ b/collated/test/A0097627N.md @@ -0,0 +1,300 @@ +# A0097627N +###### \java\guitests\RedoCommandTest.java +``` java +package guitests; + +import guitests.guihandles.TaskCardHandle; + +import org.junit.Test; + +import seedu.savvytasker.logic.commands.UndoCommand; +import seedu.savvytasker.logic.commands.RedoCommand; +import seedu.savvytasker.logic.commands.HelpCommand; +import seedu.savvytasker.testutil.TestTask; +import seedu.savvytasker.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.savvytasker.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.savvytasker.logic.commands.RedoCommand.MESSAGE_REDO_ACKNOWLEDGEMENT; + +public class RedoCommandTest extends SavvyTaskerGuiTest { + + TestTask[] expectedList = td.getTypicalTasks(); + TestTask[] currentList = td.getTypicalTasks(); + TestTask firstTaskToAdd = td.happy; + TestTask secondTaskToAdd = td.haloween; + TestTask pjmTaskToAdd = td.pjm; + TestTask projectMeetingTaskToAdd = td.projectMeeting; + + @Test + // redo one add command + public void redoAddTest() { + expectedList = TestUtil.addTasksToList(currentList, firstTaskToAdd); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + @Test + // redo a delete command + public void redoDeleteTest() { + expectedList = TestUtil.removeTaskFromList(currentList, 1); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + @Test + // redo clear command + public void redoClearTest() { + commandBox.runCommand("clear"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertListSize(0); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + @Test + // redo alias command + public void redoAliasTest() { + expectedList = td.getTypicalTasks(); + expectedList = TestUtil.addTasksToList(expectedList, projectMeetingTaskToAdd); + commandBox.runCommand("alias k/pjm r/Project Meeting"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + commandBox.runCommand(pjmTaskToAdd.getAddCommand()); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + + @Test + // redo unalias command + public void redoUnaliasTest() { + expectedList = TestUtil.addTasksToList(currentList, pjmTaskToAdd); + commandBox.runCommand("alias k/pjm r/Project Meeting"); + commandBox.runCommand("unalias pjm"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + commandBox.runCommand(pjmTaskToAdd.getAddCommand()); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + + // redo two add commands + @Test + public void redoTwoAddTest() { + expectedList = TestUtil.addTasksToList(currentList, firstTaskToAdd); + expectedList = TestUtil.addTasksToList(expectedList, secondTaskToAdd); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand(secondTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + // redo two delete commands + @Test + public void redoTwoDeleteTest() { + expectedList = TestUtil.removeTaskFromList(currentList, 1); + expectedList = TestUtil.removeTaskFromList(expectedList, 1); + commandBox.runCommand("delete 1"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + // redo a delete command followed by an add command + @Test + public void redoDeleteAddTest() { + expectedList = TestUtil.addTasksToList(currentList, firstTaskToAdd); + expectedList = TestUtil.removeTaskFromList(expectedList, 1); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + // redo an add command followed by a delete command + @Test + public void redoAddDeleteTest() { + expectedList = TestUtil.removeTaskFromList(currentList, 1); + expectedList = TestUtil.addTasksToList(expectedList, firstTaskToAdd); + commandBox.runCommand("delete 1"); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + commandBox.runCommand("redo"); + commandBox.runCommand("redo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_REDO_ACKNOWLEDGEMENT); + } + + // invalid command + @Test + public void invalidTest() { + commandBox.runCommand("redos"); + assertResultMessage("Input: redos\n" + String.format(MESSAGE_UNKNOWN_COMMAND, HelpCommand.MESSAGE_USAGE)); + } +} +``` +###### \java\guitests\UndoCommandTest.java +``` java +package guitests; + +import guitests.guihandles.TaskCardHandle; + +import org.junit.Test; + +import seedu.savvytasker.logic.commands.UndoCommand; +import seedu.savvytasker.logic.commands.HelpCommand; +import seedu.savvytasker.testutil.TestTask; +import seedu.savvytasker.testutil.TestUtil; + +import static org.junit.Assert.assertTrue; +import static seedu.savvytasker.commons.core.Messages.MESSAGE_UNKNOWN_COMMAND; +import static seedu.savvytasker.logic.commands.UndoCommand.MESSAGE_UNDO_ACKNOWLEDGEMENT; + +public class UndoCommandTest extends SavvyTaskerGuiTest { + + TestTask[] expectedList = td.getTypicalTasks(); + TestTask[] currentList = td.getTypicalTasks(); + TestTask firstTaskToAdd = td.happy; + TestTask secondTaskToAdd = td.haloween; + TestTask pjmTaskToAdd = td.pjm; + TestTask projectMeetingTaskToAdd = td.projectMeeting; + + @Test + // undo one add command + public void undoAddTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + @Test + // undo a delete command + public void undoDeleteTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + @Test + // undo clear command + public void undoClearTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand("clear"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + @Test + // undo alias command + public void undoAliasTest() { + expectedList = td.getTypicalTasks(); + expectedList = TestUtil.addTasksToList(expectedList, pjmTaskToAdd); + commandBox.runCommand("alias k/pjm r/Project Meeting"); + commandBox.runCommand("undo"); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + commandBox.runCommand(pjmTaskToAdd.getAddCommand()); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + + @Test + // undo unalias command + public void undoUnaliasTest() { + expectedList = TestUtil.addTasksToList(currentList, projectMeetingTaskToAdd); + commandBox.runCommand("alias k/pjm r/Project Meeting"); + commandBox.runCommand("unalias pjm"); + commandBox.runCommand("undo"); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + commandBox.runCommand(pjmTaskToAdd.getAddCommand()); + assertTrue(taskListPanel.isListMatching(expectedList)); + } + + // undo mark command + @Test + public void undoMarkTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand("mark 1"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + // undo two add commands + @Test + public void undoTwoAddTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand(secondTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + // undo two delete commands + @Test + public void undoTwoDeleteTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand("delete 1"); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + // undo a delete command followed by an add command + @Test + public void undoDeleteAddTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("delete 1"); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + // undo an add command followed by a delete command + @Test + public void undoAddDeleteTest() { + expectedList = td.getTypicalTasks(); + commandBox.runCommand("delete 1"); + commandBox.runCommand(firstTaskToAdd.getAddCommand()); + commandBox.runCommand("undo"); + commandBox.runCommand("undo"); + assertTrue(taskListPanel.isListMatching(expectedList)); + assertResultMessage(MESSAGE_UNDO_ACKNOWLEDGEMENT); + } + + // invalid command + @Test + public void invalidTest() { + commandBox.runCommand("undos"); + assertResultMessage("Input: undos\n" + String.format(MESSAGE_UNKNOWN_COMMAND, HelpCommand.MESSAGE_USAGE)); + } +} +``` diff --git a/collated/test/A0138431L.md b/collated/test/A0138431L.md new file mode 100644 index 000000000000..2df548b81caf --- /dev/null +++ b/collated/test/A0138431L.md @@ -0,0 +1,36 @@ +# A0138431L +###### \java\guitests\StorageCommandTest.java +``` java +public class StorageCommandTest extends SavvyTaskerGuiTest { + + private static final String CONFIG_JSON = "config.json"; + private static final String CONFIG_LOCATION = "./src/test/data/SaveLocationCommandTest"; + + @Test + public void saveToValidFilePath_success() throws DataConversionException, IOException, DuplicateTaskException { + String testFilePath = "./src/test/data/StorageCommandTest/newStorageLocation/"; + commandBox.runCommand("storage " + testFilePath); + assertWriteToJsonSuccess(); + assertWriteToXmlSuccess(); + } + private void assertWriteToJsonSuccess() throws DataConversionException { + JsonConfigStorage jsonConfigStorage = new JsonConfigStorage(CONFIG_LOCATION); + Optional config = jsonConfigStorage.readConfig(CONFIG_JSON); + assert(config.isPresent()); + } + + private void assertWriteToXmlSuccess() { + TestTask[] currentList = td.getTypicalTasks(); + assertTrue(taskListPanel.isListMatching(currentList)); + } + + @Test + public void storage() { + //invalid command + commandBox.runCommand("store"); + assertResultMessage("Input: store\n" + + String.format(MESSAGE_UNKNOWN_COMMAND, HelpCommand.MESSAGE_USAGE)); + } + +} +``` diff --git a/collated/test/A0139915W.md b/collated/test/A0139915W.md index 3a51dbc567e6..23287a4c2f9e 100644 --- a/collated/test/A0139915W.md +++ b/collated/test/A0139915W.md @@ -3,6 +3,9 @@ ``` java public class AddCommandTest extends SavvyTaskerGuiTest { + private DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()); + private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); + @Test public void add() { //add one task @@ -44,8 +47,7 @@ public class AddCommandTest extends SavvyTaskerGuiTest { TestTask[] expectedList = TestUtil.addTasksToList(currentList, taskToAdd); assertTrue(taskListPanel.isListMatching(expectedList)); } - - private DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()); + private String getLocaleDateString(Date date) { try { return formatter.format(date); @@ -55,7 +57,6 @@ public class AddCommandTest extends SavvyTaskerGuiTest { return null; } - private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); private Date getDate(String ddmmyyyy) { try { return format.parse(ddmmyyyy); @@ -76,11 +77,9 @@ public class DeleteCommandTest extends SavvyTaskerGuiTest { //delete the first in the list TestTask[] currentList = td.getTypicalTasks(); int targetIndex = 1; - assertDeleteSuccess(targetIndex, currentList); //delete the last in the list - currentList = TestUtil.removeTaskFromList(currentList, targetIndex); targetIndex = currentList.length; assertDeleteSuccess(targetIndex, currentList); @@ -438,6 +437,9 @@ public class ListCommandTest extends SavvyTaskerGuiTest { ``` java public class ModifyCommandTest extends SavvyTaskerGuiTest { + private DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()); + private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); + @Test public void add() { //modify task @@ -480,7 +482,6 @@ public class ModifyCommandTest extends SavvyTaskerGuiTest { assertTrue(taskListPanel.isListMatching(expectedList)); } - private DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.getDefault()); private String getLocaleDateString(Date date) { try { return formatter.format(date); @@ -490,7 +491,6 @@ public class ModifyCommandTest extends SavvyTaskerGuiTest { return null; } - private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); private Date getDate(String ddmmyyyy) { try { return format.parse(ddmmyyyy); @@ -506,6 +506,8 @@ public class ModifyCommandTest extends SavvyTaskerGuiTest { ``` java public class SmartDefaultDatesTest { + private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HHmmss"); + @Test public void smartDefaultDates_parseStart() { DateParser dateParser = new DateParser(); @@ -520,10 +522,9 @@ public class SmartDefaultDatesTest { SmartDefaultDates sdd = new SmartDefaultDates(inferredStart, inferredEnd); // specifying only start date, assumed to on the given date at 12am // and to end on the given date at 2359:59 - Date expectedStartTime = getDate("31/12/2016 000000"); - Date expectedEndTime = getDate("31/12/2016 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate("31/12/2016 000000"), getDate("31/12/2016 235959"), + sdd.getStartDate(), sdd.getEndDate()); SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy"); Date today = today(0, 0); @@ -536,10 +537,9 @@ public class SmartDefaultDatesTest { sdd = new SmartDefaultDates(inferredStart, inferredEnd); // specifying only start time, assumed to start today at the given time // and to end today 2359:59 - expectedStartTime = getDate(sdf.format(today) + " 150000"); - expectedEndTime = getDate(sdf.format(today) + " 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate(sdf.format(today) + " 150000"), getDate(sdf.format(today) + " 235959"), + sdd.getStartDate(), sdd.getEndDate()); } @Test @@ -558,10 +558,9 @@ public class SmartDefaultDatesTest { SmartDefaultDates sdd = new SmartDefaultDates(inferredStart, inferredEnd); // specified only the end date, assumed to start today at 12am // and to end on the given date at 2359:59 - Date expectedStartTime = getDate(sdf.format(today) + " 000000"); - Date expectedEndTime = getDate("31/12/2016 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate(sdf.format(today) + " 000000"), getDate("31/12/2016 235959"), + sdd.getStartDate(), sdd.getEndDate()); try { //use MM-dd-yyyy @@ -572,10 +571,9 @@ public class SmartDefaultDatesTest { sdd = new SmartDefaultDates(inferredStart, inferredEnd); // specified only the end time, assumed to start today at 12am // and to end at the given time today - expectedStartTime = getDate(sdf.format(today) + " 000000"); - expectedEndTime = getDate(sdf.format(today) + " 150000"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate(sdf.format(today) + " 000000"), getDate(sdf.format(today) + " 150000"), + sdd.getStartDate(), sdd.getEndDate()); try { @@ -587,10 +585,9 @@ public class SmartDefaultDatesTest { sdd = new SmartDefaultDates(inferredStart, inferredEnd); // specified only the end date in the past, start date will be null // and to end on the given date at 2359:59 - expectedStartTime = null; - expectedEndTime = getDate("31/12/2000 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + null, getDate("31/12/2000 235959"), + sdd.getStartDate(), sdd.getEndDate()); } @Test @@ -617,10 +614,9 @@ public class SmartDefaultDatesTest { // no time supplied for start and end // start defaults to 0000 // end defaults to 2359:59 - Date expectedStartTime = getDate("31/12/2016 000000"); - Date expectedEndTime = getDate("31/12/2016 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate("31/12/2016 000000"), getDate("31/12/2016 235959"), + sdd.getStartDate(), sdd.getEndDate()); inferredStart = null; inferredEnd = null; @@ -636,10 +632,9 @@ public class SmartDefaultDatesTest { // start defaults to 0000 // end defaults to 2359:59 // no restrictions imposed on end time earlier than start time - expectedStartTime = getDate("31/12/2016 000000"); - expectedEndTime = getDate("30/12/2016 235959"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate("31/12/2016 000000"), getDate("30/12/2016 235959"), + sdd.getStartDate(), sdd.getEndDate()); inferredStart = null; inferredEnd = null; @@ -653,10 +648,9 @@ public class SmartDefaultDatesTest { sdd = new SmartDefaultDates(inferredStart, inferredEnd); // no date supplied for start and end // start and end defaults to the given time today - expectedStartTime = getDate(sdf.format(today) + " 100000"); - expectedEndTime = getDate(sdf.format(today) + " 220000"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate(sdf.format(today) + " 100000"), getDate(sdf.format(today) + " 220000"), + sdd.getStartDate(), sdd.getEndDate()); inferredStart = null; inferredEnd = null; @@ -671,10 +665,9 @@ public class SmartDefaultDatesTest { // no date supplied for start and end, end time ends before start time // start and end defaults to the given time today // no restrictions imposed on end time being earlier - expectedStartTime = getDate(sdf.format(today) + " 220000"); - expectedEndTime = getDate(sdf.format(today) + " 100000"); - assertEquals(expectedStartTime, sdd.getStartDate()); - assertEquals(expectedEndTime, sdd.getEndDate()); + assertStartEndEquals( + getDate(sdf.format(today) + " 220000"), getDate(sdf.format(today) + " 100000"), + sdd.getStartDate(), sdd.getEndDate()); } @Test @@ -685,10 +678,8 @@ public class SmartDefaultDatesTest { SmartDefaultDates sdd = new SmartDefaultDates(null, null); Date actualStart = sdd.getStart(dateParser.new InferredDate(new Date(), true, true)); Date actualEnd = sdd.getEnd(dateParser.new InferredDate(new Date(), true, true)); - Date expectedStart = null; - Date expectedEnd = null; - assertEquals(expectedStart, actualStart); - assertEquals(expectedEnd, actualEnd); + assertStartEndEquals(null, null, + actualStart, actualEnd); try { //use MM-dd-yyyy @@ -697,10 +688,9 @@ public class SmartDefaultDatesTest { } catch (ParseException e) { assert false; //won't get here } - expectedStart = getDate(sdf.format(today) + " 220000"); - expectedEnd = getDate(sdf.format(today) + " 100000"); - assertEquals(expectedStart, actualStart); - assertEquals(expectedEnd, actualEnd); + assertStartEndEquals( + getDate(sdf.format(today) + " 220000"), getDate(sdf.format(today) + " 100000"), + actualStart, actualEnd); try { //use MM-dd-yyyy @@ -709,13 +699,16 @@ public class SmartDefaultDatesTest { } catch (ParseException e) { assert false; //won't get here } - expectedStart = getDate("31/12/2016 000000"); - expectedEnd = getDate("31/12/2016 235959"); + assertStartEndEquals(getDate("31/12/2016 000000"), getDate("31/12/2016 235959"), + actualStart, actualEnd); + } + + private void assertStartEndEquals(Date expectedStart, Date expectedEnd, + Date actualStart, Date actualEnd) { assertEquals(expectedStart, actualStart); assertEquals(expectedEnd, actualEnd); } - private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy HHmmss"); private Date getDate(String ddmmyyyy) { try { return format.parse(ddmmyyyy); @@ -1088,7 +1081,6 @@ public class TestTask implements ReadOnlyTask { ``` ###### \java\seedu\savvytasker\testutil\TestUtil.java ``` java - public static final Task[] sampleTaskData = getSampleTaskData(); private static Task[] getSampleTaskData() { return new Task[]{ @@ -1166,7 +1158,7 @@ public class TestTask implements ReadOnlyTask { public class TypicalTestTasks { public TestTask highPriority, medPriority, lowPriority, furthestDue, - nearerDue, notSoNearerDue, earliestDue, longDue, happy, haloween; + nearerDue, notSoNearerDue, earliestDue, longDue, happy, haloween, pjm, projectMeeting; private SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy"); public TypicalTestTasks() { @@ -1191,6 +1183,8 @@ public class TypicalTestTasks { //Manually added happy = new TaskBuilder().withId(9).withTaskName("Happy Task").build(); haloween = new TaskBuilder().withId(10).withTaskName("Haloween Task").build(); + pjm = new TaskBuilder().withId(11).withTaskName("pjm").build(); + projectMeeting = new TaskBuilder().withId(12).withTaskName("Project Meeting").build(); } catch (IllegalValueException e) { e.printStackTrace(); assert false : "not possible"; diff --git a/docs/AboutUs.md b/docs/AboutUs.md index aa805f3a4171..b4a3d6ee4b84 100644 --- a/docs/AboutUs.md +++ b/docs/AboutUs.md @@ -18,8 +18,18 @@
**Role**: Developer
Responsibilities: Team Lead
-Component SME: UI - +Component SME: [UI](DeveloperGuide.md#UI-component) +* Aspects/tools in charge of: Documentation, Code Quality +* Features implemented: + * [Storage](UserGuide.md#change-storage-location--storage) + * [UP](UserGuide.md#command-stack-history) + * [DOWN](UserGuide.md#command-stack-history) + * [LEFT](UserGuide.md#week-selection) + * [RIGHT](UserGuide.md#week-selection) +* Code written: [[functional code](../collated/main/A0138431L.md)][[test code](../collated/test/A0138431L.md)][[docs](../collated/docs/A0138431L.md)] +* Other major contributions: + * Design of UI which includes MainWindow.fxml, DailyList.fxml, FloatingList.fxml and UpcomingList.fxml + ----- #### [Low Zheng Heng Henry](http://github.com/e0003801) @@ -66,6 +76,6 @@ Component SME: UI * [Unmark task](UserGuide.md#unmark-a-task-as-done--unmark) * [Undo task](UserGuide.md#undo-the-most-recent-operation--undo) * [Redo task](UserGuide.md#redo-the-most-recent-undo-operation--redo) -* Code written: [[functional code](../collated/main/A0097627N.md)][[docs](../collated/docs/A0097627N.md)] +* Code written: [[functional code](../collated/main/A0097627N.md)][[test code](../collated/test/A0097627N.md)][[docs](../collated/docs/A0097627N.md)] ----- diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index 16f7b1f58831..455eabffc885 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -109,14 +109,15 @@ The sections below give more details of each component. [//]: # (@@author) +[//]: # (@@author A0138431L) ### UI component
**API** : [`Ui.java`](../src/main/java/seedu/savvytasker/ui/Ui.java) -The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `PersonListPanel`, -`StatusBarFooter`, `BrowserPanel` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class +The UI consists of a `MainWindow` that is made up of parts e.g.`CommandBox`, `ResultDisplay`, `TaskListPanel`, `UpcomingPanel`, `DailyPanel`, `FloatingPanel`, +`StatusBarFooter` etc. All these, including the `MainWindow`, inherit from the abstract `UiPart` class and they can be loaded using the `UiPartLoader`. The `UI` component uses JavaFx UI framework. The layout of these UI parts are defined in matching `.fxml` files @@ -129,6 +130,10 @@ The `UI` component, * Binds itself to some data in the `Model` so that the UI can auto-update when data in the `Model` change. * Responds to events raised from various parts of the App and updates the UI accordingly. +The cursor will be focus to the `CommandBox` by default as the `CommandBox` carries out numerous keyboard shortcuts to make the app more user-friendly. + +[//]: # (@@author) + [//]: # (@@author A0139916U) ### Logic component diff --git a/docs/UserGuide.md b/docs/UserGuide.md index a2594b5d230f..b6cf2c1d6ae7 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -155,13 +155,13 @@ Format: `modify INDEX [t/TASK_NAME] [s/START_DATE] [e/END_DATE] [l/LOCATION] [p/ #### Change storage location : `storage` Changes the storage location of Savvy Tasker.
-Format: `storage PATH` +Format: `storage NEW_FILEPATH` > Parameters | Description > -------- | :-------- > PATH | `Mandatory` Specifies the path where Savvy Tasker's task list is saved at. >
-> If the new storage location specified by `PATH` is not accessible by Savvy Tasker, no change will be made to the existing path. +> If the new storage location specified by `NEW_FILEPATH` is not accessible by Savvy Tasker, no change will be made to the existing path. [//]: # (@@author A0097627N) @@ -256,6 +256,20 @@ Add task named "pjm" to task list the file that contains the data of your previous Savvy Tasker folder. +[//]: # (@@author A0139915W) + +[//]: # (@@author A0138431L) + +#### Command stack history +UP: Return last user input command in command box
+DOWN: Return (if any) next user input command in command box
+> Note that DOWN is only allowed after at least an UP is being entered + + +#### Week Selection +Ctrl + LEFT: Display previous week’s daily task list
+Ctrl + RIGHT: Display next week’s daily task list + [//]: # (@@author A0139915W) ## Command Summary @@ -272,8 +286,30 @@ Command | Format [Help](#viewing-help--help) | `help` [Modify](#modifies-a-task--modify) | `modify INDEX [t/TASK_NAME] [s/START_DATE] [e/END_DATE] [l/LOCATION] [p/PRIORITY_LEVEL] [r/RECURRING_TYPE] [n/NUMBER_OF_RECURRENCE] [c/CATEGORY] [d/DESCRIPTION]`
Example: `modify 2 t/Wednesday Weekly Milestone s/wed d/Project Meeting and Finalization` [Mark](#mark-a-task-as-done--mark) | `mark INDEX [MORE_INDEX]`
Example: `mark 1 2 3` -[Storage](#change-storage-location--storage) | `storage PATH`
Example: `storage data/savvytasker.xml` +[Storage](#change-storage-location--storage) | `storage NEW_FILEPATH`
Example: `storage data/savvytasker.xml` [Unmark](#unmark-a-task-as-done--unmark) | `unmark INDEX [MORE_INDEX]`
Example: `unmark 1 2 3` [Undo](#undo-the-most-recent-operation--undo) | `undo` [Redo](#redo-the-most-recent-undo-operation--redo) | `redo` [Unalias](#unalias-a-keyword--unalias) | `unalias s/SHORT_KEYWORD`
Example: `unalias s/mss` + +[//]: # (@@author A0138431L) + +## Keyboard Shortcuts + +Key Codes | Function | Command Box Input +-------- | :-------- | :-------- +Esc | Toggle to show/hide a list of keyboard shortcuts | - +Ctrl + D | [Clear](#clearing-all-entries--clear) all entries | `clear` +Ctrl + Q | [Exit](#exiting-the-program--exit) | `exit` +Ctrl + L | [List](#listing-all-tasks-list) all unmarked task by date, earliest task first | `list` +Ctrl + A | [List](#listing-all-tasks-list) archived tasks | `list archived` +Ctrl + P | [List](#listing-all-tasks-list) all unmarked task by priority level, high priority first | `list priorityLevel` +Ctrl + I | [List](#listing-all-tasks-list) all alias keys | `list alias` +Ctrl + H | [Help](#viewing-help--help) | `help` +Ctrl + S | Popups a directory chooser dialog box to choose a new filepath | `storage NEW_FILEPATH` +Ctrl + Z | [Undo](#undo-the-most-recent-operation--undo) | `undo` +Ctrl + Y | [Redo](#redo-the-most-recent-undo-operation--redo) | `redo` +Ctrl + UP | Return [last user input](#command-stack-history) command in command box | - +Ctrl + DOWN | Return (if any) [next user input](#command-stack-history) command in command box | - +Ctrl + LEFT | Displays [previous week’s](#week-selection) daily task list | - +Ctrl + RIGHT | Display [next week’s](#week-selection) daily task list | - diff --git a/docs/images/Ui.png b/docs/images/Ui.png index 4ae9de18c2d9..5ac53387fb40 100644 Binary files a/docs/images/Ui.png and b/docs/images/Ui.png differ diff --git a/docs/images/UiClassDiagram.png b/docs/images/UiClassDiagram.png index af140c15285f..dd8be5c36d4e 100644 Binary files a/docs/images/UiClassDiagram.png and b/docs/images/UiClassDiagram.png differ diff --git a/src/main/java/seedu/savvytasker/commons/events/ui/ShowCheatsheetEvent.java b/src/main/java/seedu/savvytasker/commons/events/ui/ShowCheatsheetEvent.java new file mode 100644 index 000000000000..30ffd9d6605e --- /dev/null +++ b/src/main/java/seedu/savvytasker/commons/events/ui/ShowCheatsheetEvent.java @@ -0,0 +1,14 @@ +//@@author A0138431L + +package seedu.savvytasker.commons.events.ui; + +import seedu.savvytasker.commons.events.BaseEvent; + +/** Indicates cheatsheet display has been toggled */ +public class ShowCheatsheetEvent extends BaseEvent { + @Override + public String toString() { + return "Cheatsheet display has been toggled"; + } + +} diff --git a/src/main/java/seedu/savvytasker/commons/events/ui/WeekSelectionChangedEvent.java b/src/main/java/seedu/savvytasker/commons/events/ui/WeekSelectionChangedEvent.java new file mode 100644 index 000000000000..dc74c7311f14 --- /dev/null +++ b/src/main/java/seedu/savvytasker/commons/events/ui/WeekSelectionChangedEvent.java @@ -0,0 +1,16 @@ +//@@author A0138431L + +package seedu.savvytasker.commons.events.ui; + +import seedu.savvytasker.commons.events.BaseEvent; + +/** Indicates the SavvyTasker in the model has changed*/ + +public class WeekSelectionChangedEvent extends BaseEvent { + + @Override + public String toString() { + return "Selected week has been changed"; + } + +} \ No newline at end of file diff --git a/src/main/java/seedu/savvytasker/logic/Logic.java b/src/main/java/seedu/savvytasker/logic/Logic.java index 1cf9b6cba531..0250b878c806 100755 --- a/src/main/java/seedu/savvytasker/logic/Logic.java +++ b/src/main/java/seedu/savvytasker/logic/Logic.java @@ -27,9 +27,6 @@ public interface Logic { /** */ boolean canParseHeader(String keyword); - /** Returns the list of tasks that are overdue */ - ObservableList getFilteredOverdueTasks(); - /** Returns the list of floating tasks */ ObservableList getFilteredFloatingTasks(); diff --git a/src/main/java/seedu/savvytasker/logic/LogicManager.java b/src/main/java/seedu/savvytasker/logic/LogicManager.java index b7c4231b2213..69a3d1ffdb54 100755 --- a/src/main/java/seedu/savvytasker/logic/LogicManager.java +++ b/src/main/java/seedu/savvytasker/logic/LogicManager.java @@ -114,10 +114,6 @@ public ObservableList getAliasSymbolList() { //@@author //@@author A0138431L - @Override - public ObservableList getFilteredOverdueTasks() { - return model.getFilteredOverdueTasks(); - } @Override public ObservableList getFilteredFloatingTasks() { diff --git a/src/main/java/seedu/savvytasker/logic/commands/StorageAndModelRequiringCommand.java b/src/main/java/seedu/savvytasker/logic/commands/StorageAndModelRequiringCommand.java index fd18f92fa260..88f26a7f3072 100644 --- a/src/main/java/seedu/savvytasker/logic/commands/StorageAndModelRequiringCommand.java +++ b/src/main/java/seedu/savvytasker/logic/commands/StorageAndModelRequiringCommand.java @@ -3,7 +3,7 @@ import seedu.savvytasker.model.Model; import seedu.savvytasker.storage.Storage; -//@@author A0139915W +//@@author A0138431L /** * Represents a command which requires the Storage class as a dependency. * Commands should inherit this class if they only require dependency for diff --git a/src/main/java/seedu/savvytasker/model/Model.java b/src/main/java/seedu/savvytasker/model/Model.java index bd52e32918b4..fd42b55aae28 100644 --- a/src/main/java/seedu/savvytasker/model/Model.java +++ b/src/main/java/seedu/savvytasker/model/Model.java @@ -83,9 +83,6 @@ public interface Model { int getAliasSymbolCount(); //@@author A0138431L - /** Returns the filtered task list of overdue task as an {@code UnmodifiableObservableList} - * as of current date */ - UnmodifiableObservableList getFilteredOverdueTasks(); /** Returns the filtered task list of floating task as an {@code UnmodifiableObservableList} */ UnmodifiableObservableList getFilteredFloatingTasks(); @@ -97,9 +94,6 @@ public interface Model { /** Returns the filtered task list of upcoming task as an {@code UnmodifiableObservableList} * as of expected date */ UnmodifiableObservableList getFilteredUpcomingTasks(Date date); - - /** Updates the filter of the filtered task list to show all overdue tasks */ - void updateFilteredListToShowOverdue(); /** Updates the filter of the filtered task list to show all floating tasks */ void updateFilteredListToShowFloating(); @@ -109,5 +103,6 @@ public interface Model { /** Updates the filter of the filtered task list to show all upcoming tasks after the selected week*/ void updateFilteredListToShowUpcoming(); + //@@author } diff --git a/src/main/java/seedu/savvytasker/model/ModelManager.java b/src/main/java/seedu/savvytasker/model/ModelManager.java index 396e49ed67ed..b58f9ae4ec01 100755 --- a/src/main/java/seedu/savvytasker/model/ModelManager.java +++ b/src/main/java/seedu/savvytasker/model/ModelManager.java @@ -45,8 +45,6 @@ public class ModelManager extends ComponentManager implements Model { private final SortedList sortedAndFilteredTasks; private final FilteredList filteredFloatingTasks; private final SortedList sortedAndFilteredFloatingTasks; - private final FilteredList filteredOverdueTasks; - private final SortedList sortedAndFilteredOverdueTasks; private final FilteredList filteredDay1Tasks; private final SortedList sortedAndFilteredDay1Tasks; private final FilteredList filteredDay2Tasks; @@ -81,9 +79,6 @@ public ModelManager(SavvyTasker src) { filteredFloatingTasks = new FilteredList<>(savvyTasker.getTasks()); sortedAndFilteredFloatingTasks = new SortedList<>(filteredFloatingTasks, new TaskSortedByDefault()); - filteredOverdueTasks = new FilteredList<>(savvyTasker.getTasks()); - sortedAndFilteredOverdueTasks = new SortedList<>(filteredOverdueTasks, new TaskSortedByDefault()); - filteredDay1Tasks = new FilteredList<>(savvyTasker.getTasks()); sortedAndFilteredDay1Tasks = new SortedList<>(filteredDay1Tasks, new TaskSortedByDefault()); filteredDay2Tasks = new FilteredList<>(savvyTasker.getTasks()); @@ -117,9 +112,6 @@ public ModelManager(ReadOnlySavvyTasker initialData) { filteredFloatingTasks = new FilteredList<>(savvyTasker.getTasks()); sortedAndFilteredFloatingTasks = new SortedList<>(filteredFloatingTasks, new TaskSortedByDefault()); - filteredOverdueTasks = new FilteredList<>(savvyTasker.getTasks()); - sortedAndFilteredOverdueTasks = new SortedList<>(filteredOverdueTasks, new TaskSortedByDefault()); - filteredDay1Tasks = new FilteredList<>(savvyTasker.getTasks()); sortedAndFilteredDay1Tasks = new SortedList<>(filteredDay1Tasks, new TaskSortedByDefault()); filteredDay2Tasks = new FilteredList<>(savvyTasker.getTasks()); @@ -293,11 +285,6 @@ private void updateFilteredTaskList(Expression expression, Comparator comp //@author A0138431L //Get filtered task list according to date category - @Override - public UnmodifiableObservableList getFilteredOverdueTasks() { - updateFilteredListToShowOverdue(); - return new UnmodifiableObservableList(filteredOverdueTasks); - } @Override public UnmodifiableObservableList getFilteredFloatingTasks() { @@ -336,20 +323,6 @@ public UnmodifiableObservableList getFilteredUpcomingTasks(Date da return new UnmodifiableObservableList(filteredUpcomingTasks); } - //Binding isOverdue quantifier predicate to filtered list - @Override - public void updateFilteredListToShowOverdue() { - updateFilteredOverdueTaskList(new PredicateExpression(new TaskIsOverdueQualifier())); - } - - private void updateFilteredOverdueTaskList(Expression expression) { - updateFilteredOverdueTaskList(expression, new TaskSortedByDefault()); - } - - private void updateFilteredOverdueTaskList(Expression expression, Comparator comparator) { - filteredOverdueTasks.setPredicate(expression::satisfies); - sortedAndFilteredOverdueTasks.setComparator(comparator); - } //Binding isFloating quantifier predicate to filtered list @Override diff --git a/src/main/java/seedu/savvytasker/model/task/ReadOnlyTask.java b/src/main/java/seedu/savvytasker/model/task/ReadOnlyTask.java index a8648b5cfe9f..d29526f9e35a 100644 --- a/src/main/java/seedu/savvytasker/model/task/ReadOnlyTask.java +++ b/src/main/java/seedu/savvytasker/model/task/ReadOnlyTask.java @@ -1,7 +1,10 @@ package seedu.savvytasker.model.task; +import java.text.SimpleDateFormat; import java.util.Date; +import org.apache.commons.lang.time.DateUtils; + //@@author A0139915W /** * A read-only immutable interface for a Task in the TaskList. @@ -74,14 +77,8 @@ default String getAsText() { */ default String getTextForUi() { final StringBuilder builder = new StringBuilder(); - if (getStartDateTime() != null) { - builder.append(" Start: ") - .append(getStartDateTime()) - .append("\n"); - } - if (getEndDateTime() != null) { - builder.append(" End: ") - .append(getEndDateTime()) + if (getStartDateTime() != null || getEndDateTime() != null) { + builder.append(generateDateTime(getStartDateTime(), getEndDateTime())) .append("\n"); } if (getLocation() != null && !getLocation().isEmpty()) { @@ -89,9 +86,6 @@ default String getTextForUi() { .append(getLocation()) .append("\n"); } - builder.append(" Priority: ") - .append(getPriority()) - .append("\n"); if (getCategory() != null && !getCategory().isEmpty()) { builder.append(" Category: ") .append(getCategory()) @@ -104,6 +98,103 @@ default String getTextForUi() { } return builder.toString(); } + //@@author + + //@@author A0138431L + static final String EMPTY_FIELD = " "; + + static String DATE_PATTERN = "dd MMM yy"; + static String TIME_PATTERN = "hh:mm a"; + + // String format for deadline tasks dateTime format + static String DEADLINE_FORMAT = "by %1$s, %2$s"; + + // String format for event tasks dateTime format + static String EVENT_DIFF_START_END_DATE_FORMAT = "%1$s, %2$s to %3$s, %4$s"; + static String EVENT_SAME_START_END_DATE_FORMAT = "%1$s, %2$s to %3$s"; + + static Date lastDayOfSelectedWeek = new Date(); + + /** + * Generates the DateTime Format for all tasks with time. + * + * @param task the task to have its DateTime Format generated + * + * @return DateTime Format (e.g. (31 Oct 16, 10:00PM) + **/ + default String generateDateTime(Date start, Date end) { + String dateTimeFormat; + //Floating Task + if(start == null && end == null) { + dateTimeFormat = EMPTY_FIELD; + } + //Deadline Task + else if(start == null && end != null) { + dateTimeFormat = generateDeadlineDateTime(end); + //Event Task + }else { + dateTimeFormat = generateEventDateTime(start, end); + } + + return dateTimeFormat; + + } + + /** + * Generates the dateTimeFormat for deadline tasks + * + * @param task the task to have its dateTimeFormat generated + * + * @return dateTimeFormat (e.g. 30 Oct 16, 10:00PM) + */ + default String generateDeadlineDateTime(Date end) { + + String dateTimeFormat; + + SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + SimpleDateFormat timeFormatter = new SimpleDateFormat(TIME_PATTERN); + + String taskEndDateFormat = dateFormatter.format(end.getTime()); + String taskEndTimeFormat = timeFormatter.format(end.getTime()); + + dateTimeFormat = String.format(DEADLINE_FORMAT, taskEndDateFormat, taskEndTimeFormat); + + return dateTimeFormat; + + } + + /** + * Generates the dateTimeFormat for ranged tasks + * + * @param task the task to have its dateTimeFormat generated + * + * @return dateTimeFormat (e.g. 30 Oct 16, 8:00AM to 9:00PM) + */ + default String generateEventDateTime(Date start, Date end) { + + String dateTimeFormat; + + SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + SimpleDateFormat timeFormatter = new SimpleDateFormat(TIME_PATTERN); + + String taskStartDateFormat = dateFormatter.format(start.getTime()); + String taskStartTimeFormat = timeFormatter.format(start.getTime()); + + String taskEndDateFormat = dateFormatter.format(end.getTime()); + String taskEndTimeFormat = timeFormatter.format(end.getTime()); + + if(DateUtils.isSameDay(start, end) == false) { + + dateTimeFormat = String.format(EVENT_DIFF_START_END_DATE_FORMAT, taskStartDateFormat, taskStartTimeFormat, taskEndDateFormat, taskEndTimeFormat); + + } else { + + dateTimeFormat = String.format(EVENT_SAME_START_END_DATE_FORMAT, taskEndDateFormat, taskStartTimeFormat, taskEndTimeFormat); + + } + return dateTimeFormat; + + } } //@@author diff --git a/src/main/java/seedu/savvytasker/ui/CommandBox.java b/src/main/java/seedu/savvytasker/ui/CommandBox.java index e2d5d3c086d3..1f5e2eb15ecf 100755 --- a/src/main/java/seedu/savvytasker/ui/CommandBox.java +++ b/src/main/java/seedu/savvytasker/ui/CommandBox.java @@ -2,6 +2,8 @@ package seedu.savvytasker.ui; import java.io.File; +import java.util.Calendar; +import java.util.Date; import java.util.Stack; import java.util.logging.Logger; @@ -11,6 +13,8 @@ import javafx.scene.Node; import javafx.scene.control.SplitPane; import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyCombination; @@ -19,7 +23,10 @@ import javafx.stage.DirectoryChooser; import javafx.stage.Stage; import seedu.savvytasker.commons.core.LogsCenter; +import seedu.savvytasker.commons.events.model.SavvyTaskerChangedEvent; import seedu.savvytasker.commons.events.ui.IncorrectCommandAttemptedEvent; +import seedu.savvytasker.commons.events.ui.ShowCheatsheetEvent; +import seedu.savvytasker.commons.events.ui.WeekSelectionChangedEvent; import seedu.savvytasker.commons.util.FxViewUtil; import seedu.savvytasker.logic.Logic; import seedu.savvytasker.logic.commands.CommandResult; @@ -32,6 +39,32 @@ public class CommandBox extends UiPart { private AnchorPane commandPane; private ResultDisplay resultDisplay; String previousCommandTest; + private Date date = new Date(); + private static int DAYS_OF_WEEK = 7; + + private final String UNDO_COMMAND = "undo"; + private final String REDO_COMMAND = "redo"; + private final String HELP_COMMAND = "help"; + private final String EXIT_COMMAND = "exit"; + private final String LIST_COMMAND = "list"; + private final String LIST_ARCHIVED_COMMAND = "list archived"; + private final String LIST_PRIORITY_COMMAND = "list priorityLevel"; + private final String LIST_ALIAS_COMMAND = "list alias"; + private final String CLEAR_COMMAND = "clear"; + private final String STORAGE_COMMAND = "storage ."; + + KeyCombination saveKey = new KeyCodeCombination(KeyCode.S, KeyCombination.META_DOWN); + KeyCombination undoKey = new KeyCodeCombination(KeyCode.Z, KeyCombination.META_DOWN); + KeyCombination redoKey = new KeyCodeCombination(KeyCode.Y, KeyCombination.META_DOWN); + KeyCombination helpKey = new KeyCodeCombination(KeyCode.H, KeyCombination.META_DOWN); + KeyCombination exitKey = new KeyCodeCombination(KeyCode.Q, KeyCombination.META_DOWN); + KeyCombination listKey = new KeyCodeCombination(KeyCode.L, KeyCombination.META_DOWN); + KeyCombination listArchivedKey = new KeyCodeCombination(KeyCode.A, KeyCombination.META_DOWN); + KeyCombination listPriorityKey = new KeyCodeCombination(KeyCode.P, KeyCombination.META_DOWN); + KeyCombination listAliasKey = new KeyCodeCombination(KeyCode.I, KeyCombination.META_DOWN); + KeyCombination clearKey = new KeyCodeCombination(KeyCode.D, KeyCombination.META_DOWN); + KeyCombination leftKey = new KeyCodeCombination(KeyCode.LEFT, KeyCombination.META_DOWN); + KeyCombination rightKey = new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.META_DOWN); // stack to store commands history private static Stack COMMAND_HISTORY_STACK = new Stack(); @@ -139,15 +172,6 @@ public void commandTextFieldOnKeyPressedHandler(KeyEvent keyEvent) { String userInput = commandTextField.getText().trim(); - KeyCombination saveKey = new KeyCodeCombination(KeyCode.S, KeyCombination.META_DOWN); - KeyCombination undoKey = new KeyCodeCombination(KeyCode.Z, KeyCombination.META_DOWN); - KeyCombination redoKey = new KeyCodeCombination(KeyCode.Y, KeyCombination.META_DOWN); - KeyCombination helpKey = new KeyCodeCombination(KeyCode.H, KeyCombination.META_DOWN); - KeyCombination exitKey = new KeyCodeCombination(KeyCode.Q, KeyCombination.META_DOWN); - KeyCombination listKey = new KeyCodeCombination(KeyCode.L, KeyCombination.META_DOWN); - KeyCombination listArchivedKey = new KeyCodeCombination(KeyCode.A, KeyCombination.META_DOWN); - KeyCombination clearKey = new KeyCodeCombination(KeyCode.D, KeyCombination.META_DOWN); - try { KeyCode keyCode = keyEvent.getCode(); @@ -158,10 +182,9 @@ public void commandTextFieldOnKeyPressedHandler(KeyEvent keyEvent) { }else if (keyCode == KeyCode.ESCAPE) { - //close help dialog - //processEsc(); + showCheatsheet(); - }else if (keyCode == KeyCode.UP) { + } else if (keyCode == KeyCode.UP) { processUp(userInput); @@ -169,33 +192,49 @@ public void commandTextFieldOnKeyPressedHandler(KeyEvent keyEvent) { processDown(userInput); + } else if (leftKey.match(keyEvent)) { + + processDate(-1); + + } else if (rightKey.match(keyEvent)) { + + processDate(1); + } else if (undoKey.match(keyEvent)) { - executeCommand("undo"); + executeCommand(UNDO_COMMAND); } else if (redoKey.match(keyEvent)) { - executeCommand("redo"); + executeCommand(REDO_COMMAND); } else if (helpKey.match(keyEvent)) { - executeCommand("help"); + executeCommand(HELP_COMMAND); } else if (exitKey.match(keyEvent)) { - executeCommand("exit"); + executeCommand(EXIT_COMMAND); } else if (listKey.match(keyEvent)) { - executeCommand("list"); + executeCommand(LIST_COMMAND); } else if (listArchivedKey.match(keyEvent)) { - executeCommand("list t/Archived"); + executeCommand(LIST_ARCHIVED_COMMAND); + + } else if (listPriorityKey.match(keyEvent)) { + + executeCommand(LIST_PRIORITY_COMMAND); + + } else if (listAliasKey.match(keyEvent)) { + + executeCommand(LIST_ALIAS_COMMAND); } else if (clearKey.match(keyEvent)) { - executeCommand("clear"); + executeCommand(CLEAR_COMMAND); } @@ -229,23 +268,10 @@ public void processSave() { DirectoryChooser directoryChooser = new DirectoryChooser(); File selectedFile = directoryChooser.showDialog(primaryStage); String filepath = selectedFile.getAbsolutePath(); - executeCommand("save " + filepath); + executeCommand(STORAGE_COMMAND + filepath + "/savvytasker.xml"); } - /** - * Process the event that occurs after the user presses the [ESC] button. - * - * @param userInput the command keyed in by the user. - - public void processEsc() { - - if (userInput.equals("") && isHelpViewVisible()) { - - hideHelpView(); - - } - /** * Process the event that occurs after the user presses the [UP] button. * @@ -296,6 +322,27 @@ public void processDown(String userInput) { } + /** + * Process the event that occurs after the user presses the left or right button. + * + * @param numbers of week to be added to the current selected week to be displayed in the daily task list view + */ + public void processDate(int numberOfWeek) { + + date = addWeek(numberOfWeek, date); + indicateWeekSelectionChanged(); + } + + /** Raises an event to indicate the week to be displayed has changed */ + private void indicateWeekSelectionChanged() { + raise(new WeekSelectionChangedEvent()); + } + + /** Raises an event to indicate the week to be displayed has changed */ + private void showCheatsheet() { + raise(new ShowCheatsheetEvent()); + } + /** * Execute commands * @@ -306,5 +353,23 @@ public void executeCommand(String commandInput) { resultDisplay.postMessage(commandResult.feedbackToUser); logger.info("Result: " + commandResult.feedbackToUser); } + + private Date addWeek(int numberOfWeek, Date date) { + + //convert date object to calendar object and add 1 days + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(date); + + calendarExpectedDate.add(Calendar.DATE, (numberOfWeek*DAYS_OF_WEEK)); + + //convert calendar object back to date object + date = calendarExpectedDate.getTime(); + + return date; + } + + public Date getDate() { + return date; + } } diff --git a/src/main/java/seedu/savvytasker/ui/DailyPanel.java b/src/main/java/seedu/savvytasker/ui/DailyPanel.java index d94c16d0f800..a252b9c33ea2 100644 --- a/src/main/java/seedu/savvytasker/ui/DailyPanel.java +++ b/src/main/java/seedu/savvytasker/ui/DailyPanel.java @@ -1,22 +1,27 @@ package seedu.savvytasker.ui; import java.text.SimpleDateFormat; +import java.util.Calendar; import java.util.Date; import java.util.logging.Logger; +import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang.time.DateUtils; + import javafx.application.Platform; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.Node; -import javafx.scene.control.Label; import javafx.scene.control.ListCell; import javafx.scene.control.ListView; import javafx.scene.control.SplitPane; +import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import seedu.savvytasker.commons.core.LogsCenter; import seedu.savvytasker.commons.events.ui.TaskPanelSelectionChangedEvent; +import seedu.savvytasker.commons.util.FxViewUtil; import seedu.savvytasker.model.task.ReadOnlyTask; //@@author A0138431L @@ -39,8 +44,9 @@ public class DailyPanel extends UiPart { private VBox panel; private AnchorPane placeHolderPane; + @FXML - private Label dayHeader; + private TextField header; @FXML private ListView taskListView; @@ -75,12 +81,19 @@ public static DailyPanel load(Stage primaryStage, AnchorPane DailyListPlaceholde private void configure(ObservableList taskList, int dayOfTheWeek, Date date) { String dateHeader = generateHeader(dayOfTheWeek, date); + Date today = new Date(); + if(date == today) { + placeHolderPane.setStyle("-fx-background-color:#FF0000"); + header.setStyle("-fx-text-fill:#FF0000"); + } setConnections(taskList, dateHeader); addToPlaceholder(); + } private void setConnections(ObservableList taskList, String dateHeader) { - dayHeader.setText(dateHeader); + header.clear(); + header.setText(dateHeader); taskListView.setItems(taskList); taskListView.setCellFactory(listView -> new TaskListViewCell()); setEventHandlerForSelectionChangeEvent(); @@ -112,30 +125,43 @@ private String generateHeader(int dayOfTheWeek, Date date) { SimpleDateFormat dayFormatter = new SimpleDateFormat(DAY_PATTERN); SimpleDateFormat dateFormatter = new SimpleDateFormat(DATE_PATTERN); + Date today = new Date(); + Date tomorrow = new Date(); + tomorrow = addDay(1, tomorrow); + String day; - switch (dayOfTheWeek) { - - case 0: + if(DateUtils.isSameDay(date, today)) { day = TODAY_TITLE; - break; - - case 1: + + } else if (DateUtils.isSameDay(date, tomorrow)) { day = TOMORROW_TITLE; - break; - - default: + + } else { day = dayFormatter.format(date); - break; } String header = String.format(DAY_DATE_FORMAT, day, dateFormatter.format(date)); return header; } + + private Date addDay(int i, Date date) { + + //convert date object to calendar object and add 1 days + Calendar calendarExpectedDate = Calendar.getInstance(); + calendarExpectedDate.setTime(date); + + calendarExpectedDate.add(Calendar.DATE, i); + + //convert calendar object back to date object + date = calendarExpectedDate.getTime(); + + return date; + } class TaskListViewCell extends ListCell { @@ -150,7 +176,7 @@ protected void updateItem(ReadOnlyTask task, boolean empty) { setGraphic(null); setText(null); } else { - setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); + setGraphic(TaskCard.load(task, 0, false).getLayout()); } } } diff --git a/src/main/java/seedu/savvytasker/ui/FloatingPanel.java b/src/main/java/seedu/savvytasker/ui/FloatingPanel.java index dd32e6abe677..2074179901d2 100644 --- a/src/main/java/seedu/savvytasker/ui/FloatingPanel.java +++ b/src/main/java/seedu/savvytasker/ui/FloatingPanel.java @@ -104,7 +104,7 @@ protected void updateItem(ReadOnlyTask task, boolean empty) { setGraphic(null); setText(null); } else { - setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); + setGraphic(TaskCard.load(task, 0, false).getLayout()); } } } diff --git a/src/main/java/seedu/savvytasker/ui/MainWindow.java b/src/main/java/seedu/savvytasker/ui/MainWindow.java index c1eb8c6a6c5f..4f6a94758057 100755 --- a/src/main/java/seedu/savvytasker/ui/MainWindow.java +++ b/src/main/java/seedu/savvytasker/ui/MainWindow.java @@ -12,6 +12,8 @@ import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.MenuItem; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; import javafx.scene.input.KeyCombination; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; @@ -22,6 +24,8 @@ import seedu.savvytasker.commons.core.LogsCenter; import seedu.savvytasker.commons.events.model.SavvyTaskerChangedEvent; import seedu.savvytasker.commons.events.ui.ExitAppRequestEvent; +import seedu.savvytasker.commons.events.ui.ShowCheatsheetEvent; +import seedu.savvytasker.commons.events.ui.WeekSelectionChangedEvent; import seedu.savvytasker.logic.Logic; import seedu.savvytasker.model.UserPrefs; import seedu.savvytasker.model.task.ReadOnlyTask; @@ -31,11 +35,10 @@ * a sorting and filtered list that display the result of the user command * on the left and a week's view of the task * - * The week's view contains 4 lists, namely the floating list, overdue list, + * The week's view contains 4 lists, namely the floating list, * days of the week list and upcoming list * * Floating list contains task without start and end dateTime - * Overdue list contains task with end date before current date * Days of the week list contains task that falls on the respective day of the selected week * Upcoming list contains task with start date after the last day of selected week * @@ -44,14 +47,18 @@ */ public class MainWindow extends UiPart { - private static final String ICON = "/images/address_book_32.png"; + private static final String ICON = "/images/savvytasker-icon.png"; + private static final Image image = new Image(MainWindow.class.getResourceAsStream(ICON)); + private static final String CHEATSHEET = "/images/cheatsheet.png"; + private static final Image imageOverlay = new Image(MainWindow.class.getResourceAsStream(CHEATSHEET)); private static final String FXML = "MainWindow.fxml"; public static final int MIN_HEIGHT = 700; public static final int MIN_WIDTH = 1150; private Logic logic; - Date date = new Date(); - private static int DAYS_OF_WEEK = 7; + Date firstDayOfSelectedWeek = new Date(); + private static int DAYS_OF_WEEK = 7; + private boolean isShown = false; // Independent Ui parts residing in this Ui container //private BrowserPanel browserPanel; @@ -65,8 +72,6 @@ public class MainWindow extends UiPart { @FXML private FloatingPanel floatingPanel; @FXML - private OverduePanel overduePanel; - @FXML private DailyPanel dailyPanel; @FXML private UpcomingPanel upcomingPanel; @@ -84,7 +89,10 @@ public class MainWindow extends UiPart { private AnchorPane commandBoxPlaceholder; @FXML - private MenuItem helpMenuItem; + private ImageView imageIcon; + + @FXML + private ImageView cheatsheet; @FXML private AnchorPane taskListPanelPlaceholder; @@ -101,13 +109,9 @@ public class MainWindow extends UiPart { @FXML private VBox listPanel; - @FXML private AnchorPane floatingPanelPlaceholder; - @FXML - private AnchorPane overduePanelPlaceholder; - @FXML private AnchorPane day1PanelPlaceholder; @FXML @@ -122,7 +126,7 @@ public class MainWindow extends UiPart { private AnchorPane day6PanelPlaceholder; @FXML private AnchorPane day7PanelPlaceholder; - + @FXML private AnchorPane upcomingPanelPlaceholder; @@ -168,25 +172,25 @@ private void configure(String appTitle, String addressBookName, Config config, U } void fillInnerParts() { - //browserPanel = BrowserPanel.load(browserPlaceholder); + imageIcon.setImage(image); taskListPanel = TaskListPanel.load(primaryStage, getTaskListPlaceholder(), logic.getFilteredTaskList()); aliasSymbolListPanel = AliasSymbolListPanel.load(primaryStage, getAliasSymbolPlaceholder(), logic.getAliasSymbolList()); setDefaultView(); - resultDisplay = ResultDisplay.load(primaryStage, getResultDisplayPlaceholder()); statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), config.getSavvyTaskerFilePath()); commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), resultDisplay, logic); commandBox.getCommandTextField().requestFocus(); floatingPanel = FloatingPanel.load(primaryStage, getFloatingPanelPlaceholder(), logic.getFilteredFloatingTasks()); - overduePanel = OverduePanel.load(primaryStage, getOverduePanelPlaceholder(), logic.getFilteredOverdueTasks()); loadDailyPanel(); - upcomingPanel = UpcomingPanel.load(primaryStage, getUpcomingPanelPlaceholder(), logic.getFilteredUpcomingTasks(date)); + upcomingPanel = UpcomingPanel.load(primaryStage, getUpcomingPanelPlaceholder(), logic.getFilteredUpcomingTasks(firstDayOfSelectedWeek)); + cheatsheet.setImage(imageOverlay); } private void loadDailyPanel() { + firstDayOfSelectedWeek = commandBox.getDate(); for (int i = 0; i < DAYS_OF_WEEK; i++) { Date onDate = new Date(); - onDate.setTime(date.getTime()); + onDate.setTime(firstDayOfSelectedWeek.getTime()); onDate = addDay(i, onDate); dailyPanel = DailyPanel.load(primaryStage, getDailyPanelPlaceholder(i), logic.getFilteredDailyTasks(i, onDate), i, onDate); @@ -221,6 +225,10 @@ private VBox getListPanel() { return listPanel; } + private VBox getRootLayout() { + return rootLayout; + } + private AnchorPane getCommandBoxPlaceholder() { return commandBoxPlaceholder; } @@ -245,10 +253,6 @@ private AnchorPane getFloatingPanelPlaceholder() { return floatingPanelPlaceholder; } - private AnchorPane getOverduePanelPlaceholder() { - return overduePanelPlaceholder; - } - private AnchorPane getDailyPanelPlaceholder(int index) { switch(index) { @@ -276,14 +280,12 @@ private AnchorPane getDailyPanelPlaceholder(int index) { case 5: return day6PanelPlaceholder; - + case 6: + default: return day7PanelPlaceholder; - default: - - return day1PanelPlaceholder; } } @@ -370,18 +372,36 @@ public TaskListPanel getTaskListPanel() { return this.taskListPanel; } - public void loadPersonPage(ReadOnlyTask task) { + //feature removed //browserPanel.loadPersonPage(task); } public void releaseResources() { - //browserPanel.freeResources(); + //feature removed + //browserPanel.freeResources(); } @Subscribe public void handleSavvyTaskerChangedEvent(SavvyTaskerChangedEvent stce) { loadDailyPanel(); } + + @Subscribe + public void handleWeekSelectionChangedEvent(WeekSelectionChangedEvent stce) { + loadDailyPanel(); + } + + @Subscribe + public void handleCheatsheetDisplayToggledEvent(ShowCheatsheetEvent stce) { + + if(isShown == false) { + cheatsheet.setVisible(true); + isShown = true; + } else { + cheatsheet.setVisible(false); + isShown = false; + } + } } \ No newline at end of file diff --git a/src/main/java/seedu/savvytasker/ui/OverduePanel.java b/src/main/java/seedu/savvytasker/ui/OverduePanel.java deleted file mode 100644 index b4eba1935b7d..000000000000 --- a/src/main/java/seedu/savvytasker/ui/OverduePanel.java +++ /dev/null @@ -1,112 +0,0 @@ -package seedu.savvytasker.ui; - -import java.util.logging.Logger; - -import javafx.application.Platform; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.control.ListCell; -import javafx.scene.control.ListView; -import javafx.scene.control.SplitPane; -import javafx.scene.layout.AnchorPane; -import javafx.scene.layout.VBox; -import javafx.stage.Stage; -import seedu.savvytasker.commons.core.LogsCenter; -import seedu.savvytasker.commons.events.ui.TaskPanelSelectionChangedEvent; -import seedu.savvytasker.model.task.ReadOnlyTask; - -//@@author A0138431L - -/** -* Panel containing the list overdue task. -* @author A0138431L -* -*/ -public class OverduePanel extends UiPart { - private final Logger logger = LogsCenter.getLogger(TaskListPanel.class); - private static final String FXML = "OverdueList.fxml"; - private VBox panel; - private AnchorPane placeHolderPane; - - @FXML - private ListView taskListView; - - public OverduePanel() { - super(); - } - - @Override - public void setNode(Node node) { - panel = (VBox) node; - } - - @Override - public String getFxmlPath() { - return FXML; - } - - @Override - public void setPlaceholder(AnchorPane pane) { - this.placeHolderPane = pane; - } - - public static OverduePanel load(Stage primaryStage, AnchorPane overdueListPlaceholder, - ObservableList taskList) { - OverduePanel oveduePanel = - UiPartLoader.loadUiPart(primaryStage, overdueListPlaceholder, new OverduePanel()); - oveduePanel.configure(taskList); - return oveduePanel; - } - - private void configure(ObservableList taskList) { - setConnections(taskList); - addToPlaceholder(); - } - - private void setConnections(ObservableList taskList) { - taskListView.setItems(taskList); - taskListView.setCellFactory(listView -> new TaskListViewCell()); - setEventHandlerForSelectionChangeEvent(); - } - - private void addToPlaceholder() { - SplitPane.setResizableWithParent(placeHolderPane, false); - placeHolderPane.getChildren().add(panel); - } - - private void setEventHandlerForSelectionChangeEvent() { - taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { - if (newValue != null) { - logger.fine("Selection in overdue task list panel changed to : '" + newValue + "'"); - raise(new TaskPanelSelectionChangedEvent(newValue)); - } - }); - } - - public void scrollTo(int index) { - Platform.runLater(() -> { - taskListView.scrollTo(index); - taskListView.getSelectionModel().clearAndSelect(index); - }); - } - - class TaskListViewCell extends ListCell { - - public TaskListViewCell() { - } - - @Override - protected void updateItem(ReadOnlyTask task, boolean empty) { - super.updateItem(task, empty); - - if (empty || task == null) { - setGraphic(null); - setText(null); - } else { - setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); - } - } - } - -} diff --git a/src/main/java/seedu/savvytasker/ui/TaskCard.java b/src/main/java/seedu/savvytasker/ui/TaskCard.java index d978f9229316..1646d1a1b142 100644 --- a/src/main/java/seedu/savvytasker/ui/TaskCard.java +++ b/src/main/java/seedu/savvytasker/ui/TaskCard.java @@ -1,8 +1,14 @@ +//@@author A0138431L + package seedu.savvytasker.ui; +import java.util.Date; + import javafx.fxml.FXML; 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 seedu.savvytasker.model.task.ReadOnlyTask; @@ -10,10 +16,13 @@ public class TaskCard extends UiPart{ private static final String FXML = "TaskListCard.fxml"; + private static final String ICON = "/images/overdue.png"; + private static final Image OVERDUE_IMAGE = new Image(MainWindow.class.getResourceAsStream(ICON)); + public static final String LOW_PRIORITY_BACKGROUND = "-fx-background-color:#CEFFDC"; public static final String MEDIUM_PRIORITY_BACKGROUND = "-fx-background-color:#FFFED8"; public static final String HIGH_PRIORITY_BACKGROUND = "-fx-background-color:#FF8180"; - + @FXML private HBox cardPane; @FXML @@ -22,12 +31,19 @@ public class TaskCard extends UiPart{ private Label id; @FXML private Label details; + @FXML + private ImageView overdueIcon; + private boolean isShowingIndex; private ReadOnlyTask task; private int displayedIndex; + + public TaskCard(boolean isShowingIndex){ + this.isShowingIndex = isShowingIndex; + } - public static TaskCard load(ReadOnlyTask task, int displayedIndex){ - TaskCard card = new TaskCard(); + public static TaskCard load(ReadOnlyTask task, int displayedIndex, boolean isShowingIndex){ + TaskCard card = new TaskCard(isShowingIndex); card.task = task; card.displayedIndex = displayedIndex; return UiPartLoader.loadUiPart(card); @@ -35,16 +51,36 @@ public static TaskCard load(ReadOnlyTask task, int displayedIndex){ @FXML public void initialize() { - taskName.setText(task.getTaskName()); - id.setText(displayedIndex + ". "); + + taskName.setText(task.getTaskName()); + if (isShowingIndex) { + id.setText(displayedIndex + ". "); + } details.setText(task.getTextForUi()); setCardBackground(); + setOverdue(); + } public HBox getLayout() { return cardPane; } + private void setOverdue() { + + Date today = new Date(); + + if (task.getEndDateTime() != null) { + + Date endDateTime = task.getEndDateTime(); + + if (endDateTime.compareTo(today)<0 && task.isArchived() == false) { + + overdueIcon.setImage(OVERDUE_IMAGE); + } + } + + } private void setCardBackground() { if (task.getPriority().toString().equals("High")) { diff --git a/src/main/java/seedu/savvytasker/ui/TaskListPanel.java b/src/main/java/seedu/savvytasker/ui/TaskListPanel.java index 56b053ec13b1..659260270feb 100644 --- a/src/main/java/seedu/savvytasker/ui/TaskListPanel.java +++ b/src/main/java/seedu/savvytasker/ui/TaskListPanel.java @@ -58,6 +58,7 @@ public static TaskListPanel load(Stage primaryStage, AnchorPane personListPlaceh private void configure(ObservableList taskList) { setConnections(taskList); addToPlaceholder(); + setBackground(); } private void setConnections(ObservableList taskList) { @@ -70,8 +71,17 @@ private void addToPlaceholder() { SplitPane.setResizableWithParent(placeHolderPane, false); placeHolderPane.getChildren().add(panel); } + + private void setBackground() { + if (taskListView.getItems().size() > 0) { + taskListView.setStyle("-fx-background-color: transparent"); + } else { + taskListView.setStyle("-fx-background-color: white"); + } + } private void setEventHandlerForSelectionChangeEvent() { + setBackground(); taskListView.getSelectionModel().selectedItemProperty().addListener((observable, oldValue, newValue) -> { if (newValue != null) { logger.fine("Selection in task list panel changed to : '" + newValue + "'"); @@ -97,7 +107,7 @@ protected void updateItem(ReadOnlyTask task, boolean empty) { setGraphic(null); setText(null); } else { - setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); + setGraphic(TaskCard.load(task, getIndex() + 1, true).getLayout()); } } } diff --git a/src/main/java/seedu/savvytasker/ui/UpcomingPanel.java b/src/main/java/seedu/savvytasker/ui/UpcomingPanel.java index 6c374a4410d7..7862582b15fa 100644 --- a/src/main/java/seedu/savvytasker/ui/UpcomingPanel.java +++ b/src/main/java/seedu/savvytasker/ui/UpcomingPanel.java @@ -104,7 +104,7 @@ protected void updateItem(ReadOnlyTask task, boolean empty) { setGraphic(null); setText(null); } else { - setGraphic(TaskCard.load(task, getIndex() + 1).getLayout()); + setGraphic(TaskCard.load(task, 0, false).getLayout()); } } } diff --git a/src/main/resources/images/cheatsheet.png b/src/main/resources/images/cheatsheet.png new file mode 100644 index 000000000000..4c4c2bbfdc0b Binary files /dev/null and b/src/main/resources/images/cheatsheet.png differ diff --git a/src/main/resources/images/overdue.png b/src/main/resources/images/overdue.png new file mode 100644 index 000000000000..ed1b6b13e27a Binary files /dev/null and b/src/main/resources/images/overdue.png differ diff --git a/src/main/resources/images/savvytasker-icon.png b/src/main/resources/images/savvytasker-icon.png new file mode 100644 index 000000000000..a809380abe90 Binary files /dev/null and b/src/main/resources/images/savvytasker-icon.png differ diff --git a/src/main/resources/view/DailyList.fxml b/src/main/resources/view/DailyList.fxml index 0ad3c5149b13..6a359692d8bb 100644 --- a/src/main/resources/view/DailyList.fxml +++ b/src/main/resources/view/DailyList.fxml @@ -1,8 +1,8 @@ - + @@ -12,7 +12,7 @@ - diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 1efb0b72e305..f46f4b2772e4 100755 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -1,3 +1,4 @@ +/*@@author A0138431L */ .background { -fx-background-color: derive(#1d1d1d, 20%); } @@ -94,16 +95,18 @@ .list-cell .label { -fx-text-fill: #010504; + -fx-font-family:'Helvetica Condensed'; } .cell_big_label { - -fx-font-size: 16px; + -fx-font-size: 12px; -fx-text-fill: #010504; + -fx-font-family:'Helvetica Condensed'; } .cell_small_label { - -fx-font-size: 11px; - -fx-text-fill: #010504; + -fx-font-size: 10px; + -fx-text-fill: #696969; } .anchor-pane { @@ -276,10 +279,9 @@ -fx-padding: 8 1 8 1; } -#cardPane { - -fx-background-color: transparent; - -fx-border-color: #d6d6d6; - -fx-border-width: 1 1 1 1; +.cardPane { + -fx-border-color: #ffffff; + -fx-border-radius: 5px; } #commandTypeLabel { @@ -294,29 +296,21 @@ #taskListView { -fx-background-color: transparent; } -/*------------------------------------------ OverduePanel Styling ------------------------------------------*/ -.overdue-scrollpane, .overdue-panel { - -fx-background-color:#DCFFFD; -} - -.overdue-scrollpane > .scroll-bar:horizontal .thumb, -.overdue-scrollpane > .scroll-bar:vertical .thumb { - -fx-background-color:#99534D; -} - -.overdue-panel .title, .overdue-panel .taskname { - -fx-text-fill:#000000; -} - -.overdue-panel .subtitle, .overdue-panel .timestamp { - -fx-text-fill:#B26059; +#header { + fx-font-size: 11px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Condensed'; } /*------------------------------------------ FloatingPanel Styling ------------------------------------------*/ .floating-scrollpane, .floating-panel { -fx-background-color:#ACEDFF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; } .floating-scrollpane { @@ -340,6 +334,10 @@ .daily-scrollpane, .daily-panel { -fx-background-color:#99D3FF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; } .daily-scrollpane > .scroll-bar:horizontal .thumb, @@ -358,6 +356,10 @@ /*------------------------------------------ UpcomingPanel Styling ------------------------------------------*/ .upcoming-scrollpane, .upcoming-panel { -fx-background-color:#D1E0FF; + -fx-border-radius: 15px; + -fx-font-size: 14px; + -fx-text-fill: #000000; + -fx-font-family:'Helvetica Bold'; } .upcoming-scrollpane > .scroll-bar:horizontal .thumb, @@ -395,81 +397,3 @@ .archived-panel .taskname, .archived-panel .timestamp { -fx-text-fill:#FFFFFF; } - -/*------------------------------------------ SearchPanel Styling ------------------------------------------*/ - -.search-view-title { - -fx-font-family:'Helvetica Condensed'; - -fx-font-size:24; - -fx-font-weight:normal; -} - -.search-panel .title { - -fx-font-family:'Helvetica Condensed'; - -fx-font-size:20; - -fx-font-weight:normal; -} - -.search-panel .check-box { - -fx-font-family:'Helvetica'; - -fx-text-fill:#FFFFFF; - -fx-font-size:10; - -fx-font-weight:bold; -} - -.search-panel .taskname, .search-panel .timestamp { - -fx-text-fill:#FFFFFF; -} - -/*------------------------------------------ HelpPanel Styling ------------------------------------------*/ - -.help-panel .title { - -fx-font-family:'Helvetica Bold'; - -fx-font-size:48; - -fx-font-weight:normal; -} - -.help-panel .subtitle { - -fx-font-family:'Helvetica'; - -fx-font-size:13; - -fx-font-weight:bold; -} - -.overlay-scrollpane, .overlay-scrollpane > .viewport { - -fx-background-color:transparent; -} - -.scroll-pane > .scroll-bar:horizontal, -.scroll-pane > .scroll-bar:vertical { - -fx-background-color:transparent; - -fx-background-radius:2em; - -fx-border-radius:2em; -} - -.scorll-pane > .scroll-bar:horizontal .track, -.scroll-pane > .scroll-bar:vertical .track { - -fx-background-color:transparent; - -fx-background-radius:0em; - -fx-border-color:transparent; - -fx-border-radius:2em; -} - -.scroll-pane > .scroll-bar:horizontal .thumb, -.scroll-pane > .scroll-bar:vertical .thumb { - -fx-background-insets:4, 5, 6; - -fx-background-radius:2em; - -fx-border-radius:2em; -} - -.scroll-pane > .scroll-bar > .increment-button, -.scroll-pane > .scroll-bar > .decrement-button { - -fx-background-color:transparent; - -fx-background-radius:0em; - -fx-padding:0 0 10 0; -} - -.scroll-pane > .scroll-bar > .increment-button > .increment-arrow, -.scroll-pane > .scroll-bar > .decrement-button > .decrement-arrow { - -fx-shape:" "; - -fx-padding:0; -} \ No newline at end of file diff --git a/src/main/resources/view/FloatingList.fxml b/src/main/resources/view/FloatingList.fxml index 8d58ccc772b8..b6e353e0d1fc 100644 --- a/src/main/resources/view/FloatingList.fxml +++ b/src/main/resources/view/FloatingList.fxml @@ -1,11 +1,10 @@ - + - @@ -13,11 +12,7 @@ - + diff --git a/src/main/resources/view/MainWindow.fxml b/src/main/resources/view/MainWindow.fxml index 9c39bab158f1..6c3ab490740a 100755 --- a/src/main/resources/view/MainWindow.fxml +++ b/src/main/resources/view/MainWindow.fxml @@ -21,7 +21,7 @@ - + @@ -37,15 +37,14 @@ - + - + - @@ -56,12 +55,7 @@ - - - - - - + @@ -73,56 +67,54 @@ - + + + + + + - + - - - - + - + - + - + - + - + - + - + + + + + + - - - - - - - - - - + diff --git a/src/main/resources/view/OverdueList.fxml b/src/main/resources/view/OverdueList.fxml deleted file mode 100644 index ab5ca805ad79..000000000000 --- a/src/main/resources/view/OverdueList.fxml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml deleted file mode 100755 index 9953ce588beb..000000000000 --- a/src/main/resources/view/PersonListCard.fxml +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListPanel.fxml b/src/main/resources/view/PersonListPanel.fxml deleted file mode 100755 index aa9c30ae99eb..000000000000 --- a/src/main/resources/view/PersonListPanel.fxml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - - - - - - - - diff --git a/src/main/resources/view/TaskListCard.fxml b/src/main/resources/view/TaskListCard.fxml index 987e7b0ec451..7077986c6f44 100755 --- a/src/main/resources/view/TaskListCard.fxml +++ b/src/main/resources/view/TaskListCard.fxml @@ -1,42 +1,38 @@ - - - - + + + + + + + - - - - - - - - - - - - - - + + + + + + + + - - - - - + + + + + - - - - - - - + + + + +