diff --git a/collated/docs/A0116603R.md b/collated/docs/A0116603R.md
new file mode 100644
index 000000000000..a7ccb0f41a18
--- /dev/null
+++ b/collated/docs/A0116603R.md
@@ -0,0 +1,173 @@
+# A0116603R
+###### /DeveloperGuide.md
+``` md
+### Continuous Integration
+
+We use [Travis CI](https://travis-ci.org/) to perform _Continuous Integration_ on our projects.
+See [UsingTravis.md](UsingTravis.md) for more details.
+
+### Making a Release
+
+Here are the steps to create a new release.
+
+ 1. Generate a JAR file [using Gradle](UsingGradle.md#creating-the-jar-file).
+ 2. Tag the repo with the version number. e.g. `v0.1`
+ 2. [Crete a new release using GitHub](https://help.github.com/articles/creating-releases/)
+ and upload the JAR file your created.
+
+### Managing Dependencies
+
+A project often depends on third-party libraries. For example, Address Book depends on the
+[Jackson library](http://wiki.fasterxml.com/JacksonHome) for XML parsing. Managing these _dependencies_
+can be automated using Gradle. For example, Gradle can download the dependencies automatically, which
+is better than these alternatives.
+a. Include those libraries in the repo (this bloats the repo size)
+b. Require developers to download those libraries manually (this creates extra work for developers)
+
+## Appendix A : User Stories
+
+| User Stories | Likely/Not Likely |
+| ------ | ----------- |
+|As a user I can add an entry by specifying a entry title only, so that I can record entrys that need to be done ‘some day’.|Likely
+|As a user I can add entries that have a specific start and end time, so that I can record events with a specific duration.|Likely|
+|As a user I can add descriptions to my entries, so that I can add detail to my tasks.|Likely|
+|As a user I can edit an entry, so that I can make changes to entries as needed.|Likely|
+|As a user I can delete an entry, so that I can get rid of entries that I no longer care to track.|Likely|
+|As a user I can list all entries that are due, so that I can get an overview of all my entries.|Likely|
+|As a user I can find upcoming entries, so that I can decide what needs to be done soon.|Likely|
+|As a user, I can set a entry as completed, so that I can keep track of completed/uncompleted entries.|Likely|
+|As a user, I can add tags to the entries so that I can categorize entries together|Likely|
+|As a user I can search for an entry by different entry attributes, so that I can find specific entries.|Likely|
+|As a user I can undo a certain number of actions so that I can correct any mistaken actions.|Likely|
+|As a new user I can view more information about a particular command, so that I can learn how to use various commands.|Likely|
+|As an advanced user I can use shorter versions of a command, so that type a command faster.|Likely|
+|As a user, I can schedule a entry to be automatically added with a given frequency so that I don’t have to manually add entries every time.|Likely|
+|As an advanced user, I can specify a specific folder as the data storage location, so that I can choose to store the data file in a local folder controlled by a cloud syncing service, allowing me to access entry data from multiple computers.|Likely|
+|As a user, I can choose different views of all my tasks, e.g. “Due Today”, “Due This Week”, so that I can focus on what is important to me at that moment.|Likely|
+|As a user, I can use autocompletion of entry description so that I can quickly add entries that are similar to what I have added before|Not Likely|
+|As a user, I can personalize my app by changing the color scheme so that it fits my aesthetic requirements.|Not Likely|
+|As an advanced user, I can rebind default commands into my own keyboard shortcuts, so that I can use shortcuts that I am comfortable with.|Not Likely|
+|As a user, I can type in commands in a more ‘natural’ manner, so that I do not need to learn the command format.|Not Likely|
+
+## Appendix B : Use Cases
+
+(For all use cases below, the **System** is the `TaskManager` and the **Actor** is the `user`, unless specified otherwise)
+
+#### Use case: Delete person
+
+**Use case: Create a floating task**
+
+Actors: User
+
+MSS
+
+1. User request to create a new entry without specifying the start and/or end date and deadline
+2. TodoList add that particular floating task into Storage and save it
+
+ Use Case Ends
+
+*Extensions*
+
+1a. Entry is entered with invalid properties e.g. title
+> 1a1. TodoList warns user that the entry is invalid and does not add it into Storage
+>
+> Use Case Ends
+
+1b. Entry already exists
+> 1b1. TodoList warns user that the entry already exists and does not add it into Storage
+>
+> Use Case Ends
+
+**Use case: Create an event**
+
+Actors: User
+
+MSS
+
+1. User request to create a new entry while specifying the start and/or end date but not deadline
+2. TodoList add that particular event into Storage and save it
+
+ Use Case Ends
+
+**Use Case: Create a deadline**
+
+Actors: User
+
+MSS
+
+1. User request to create a new entry while specifying the deadline but no start and/or end date
+2. TodoList add that particular deadline into Storage and save it
+
+ Use Case Ends
+
+**Use Case: Delete an entry**
+
+Actors: User
+
+MSS
+
+1. User requests to delete an entry with a specified id
+2. TodoList deletes the entry
+
+ Use Case Ends
+
+*Extensions*
+
+1a. Entry with the specified id does not exist
+> 1a1. TodoList warns the user that the entry with the specified id does not exist
+>
+> Use Case Ends
+
+```
+###### /UserGuide.md
+``` md
+
+
+Our to-do application primarily takes in input using a command line interface. It supports the basic creation, reading, updating and deletion of entries. You may add entries with or without deadlines. These are known as tasks. Entries with a start and/or end time are known as events.
+
+To start, try adding a new task:
+
+```
+add Buy groceries
+```
+
+
+You can edit a task with the following command:
+
+```
+edit 0 end/tomorrow 5pm
+```
+Yep, natural language is parsed as well!
+
+
+
+Undo is quite easily done:
+
+```
+$ undo
+
+$ list
+[ ] Get groceries
+```
+
+Finally, try deleting the task:
+
+```
+delete 1
+```
+
+
+
+```
+###### /UserGuide.md
+``` md
+#### Help
+```
+help []
+```
+
+Show available commands and how to use them
+
+Help is also shown if you enter an incorrect command e.g. abcd
+
+```
diff --git a/collated/docs/A0121501E.md b/collated/docs/A0121501E.md
new file mode 100644
index 000000000000..024afd9e9850
--- /dev/null
+++ b/collated/docs/A0121501E.md
@@ -0,0 +1,162 @@
+# A0121501E
+###### /DeveloperGuide.md
+``` md
+**Use Case: List all entries**
+
+Actors: User
+
+MSS
+
+1. User requests to list all entries
+2. TodoList shows a list of entries, sorted by date (oldest first)
+
+ Use Case Ends
+
+**Use Case: List entries with filters**
+
+Actors: User
+
+MSS
+
+1. User requests to list entries with some filters
+2. TodoList shows a list of entries that satisfies the filters, sorted by date (oldest first)
+
+ Use Case Ends
+
+**Use Case: Add tags to a list of entries**
+
+Actors: User
+
+MSS
+
+1. User requests to list a filtered list of entries
+2. User requests to add tags to the listed entries
+
+ Use Case Ends
+
+*Extensions*
+
+2a. User attempts to add an invalid tag
+>2a1. TodoList tells the user that the tag name is invalid
+>
+>Use Case Ends
+
+## Appendix C : Non Functional Requirements
+
+1. User should navigate the application primarily using the command line
+2. Should be easy to navigate through large numbers of tasks and events
+3. Should not have a response time more than 100ms
+4. Should have minimal data loss after an unexpected crash
+5. Should have consistent data
+6. Should be able to accommodate more than 1000 tasks and events
+7. Should be able to work offline
+8. Should not exceed 100MB of RAM when in use
+9. Should store data as files instead of in some relational database
+10. Storage file should be human-editable and human-readable
+
+
+## Appendix D : Glossary
+
+##### Mainstream OS
+
+> Windows, Linux, Unix, OS-X
+
+
+## Appendix E : Product Survey
+
+### Trello:
+
+Strengths:
+
+- Geared towards task management: focus on only 1 thing
+- Covers various aspects of task management: deadline, assignees, descriptions, discussions
+- Designed to be collaborative
+- Unique UI, which gives a great overview of the tasks
+- Cross-platform
+
+Weaknesses:
+
+- Missing ability to schedule recurring tasks
+- Poor integration with calendar
+- A bit cluttered for personal use, since it’s designed for collaborative work
+
+### Evernote:
+
+Strengths:
+
+- All-in-one swiss army knife: Not only task management, can keep notes as well
+- Good in capturing tasks that need to be done before a specific date/time, or after a specific date/time, and items without specific times.
+- Can access tasks online and offline (if Jim is willing to pay for Evernote premium)
+
+Weaknesses:
+
+- Needs a few clicks/keyboard shortcuts to add a new task
+- No way to “block” some time out except by explicitly writing it in a note.
+
+### Google Keep:
+
+Strengths:
+
+- Compact UI that allows for many tasks to be viewed at once.
+- Colored tasks as visual aid.
+- Checkbox to indicate task completion. Alternatively, tasks can be archived to indicate completion.
+- Supports dynamic content such as links, images and drawings.
+- Sync through multiple gadget.
+
+Weakness:
+
+- No options for viewing the list of tasks in different orders (most recent, deadline etc)
+- No calendar view.
+
+```
+###### /UserGuide.md
+``` md
+#### Tagging
+```
+tag # [#...]
+```
+
+Add tag(s) to a particular entry with a specified id
+
+Examples:
+
+- `tag 123 #CS2103T #rocks`
+
+Delete tag(s) from a particular entry with a specified id using `untag`
+
+- `untag 123 #rocks`
+
+Duplicated tags will only be added once
+
+```
+###### /UserGuide.md
+``` md
+#### Deleting
+```
+delete
+```
+Delete the task with a particular entry id
+
+`list` should be executed before this command to obtain a entry id.
+
+Examples:
+
+- `Delete 42`
+
+
+#### Marking
+```
+mark
+```
+
+Check (or uncheck, for `unmark`) a entry as completed.
+
+`list` should be executed before this command to obtain a entry id.
+
+Examples:
+
+- `mark 42`
+
+- `unmark 42`
+
+```
diff --git a/collated/docs/A0126539Y.md b/collated/docs/A0126539Y.md
new file mode 100644
index 000000000000..8696fa827339
--- /dev/null
+++ b/collated/docs/A0126539Y.md
@@ -0,0 +1,167 @@
+# A0126539Y
+###### /DeveloperGuide.md
+``` md
+### Logic component
+
+
+
+**API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java)
+
+1. `Logic` uses the `Parser` class to parse the user command.
+2. This results in a `Command` object which is executed by the `LogicManager`.
+3. The command execution can affect the `Model` (e.g. adding a person) and/or raise events.
+4. The result of the command execution is encapsulated as a `CommandResult` object which is passed back to the `Ui`.
+
+Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")`
+ API call.
+
+
+### Model component
+
+
+
+**API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java)
+
+The `Model`,
+* stores a `UserPref` object that represents the user's preferences.
+* stores the Address Book data.
+* exposes a `UnmodifiableObservableList` that can be 'observed' e.g. the UI can be bound to this list
+ so that the UI automatically updates when the data in the list change.
+* does not depend on any of the other three components.
+
+### Storage component
+
+
+
+**API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java)
+
+The `Storage` component,
+* can save `UserPref` objects in json format and read it back.
+* can save the Address Book data in xml format and read it back.
+
+### Common classes
+
+Classes used by multiple components are in the `seedu.addressbook.commons` package.
+
+## Implementation
+
+### Logging
+
+We are using `java.util.logging` package for logging. The `LogsCenter` class is used to manage the logging levels
+and logging destinations.
+
+* The logging level can be controlled using the `logLevel` setting in the configuration file
+ (See [Configuration](#configuration))
+* The `Logger` for a class can be obtained using `LogsCenter.getLogger(Class)` which will log messages according to
+ the specified logging level
+* Currently log messages are output through: `Console` and to a `.log` file.
+
+**Logging Levels**
+
+* `SEVERE` : Critical problem detected which may possibly cause the termination of the application
+* `WARNING` : Can continue, but with caution
+* `INFO` : Information showing the noteworthy actions by the App
+* `FINE` : Details that is not usually noteworthy but may be useful in debugging
+ e.g. print the actual list instead of just its size
+
+### Configuration
+
+Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file
+(default: `config.json`):
+
+
+## Testing
+
+Tests can be found in the `./src/test/java` folder.
+
+**In Eclipse**:
+> If you are not using a recent Eclipse version (i.e. _Neon_ or later), enable assertions in JUnit tests
+ as described [here](http://stackoverflow.com/questions/2522897/eclipse-junit-ea-vm-option).
+
+* To run all tests, right-click on the `src/test/java` folder and choose
+ `Run as` > `JUnit Test`
+* To run a subset of tests, you can right-click on a test package, test class, or a test and choose
+ to run as a JUnit test.
+
+**Using Gradle**:
+* See [UsingGradle.md](UsingGradle.md) for how to run tests using Gradle.
+
+We have two types of tests:
+
+1. **GUI Tests** - These are _System Tests_ that test the entire App by simulating user actions on the GUI.
+ These are in the `guitests` package.
+
+2. **Non-GUI Tests** - These are tests not involving the GUI. They include,
+ 1. _Unit tests_ targeting the lowest level methods/classes.
+ e.g. `seedu.address.commons.UrlUtilTest`
+ 2. _Integration tests_ that are checking the integration of multiple code units
+ (those code units are assumed to be working).
+ e.g. `seedu.address.storage.StorageManagerTest`
+ 3. Hybrids of unit and integration tests. These test are checking multiple code units as well as
+ how the are connected together.
+ e.g. `seedu.address.logic.LogicManagerTest`
+
+**Headless GUI Testing** :
+Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use,
+ our GUI tests can be run in the _headless_ mode.
+ In the headless mode, GUI tests do not show up on the screen.
+ That means the developer can do other things on the Computer while the tests are running.
+ See [UsingGradle.md](UsingGradle.md#running-tests) to learn how to run tests in headless mode.
+
+## Dev Ops
+
+### Build Automation
+
+See [UsingGradle.md](UsingGradle.md) to learn how to use Gradle for build automation.
+
+```
+###### /UserGuide.md
+``` md
+## Commands
+
+#### Adding
+```
+add [start/ end/] [# ...] [r/] [desc/]
+```
+
+Add event or deadline
+
+Examples:
+
+
+- `add CS2103T Lecture start/2016-10-10 10:00 end/2016-10-10 12:00 r/weekly #rocks`
+
+- `add CS2105 Assignment 1 end/2016-10-10 10:00`
+
+```
+###### /UserGuide.md
+``` md
+#### Editing
+```
+edit [title/new title] [start/ end/] [#...] [r/ ] [desc/]
+```
+
+ Edit the entry with the specified entry id.
+
+ `list` should be executed before this command to obtain a entry id.
+
+Examples:
+
+- `edit 3 school`
+
+- `edit 13 #yearly`
+
+```
+###### /UserGuide.md
+``` md
+#### option
+```
+option [/ ...]
+```
+Configure user settings: name, file path to data file
+
+Examples:
+
+- `config save/data/MyNewLocation.xml`
+
+```
diff --git a/collated/docs/A0127828W.md b/collated/docs/A0127828W.md
new file mode 100644
index 000000000000..2021dcd21b51
--- /dev/null
+++ b/collated/docs/A0127828W.md
@@ -0,0 +1,186 @@
+# A0127828W
+###### /DeveloperGuide.md
+``` md
+# Developer Guide
+
+* [Setting Up](#setting-up)
+* [Design](#design)
+* [Implementation](#implementation)
+* [Testing](#testing)
+* [Dev Ops](#dev-ops)
+* [Appendix A: User Stories](#appendix-a--user-stories)
+* [Appendix B: Use Cases](#appendix-b--use-cases)
+* [Appendix C: Non Functional Requirements](#appendix-c--non-functional-requirements)
+* [Appendix D: Glossary](#appendix-d--glossary)
+* [Appendix E : Product Survey](#appendix-e-product-survey)
+
+
+## Setting up
+
+#### Prerequisites
+
+1. **JDK `1.8.0_60`** or later
+
+ > Having any Java 8 version is not enough.
+ This app will not work with earlier versions of Java 8.
+
+2. **Eclipse** IDE
+3. **e(fx)clipse** plugin for Eclipse (Do the steps 2 onwards given in
+ [this page](http://www.eclipse.org/efxclipse/install.html#for-the-ambitious))
+4. **Buildship Gradle Integration** plugin from the Eclipse Marketplace
+
+
+#### Importing the project into Eclipse
+
+0. Fork this repo, and clone the fork to your computer
+1. Open Eclipse (Note: Ensure you have installed the **e(fx)clipse** and **buildship** plugins as given
+ in the prerequisites above)
+2. Click `File` > `Import`
+3. Click `Gradle` > `Gradle Project` > `Next` > `Next`
+4. Click `Browse`, then locate the project's directory
+5. Click `Finish`
+
+ > * If you are asked whether to 'keep' or 'overwrite' config files, choose to 'keep'.
+ > * Depending on your connection speed and server load, it can even take up to 30 minutes for the set up to finish
+ (This is because Gradle downloads library files from servers during the project set up process)
+ > * If Eclipse auto-changed any settings files during the import process, you can discard those changes.
+
+## Design
+
+### Architecture
+
+
+The **_Architecture Diagram_** given above explains the high-level design of the App.
+Given below is a quick overview of each component.
+
+`Main` has only one class called [`MainApp`](../src/main/java/seedu/address/MainApp.java). It is responsible for,
+* At app launch: Initializes the components in the correct sequence, and connect them up with each other.
+* At shut down: Shuts down the components and invoke cleanup method where necessary.
+
+[**`Commons`**](#common-classes) represents a collection of classes used by multiple other components.
+Two of those classes play important roles at the architecture level.
+* `EventsCentre` : This class (written using [Google's Event Bus library](https://github.com/google/guava/wiki/EventBusExplained))
+ is used by components to communicate with other components using events (i.e. a form of _Event Driven_ design)
+* `LogsCenter` : Used by many classes to write log messages to the App's log file.
+
+The rest of the App consists four components.
+* [**`UI`**](#ui-component) : The UI of tha App.
+* [**`Logic`**](#logic-component) : The command executor.
+* [**`Model`**](#model-component) : Holds the data of the App in-memory.
+* [**`Storage`**](#storage-component) : Reads data from, and writes data to, the hard disk.
+
+Each of the four components
+* Defines its _API_ in an `interface` with the same name as the Component.
+* Exposes its functionality using a `{Component Name}Manager` class.
+
+For example, the `Logic` component (see the class diagram given below) defines it's API in the `Logic.java`
+interface and exposes its functionality using the `LogicManager.java` class.
+
+
+The _Sequence Diagram_ below shows how the components interact for the scenario where the user issues the
+command `delete 3`.
+
+
+
+>Note how the `Model` simply raises a `AddressBookChangedEvent` when the Address Book data are changed,
+ instead of asking the `Storage` to save the updates to the hard disk.
+
+The diagram below shows how the `EventsCenter` reacts to that event, which eventually results in the updates
+being saved to the hard disk and the status bar of the UI being updated to reflect the 'Last Updated' time.
+
+
+> Note how the event is propagated through the `EventsCenter` to the `Storage` and `UI` without `Model` having
+ to be coupled to either of them. This is an example of how this Event Driven approach helps us reduce direct
+ coupling between components.
+
+The sections below give more details of each component.
+
+### UI component
+
+
+
+**API** : [`Ui.java`](../src/main/java/seedu/address/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
+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/address/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.
+
+```
+###### /UserGuide.md
+``` md
+
+## Command Summary
+
+| Command |Format |
+| --- | --- |
+|add|`add [start/ end/] [# ...] [r/] [desc/]`|
+|list|`list [[keywords] [[after/] [before/] | [on/]][# ...] [recurrence=] [desc=]]`| [type/{entry, task}]
+|tag|`tag # [# ...]`|
+|untag|`untag # [# ...]`|
+|edit|`edit [title/new title] [start/ end/] [#...] [r/ ] [desc/]`|
+|delete|`delete `|
+|mark|`mark `|
+|unmark|`unmark `|
+|undo|`undo`|
+|redo|`redo`|
+|help|`help []`|
+|option|`option [/ ...]`|
+
+```
+###### /UserGuide.md
+``` md
+#### Listing
+```
+list [[keywords] [[after/] [before/] | [on/]] [# ...] [recurrence=] [desc=]] [type/{entry, task}]
+```
+
+List all entries, or entries that satisfy the given search criteria. For the best user experience, completed entries are _automatically excluded_.
+
+Examples:
+
+- `list`
+
+- `list after/2016-10-10`
+
+- `list buy banana #groceries`
+
+If you want to include completed entries in your search, replace `list` with `list-all`
+
+- `list-all buy banana`
+
+```
+###### /UserGuide.md
+``` md
+#### Undo
+
+```
+undo
+```
+
+Undo the latest change to the todo list. Handles every changes, including `clear`.
+
+#### Redo
+
+```
+redo
+```
+
+Redo the latest command reverted with `undo`.
+
+```
+###### /UserGuide.md
+``` md
+## Misc
+
+You can use UP and DOWN keys to browse through your past commands in the session.
+```
diff --git a/collated/main/A0116603R.md b/collated/main/A0116603R.md
index 1cd10cc56d91..0fdc81f35fb7 100644
--- a/collated/main/A0116603R.md
+++ b/collated/main/A0116603R.md
@@ -1,6 +1,12 @@
# A0116603R
###### /java/seedu/address/commons/events/ui/DidMarkTaskEvent.java
``` java
+/**
+ * An event indicating that the Logic component has completed
+ * executing the mark/unmark command after a user clicks on
+ * a checkbox in the GUI. This event is used to propagate feedback
+ * to the user.
+ */
public class DidMarkTaskEvent extends BaseEvent{
private CommandResult cmdResult;
@@ -21,6 +27,10 @@ public class DidMarkTaskEvent extends BaseEvent{
```
###### /java/seedu/address/commons/events/ui/MarkTaskEvent.java
``` java
+/**
+ * An event indicating that the user has clicked on a checkbox for a particular
+ * task in the GUI.
+ */
public class MarkTaskEvent extends BaseEvent {
private final static String CHECKED = "checked";
@@ -50,8 +60,34 @@ public class MarkTaskEvent extends BaseEvent {
}
```
+###### /java/seedu/address/commons/events/ui/WindowResizeEvent.java
+``` java
+/**
+ * An event indicating that the user has finished resizing the
+ * application window.
+ */
+public class WindowResizeEvent extends BaseEvent {
+ private Double newWidth;
+
+ public WindowResizeEvent(Number newWidth) {
+ this.newWidth = (Double)newWidth;
+ }
+
+ public Double getNewWidth() {
+ return newWidth;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+}
+```
###### /java/seedu/address/model/task/Entry.java
``` java
+ /**
+ * Formats the Entry as text, showing all contact details.
+ */
public String getAsText() {
final StringBuilder builder = new StringBuilder();
builder.append(getTitle());
@@ -82,10 +118,11 @@ public class MarkTaskEvent extends BaseEvent {
return buffer.substring(0, buffer.length() - DELIMITER.length());
}
}
-}
+
```
###### /java/seedu/address/model/task/Event.java
``` java
+ @Override
public String getAsText() {
final StringBuilder builder = new StringBuilder();
builder.append(super.getAsText());
@@ -105,6 +142,7 @@ public class MarkTaskEvent extends BaseEvent {
```
###### /java/seedu/address/model/task/Task.java
``` java
+ @Override
public String getAsText() {
final StringBuilder builder = new StringBuilder();
builder.append(super.getAsText());
@@ -120,7 +158,11 @@ public class MarkTaskEvent extends BaseEvent {
```
###### /java/seedu/address/ui/AppViewController.java
``` java
+/**
+ * Interaction point between Main Controller and rest of the app view(s)
+ */
public class AppViewController {
+ private static final Logger logger = LogsCenter.getLogger(AppViewController.class);
private Logic logic;
@@ -136,6 +178,7 @@ public class AppViewController {
// ################
void init() {
initChildViews();
+ EventsCenter.getInstance().registerHandler(this);
}
private void initChildViews() {
@@ -155,10 +198,50 @@ public class AppViewController {
this.logic = logic;
}
+ // #################
+ // # EVENT HANDLER #
+ // #################
+ @Subscribe
+ private void handleWindowResizeEvent(WindowResizeEvent event) {
+ logger.info(LogsCenter.getEventHandlingLogMessage(event));
+ if (event.getNewWidth().compareTo(LARGE_DISPLAY_WIDTH) <= 0) {
+ removeLargeStyleClass();
+ } else {
+ addLargeStyleClass();
+ }
+ }
+
+ // ##################
+ // # HELPER METHODS #
+ // ##################
+
+ /**
+ * Adds the LARGE_STYLE_CLASS to the root layout if the style class is not
+ * already added
+ */
+ private void removeLargeStyleClass() {
+ if (rootLayout.getStyleClass().contains(LARGE_STYLE_CLASS)) {
+ Platform.runLater(() -> rootLayout.getStyleClass().remove(LARGE_STYLE_CLASS));
+
+ }
+ }
+
+ /**
+ * Removes the LARGE_STYLE_CLASS from the root layout if the style class is
+ * present
+ */
+ private void addLargeStyleClass() {
+ if (!rootLayout.getStyleClass().contains(LARGE_STYLE_CLASS)) {
+ Platform.runLater(() -> rootLayout.getStyleClass().add(LARGE_STYLE_CLASS));
+ }
+ }
}
```
###### /java/seedu/address/ui/CommandArea.java
``` java
+/**
+ * Represents the Command Area
+ */
public class CommandArea extends VBox {
// ####################
@@ -177,6 +260,8 @@ public class CommandArea extends VBox {
private CommandResult mostRecentResult;
+ private CommandHistory commandHistoryManager;
+
// ########
// # FXML #
// ########
@@ -194,6 +279,7 @@ public class CommandArea extends VBox {
this.logic = logic;
EventsCenter.getInstance().registerHandler(this);
+ commandHistoryManager = this.logic.getCommandHistoryManager();
}
@FXML
@@ -202,9 +288,51 @@ public class CommandArea extends VBox {
statusLine.setText(Messages.STATUS_LINE_WELCOME);
}
+```
+###### /java/seedu/address/ui/CommandArea.java
+``` java
+ @Subscribe
+ private void handleDidMarkTaskEvent(DidMarkTaskEvent event) {
+ statusLine.setText(event.getCommandResult().feedbackToUser);
+ }
+
+ @Subscribe
+ private void handleFocusCommandLineEvent(FocusCommandLineEvent event) {
+ if (!cmdLine.isFocused()) {
+ cmdLine.requestFocus();
+ }
+ }
+
+ /**
+ * Sets the command box style to indicate a correct command.
+ */
+ private void setStyleToIndicateCorrectCommand() {
+ cmdLine.getStyleClass().remove("error");
+ cmdLine.setText("");
+ }
+
+ /**
+ * Sets the command box style to indicate an error
+ */
+ private void setStyleToIndicateIncorrectCommand() {
+ cmdLine.getStyleClass().add("error");
+ }
+
+ /**
+ * Restores the command box text to the previously entered command
+ */
+ private void restoreCommandText() {
+ cmdLine.setText(previousCommand);
+ }
+
+}
```
###### /java/seedu/address/ui/Controller.java
``` java
+/**
+ * Abstract class for all app view controllers.
+ * Forces controllers to implement an init method.
+ */
abstract class Controller {
BorderPane appView;
@@ -223,6 +351,7 @@ abstract class Controller {
void show() {
assert appView != null;
+ appView.setVisible(true);
appView.toFront();
getFadeTransition(DEFAULT_FADE_DURATION, OPAQUE).play();
}
@@ -231,6 +360,7 @@ abstract class Controller {
assert appView != null;
getFadeTransition(DEFAULT_FADE_DURATION, TRANSPARENT).play();
appView.toBack();
+ appView.setVisible(false);
}
private FadeTransition getFadeTransition(double duration, double newValue) {
@@ -242,6 +372,9 @@ abstract class Controller {
```
###### /java/seedu/address/ui/EntryViewCell.java
``` java
+/**
+ * Generic cell interface for displaying different types of Entries
+ */
public interface EntryViewCell {
// Default factory method for generating a ListCell
static Callback, ListCell> getFactory() {
@@ -251,6 +384,9 @@ public interface EntryViewCell {
```
###### /java/seedu/address/ui/HelpCard.java
``` java
+/**
+ * Represents a help item which can be displayed.
+ */
public class HelpCard extends HBox {
private static final String FXML = "HelpCard.fxml";
@@ -279,27 +415,34 @@ public class HelpCard extends HBox {
```
###### /java/seedu/address/ui/HelpList.java
``` java
+/**
+ * Represents a list of help cards to be displayed
+ */
public class HelpList extends ListView {
private static final String FXML = "HelpList.fxml";
- private static final String ADD_HELP = "ADD";
- private static final String ADD_HELP_TEXT = "add [st/ end/ | dl/] [# ...] [desc/]";
- private static final String EDIT_HELP = "EDIT";
- private static final String EDIT_HELP_TEXT = "edit [title/new title] [st/ end/ | deadline/] [#...] [desc/]";
- private static final String ESCAPE_HELP = "CLOSE HELP";
- private static final String ESCAPE_HELP_TEXT = "";
- private static final String LIST_HELP = "LIST";
- private static final String LIST_HELP_TEXT = "list [[keywords] [[after/] [before/] | [on/]][# ...] [recurrence=] [desc=]]";
- private static final String TAG_HELP = "TAG";
- private static final String TAG_HELP_TEXT = "tag # [# ...]";
- private static final String UNTAG_HELP = "UNTAG";
- private static final String UNTAG_HELP_TEXT = "untag # [# ...]";
- private static final String DELETE_HELP = "DELETE";
- private static final String DELETE_HELP_TEXT = "delete ";
- private static final String MARK_HELP = "MARK";
- private static final String MARK_HELP_TEXT = "mark ";
- private static final String UNMARK_HELP = "UNMARK";
- private static final String UNMARK_HELP_TEXT = "unmark ";
+ private static final String[] COMMANDS = new String[]{
+ "CLOSE HELP",
+ "ADD", "EDIT",
+ "DELETE", "LIST",
+ "LIST-ALL",
+ "TAG", "UNTAG",
+ "MARK", "UNMARK",
+ "UNDO", "OPTION"};
+
+ private static final String[] HELP_TEXT = new String[]{
+ "",
+ "add [start/ end/] [# ...] [r/] [desc/]",
+ "edit [title/new title] [start/ end/] [#...] [r/ ] [desc/]",
+ "delete ",
+ "list [[keywords] [[after/] [before/] | [on/]][# ...] [recurrence=] [desc=] [type/{task, event}]]",
+ "list-all [[keywords] [[after/] [before/] | [on/]][# ...] [recurrence=] [desc=]]",
+ "tag # [# ...]",
+ "untag # [# ...]",
+ "mark ",
+ "unmark ",
+ "undo",
+ "option [/ ...]"};
private ObservableList data;
@@ -319,15 +462,11 @@ public class HelpList extends ListView {
private void initHelpItems() {
data = FXCollections.observableArrayList();
- data.add(new HelpItem(ESCAPE_HELP, ESCAPE_HELP_TEXT));
- data.add(new HelpItem(ADD_HELP, ADD_HELP_TEXT));
- data.add(new HelpItem(EDIT_HELP, EDIT_HELP_TEXT));
- data.add(new HelpItem(LIST_HELP, LIST_HELP_TEXT));
- data.add(new HelpItem(TAG_HELP, TAG_HELP_TEXT));
- data.add(new HelpItem(UNTAG_HELP, UNTAG_HELP_TEXT));
- data.add(new HelpItem(DELETE_HELP, DELETE_HELP_TEXT));
- data.add(new HelpItem(MARK_HELP, MARK_HELP_TEXT));
- data.add(new HelpItem(UNMARK_HELP, UNMARK_HELP_TEXT));
+
+ assert (COMMANDS.length == HELP_TEXT.length);
+ for (int i=0; i {
```
###### /java/seedu/address/ui/HelpViewCell.java
``` java
+/**
+ * Custom ListCell which is displayed using a HelpCard
+ */
public class HelpViewCell extends ListCell implements EntryViewCell {
static Callback, ListCell> getFactory() {
@@ -374,6 +516,9 @@ public class HelpViewCell extends ListCell implements EntryVi
```
###### /java/seedu/address/ui/HelpViewController.java
``` java
+/**
+ * Controller which displays the help page
+ */
public class HelpViewController extends Controller {
private static final Logger logger = LogsCenter.getLogger(HelpViewController.class);
@@ -410,6 +555,10 @@ public class HelpViewController extends Controller {
```
###### /java/seedu/address/ui/MainController.java
``` java
+/**
+ * The Main Controller, which is in charge of the root layout and
+ * enables other UI elements to be placed within the application.
+ */
public class MainController extends UiPart {
// #############
@@ -417,7 +566,7 @@ public class MainController extends UiPart {
// #############
private static final String FXML = "RootLayout.fxml";
private static final String CSS_SOURCE = "/view/PriorityQTheme.css";
- private static final int MIN_HEIGHT = 760;
+ private static final int MIN_HEIGHT = 520;
private static final int MIN_WIDTH = 520;
private static final Logger logger = LogsCenter.getLogger(MainController.class);
@@ -473,7 +622,6 @@ public class MainController extends UiPart {
assert rootLayout != null;
logger.info("Initialising Scene...");
scene = new Scene(rootLayout);
-
configureScene();
}
@@ -482,7 +630,8 @@ public class MainController extends UiPart {
URL url = this.getClass().getResource(CSS_SOURCE);
String css = url.toExternalForm();
scene.getStylesheets().add(css);
- addEscapeHandlerForScene();
+ addEscapeFilterForScene();
+ scene.widthProperty().addListener(getWindowResizeEventListener());
}
/**
@@ -490,7 +639,7 @@ public class MainController extends UiPart {
* A filter is used instead of a handler since the ListView or a particular
* JavaFX node consumes the event only in the case of an `ESCAPE` key.
*/
- private void addEscapeHandlerForScene() {
+ private void addEscapeFilterForScene() {
assert scene != null;
scene.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler() {
@Override
@@ -560,12 +709,14 @@ public class MainController extends UiPart {
return new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
(int) primaryStage.getX(), (int) primaryStage.getY());
}
-
}
```
###### /java/seedu/address/ui/TaskCard.java
``` java
-public class TaskCard extends HBox {
+/**
+ * Represents Tasks and Events in the TaskList
+ */
+public class TaskCard extends VBox {
private static final String FXML = "TaskCard.fxml";
// ########
@@ -619,7 +770,6 @@ public class TaskCard extends HBox {
this.entry = entry;
this.index = index;
this.listener = listener;
- this.checkBox.selectedProperty().bindBidirectional(entry.isMarkedProperty());
initData();
}
@@ -641,8 +791,16 @@ public class TaskCard extends HBox {
private void initCommonElements() {
title.setText(entry.getTitle().fullTitle);
id.setText(Integer.toString(index));
- tags.setText(entry.tagsString());
- description.setText(entry.getDescription());
+ if (entry.tagsString().isEmpty()) {
+ hide(tags);
+ } else {
+ tags.setText(entry.tagsString());
+ }
+ if (entry.getDescription() == null || entry.getDescription().isEmpty()) {
+ hide(description);
+ } else {
+ description.setText(entry.getDescription());
+ }
}
private void initTaskSpecificElements(Task task) {
@@ -650,19 +808,40 @@ public class TaskCard extends HBox {
if (task.getDeadline() == null) {
deadline.setOpacity(TRANSPARENT);
+ styleTask(getTaskStyling(task.isMarked()));
return;
}
deadline.setText(task.getDeadlineDisplay().toUpperCase());
- String additionalStyleClass = getDeadlineStyling(task.getDeadline());
- if (!additionalStyleClass.isEmpty()) {
- deadline.getStyleClass().add(additionalStyleClass);
- }
+ styleDeadline(getDeadlineStyling(task.isMarked(), task.getDeadline()));
}
private void initCheckbox(Task task) {
- checkBox.selectedProperty().addListener(listener);
checkBox.setSelected(task.isMarked());
+ checkBox.selectedProperty().addListener(listener);
+ }
+
+ private void styleTask(String styleClass) {
+ if (!styleClass.isEmpty()) {
+ addStyleClass(styleClass, getTaskElements());
+ }
+ }
+
+ private void addStyleClass(String styleClass, Styleable... nodes) {
+ for (Styleable node : nodes) {
+ node.getStyleClass().add(styleClass);
+ }
+ }
+
+ private Styleable[] getTaskElements() {
+ return new Styleable[]{id, title, description};
+ }
+
+ private void styleDeadline(String styleClass) {
+ if (!styleClass.isEmpty()) {
+ styleTask(styleClass);
+ addStyleClass(styleClass, deadline);
+ }
}
private void hideEventSpecificElements() {
@@ -675,11 +854,13 @@ public class TaskCard extends HBox {
separator.setText(EVENT_DATE_SEPARATOR);
endTime.setText(event.getEndTimeDisplay().toUpperCase());
- String additionalStyleClass = getEventStyling(event.getStartTime(), event.getEndTime());
- if (!additionalStyleClass.isEmpty()) {
- startTime.getStyleClass().add(additionalStyleClass);
- separator.getStyleClass().add(additionalStyleClass);
- endTime.getStyleClass().add(additionalStyleClass);
+ styleEvent(getEventStyling(event.getStartTime(), event.getEndTime()));
+ }
+
+ private void styleEvent(String styleClass) {
+ addStyleClass(EVENT_DESCRIPTION_STYLE_CLASS, description);
+ if (!styleClass.isEmpty()) {
+ addStyleClass(styleClass, startTime, separator, endTime, id, title, description);
}
}
@@ -688,21 +869,25 @@ public class TaskCard extends HBox {
hide(deadline, checkBox);
}
- private void hide(Node... nodes) {
- for (Node node : nodes) {
- node.setOpacity(TRANSPARENT);
- }
- }
-
private void setEmptyText(Label... labels) {
for (Label label : labels) {
label.setText("");
}
}
+
+ private void hide(Node... nodes) {
+ for (Node node : nodes) {
+ node.managedProperty().bind(node.visibleProperty());
+ node.setVisible(false);
+ }
+ }
}
```
###### /java/seedu/address/ui/TaskList.java
``` java
+/**
+ * Represents a list of tasks to be displayed
+ */
public class TaskList extends ListView {
private static String FXML = "TaskList.fxml";
@@ -725,6 +910,9 @@ public class TaskList extends ListView {
```
###### /java/seedu/address/ui/TaskViewCell.java
``` java
+/**
+ * Custom ListCell which is displayed using a TaskCard
+ */
public class TaskViewCell extends ListCell implements EntryViewCell {
static Callback, ListCell> getFactory() {
@@ -750,6 +938,10 @@ public class TaskViewCell extends ListCell implements EntryViewCell {
```
###### /java/seedu/address/ui/TaskViewController.java
``` java
+/**
+ * Controller which initializes the default Task View -- this consists of
+ * a TaskList and a CommandArea
+ */
public class TaskViewController extends Controller {
private static final Logger logger = LogsCenter.getLogger(TaskViewController.class);
@@ -777,6 +969,7 @@ public class TaskViewController extends Controller {
appView.setCenter(taskList);
appView.setBottom(new CommandArea(logic));
appView.toFront();
+ addEnterHandlerForScene();
}
// ###################
@@ -788,6 +981,25 @@ public class TaskViewController extends Controller {
return logic.getFilteredPersonList();
}
+ /**
+ * Register an event handler to enable focusing on the command
+ * line if the user presses the key.
+ */
+ private void addEnterHandlerForScene() {
+ assert appView != null;
+ appView.addEventHandler(KeyEvent.KEY_RELEASED, new EventHandler() {
+ @Override
+ public void handle(KeyEvent event) {
+ if (event.getCode() == KeyCode.ENTER) {
+ EventsCenter.getInstance().post(new FocusCommandLineEvent());
+ }
+ }
+ });
+ }
+
+ // ##################
+ // # EVENT HANDLERS #
+ // ##################
@Subscribe
private void handleShowHelpEvent(ShowHelpListEvent event) {
logger.info(LogsCenter.getEventHandlingLogMessage(event));
@@ -804,6 +1016,9 @@ public class TaskViewController extends Controller {
```
###### /java/seedu/address/ui/util/GuiUtil.java
``` java
+/**
+ * Helper class which returns event handlers for user-initiated events
+ */
public class GuiUtil {
public static double OPAQUE = 1.0;
@@ -811,36 +1026,21 @@ public class GuiUtil {
public static double DEFAULT_FADE_DURATION = 400;
+ public static Double LARGE_DISPLAY_WIDTH = Double.valueOf(768);
+ public static String LARGE_STYLE_CLASS = "large";
+
public static final String EVENT_DATE_SEPARATOR = " - ";
+ public static final String EVENT_DESCRIPTION_STYLE_CLASS = "event";
+
private static String PAST_STYLE_CLASS = "past";
private static String ACTIVE_STYLE_CLASS = "present";
+ private static String OVERDUE_STYLE_CLASS = "overdue";
public static ChangeListener getCheckBoxEventListener(int idx) {
return (ov, old_val, new_val) -> EventsCenter.getInstance().post(new MarkTaskEvent(idx, new_val));
}
- public static String getDeadlineStyling(LocalDateTime dateTime) {
- LocalDateTime now = LocalDateTime.now();
- LocalDateTime midnight = LocalDateTime.of(now.toLocalDate().plusDays(1), LocalTime.MIDNIGHT);
- if (dateTime.isBefore(now)) {
- return PAST_STYLE_CLASS;
- } else if (dateTime.isAfter(now) && dateTime.isBefore(midnight)) {
- return ACTIVE_STYLE_CLASS;
- }
- return "";
- }
-
- public static String getEventStyling(LocalDateTime startTime, LocalDateTime endTime) {
- LocalDateTime now = LocalDateTime.now();
- if (endTime.isBefore(now)) {
- return PAST_STYLE_CLASS;
- } else if (now.isAfter(startTime) && now.isBefore(endTime)) {
- return ACTIVE_STYLE_CLASS;
- }
- return "";
- }
-}
```
###### /resources/view/PriorityQTheme.css
``` css
@@ -873,6 +1073,10 @@ public class GuiUtil {
-fx-padding: 10;
}
+.large .label {
+ -fx-font-size: 12pt;
+}
+
.list-view {
-fx-border-width: 0;
-fx-background-insets: 0;
@@ -909,10 +1113,30 @@ public class GuiUtil {
-fx-padding: 0 0 4 4;
}
+.large .taskcard #tags {
+ -fx-font-size: 11pt;
+}
+
.taskcard #description {
-fx-font-size: 9pt;
-fx-text-fill: DarkGrey;
- -fx-padding: 0 0 0 40;
+ -fx-padding: -6 0 0 26;
+}
+
+.large .taskcard #description {
+ -fx-font-size: 11pt;
+}
+
+.taskcard #description.event {
+ -fx-padding: -6 0 0 0;
+}
+
+.taskcard .label.past, #description.past {
+ -fx-text-fill: LightGrey;
+}
+
+.taskcard .label.overdue, #description.overdue {
+ -fx-text-fill: OrangeRed;
}
.taskcard #deadline {
@@ -928,11 +1152,19 @@ public class GuiUtil {
-fx-text-fill: DarkTurquoise;
}
+.large .taskcard .date {
+ -fx-font-size: 9pt;
+}
+
.taskcard .date.present#deadline {
-fx-background-color: YellowGreen;
}
.taskcard .date.past#deadline {
+ -fx-background-color: LightGrey;
+}
+
+.taskcard .date.overdue#deadline {
-fx-background-color: OrangeRed;
}
diff --git a/collated/main/A0116603Rreused.md b/collated/main/A0116603Rreused.md
new file mode 100644
index 000000000000..891934643361
--- /dev/null
+++ b/collated/main/A0116603Rreused.md
@@ -0,0 +1,85 @@
+# A0116603Rreused
+###### /java/seedu/address/ui/util/GuiUtil.java
+``` java
+ public static ChangeListener getWindowResizeEventListener() {
+ return new ChangeListener() {
+ final Timer timer = new Timer();
+ TimerTask task = null;
+ final long delayTime = 200;
+
+ @Override
+ public void changed(ObservableValue extends Number> observable, Number oldValue, final Number newValue) {
+ if (task != null) task.cancel();
+ task = new TimerTask() {
+ @Override
+ public void run() {
+ EventsCenter.getInstance().post(new WindowResizeEvent(newValue));
+ }
+ };
+ timer.schedule(task, delayTime);
+ }
+ };
+ }
+
+ /**
+ * Get the style class for floating task elements such as id, title and
+ * description.
+ * @param isMarked true, if the current task is marked as done
+ * @return a string, the style class for the floating task
+ */
+ public static String getTaskStyling(boolean isMarked) {
+ return getDeadlineStyling(isMarked, null);
+ }
+
+ /**
+ * Get the style class for deadlines. The style differs depending on whether the
+ * task is marked as done. If it is not done, it differs depending on whether it
+ * is overdue. If it is not overdue, it differs depending on whether the deadline
+ * is due by the end of the current day.
+ * @param isMarked true, if the current task is marked as done
+ * @param deadline a datetime which will be compared to the current time to
+ * determine the style class
+ * @return a string, the style class for the deadline
+ */
+ public static String getDeadlineStyling(boolean isMarked, LocalDateTime deadline) {
+ if (isMarked) {
+ return PAST_STYLE_CLASS;
+ }
+
+ if (deadline == null) {
+ return "";
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+ LocalDateTime midnight = LocalDateTime.of(now.toLocalDate().plusDays(1), LocalTime.MIDNIGHT);
+
+ if (deadline.isBefore(now)) {
+ return OVERDUE_STYLE_CLASS;
+ }
+
+ if (deadline.isAfter(now) && deadline.isBefore(midnight)) {
+ return ACTIVE_STYLE_CLASS;
+ }
+ return "";
+ }
+
+ /**
+ * Get the style class for events. The style differs depending on whether the
+ * event is over. If it is not over, but currently ongoing, a different style
+ * class is also applied.
+ * @param startTime a datetime, the start of the event
+ * @param endTime a datetime, the end of the event
+ * @return a string, the style class for the event
+ */
+ public static String getEventStyling(LocalDateTime startTime, LocalDateTime endTime) {
+ assert (startTime != null && endTime != null);
+ LocalDateTime now = LocalDateTime.now();
+ if (endTime.isBefore(now)) {
+ return PAST_STYLE_CLASS;
+ } else if (now.isAfter(startTime) && now.isBefore(endTime)) {
+ return ACTIVE_STYLE_CLASS;
+ }
+ return "";
+ }
+}
+```
diff --git a/collated/main/A0121501E.md b/collated/main/A0121501E.md
index 5444ad607084..ed0e49bec2d9 100644
--- a/collated/main/A0121501E.md
+++ b/collated/main/A0121501E.md
@@ -3,7 +3,7 @@
``` java
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState()!=CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
};
assert toAdd != null;
@@ -12,20 +12,21 @@
} catch (EntryNotFoundException enfe) {
assert false : "The target entry cannot be missing";
}
+ setRedoable();
return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, toAdd));
}
-
}
```
###### /java/seedu/address/logic/commands/ClearCommand.java
``` java
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState() != CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
}
assert model != null;
model.resetData(originalTaskManager);
+ setRedoable();
return new CommandResult(MESSAGE_UNDO_SUCCESS);
}
}
@@ -33,25 +34,25 @@
###### /java/seedu/address/logic/commands/DeleteCommand.java
``` java
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState()!=CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
};
assert model != null;
assert entryToDelete != null;
try {
model.addTask(entryToDelete);
+ setRedoable();
return new CommandResult(String.format(MESSAGE_UNDO_DELETE_PERSON_SUCCESS, entryToDelete));
} catch (DuplicateTaskException e) {
return new CommandResult(MESSAGE_DUPLICATE_ENTRY);
}
}
-
}
```
###### /java/seedu/address/logic/commands/EditCommand.java
``` java
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState()!=CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
};
assert model != null;
@@ -59,6 +60,7 @@
try {
model.editTask(reverseUpdate);
+ model.updateLastModifiedTime(taskToEdit, originalLastModifiedTime);
} catch (EntryNotFoundException e) {
assert false : "The target entry cannot be missing";
} catch (DuplicateTaskException e) {
@@ -66,13 +68,16 @@
} catch (EntryConversionException e) {
assert false: "Undo shouldn't convert Task to Event and vice versa";
}
+ setRedoable();
return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, taskToEdit));
}
-
}
```
###### /java/seedu/address/logic/commands/MarkCommand.java
``` java
+/**
+ * Marks an entry as completed.
+ */
public class MarkCommand extends UndoableCommand {
public static final String COMMAND_WORD = "mark";
@@ -87,6 +92,7 @@ public class MarkCommand extends UndoableCommand {
private final int targetIndex;
private Entry entryToMark;
+ private LocalDateTime originalLastModifiedTime;
private boolean originalIsMarked;
public MarkCommand(int targetIndex) {
@@ -96,39 +102,43 @@ public class MarkCommand extends UndoableCommand {
@Override
public CommandResult execute() {
assert model != null;
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
- indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ if (getCommandState()==CommandState.PRE_EXECUTION) {
+ UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ }
+
+ entryToMark = lastShownList.get(targetIndex - 1);
+ originalLastModifiedTime = entryToMark.getLastModifiedTime();
+ originalIsMarked = entryToMark.isMarked();
}
-
- entryToMark = lastShownList.get(targetIndex - 1);
- originalIsMarked = entryToMark.isMarked();
+
try {
model.markTask(entryToMark);
} catch (EntryNotFoundException pnfe) {
assert false : "The target entry cannot be missing";
}
- setExecutionIsSuccessful();
+ setUndoable();
return new CommandResult(String.format(MESSAGE_SUCCESS, entryToMark));
}
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState() != CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
- };
+ }
assert model != null;
assert entryToMark != null;
try {
- if (originalIsMarked) {
- model.markTask(entryToMark);
- } else {
+ if (!originalIsMarked) {
model.unmarkTask(entryToMark);
}
+ model.updateLastModifiedTime(entryToMark, originalLastModifiedTime);
+ setRedoable();
} catch (EntryNotFoundException enfe) {
assert false : "The target entry cannot be missing";
}
@@ -137,8 +147,44 @@ public class MarkCommand extends UndoableCommand {
}
}
```
+###### /java/seedu/address/logic/commands/RedoCommand.java
+``` java
+/**
+ * Redo the previous undo command.
+ */
+public class RedoCommand extends Command {
+
+ public static final String COMMAND_WORD = "redo";
+ public static final String MESSAGE_FAILURE = "No redoable commands found!";
+ private UndoableCommandHistory undoableCommandHistory;
+
+ public RedoCommand() {}
+
+ public void setData(Model model, UndoableCommandHistory undoableCommandQueue) {
+ this.model = model;
+ this.undoableCommandHistory = undoableCommandQueue;
+ }
+
+ @Override
+ public CommandResult execute() {
+ try {
+ UndoableCommand undoableCommand = undoableCommandHistory.getFromRedoStack();
+ CommandResult undoableCommandResult = undoableCommand.reExecute();
+ if (undoableCommand.getCommandState() == CommandState.UNDOABLE) {
+ undoableCommandHistory.pushToUndoStack(undoableCommand);
+ }
+ return undoableCommandResult;
+ } catch (UndoableCommandNotFoundException e) {
+ return new CommandResult(MESSAGE_FAILURE);
+ }
+ }
+}
+```
###### /java/seedu/address/logic/commands/TagCommand.java
``` java
+/*
+ * Add tags to an entry. Does not overwrite existing tags.
+ */
public class TagCommand extends UndoableCommand {
public static final String COMMAND_WORD = "tag";
@@ -146,15 +192,17 @@ public class TagCommand extends UndoableCommand {
public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Adds tags to task. "
+ "Parameters: TASK_ID TAG[,...] "
- + "Example: " + COMMAND_WORD + " 2 shopping, food";
+ + "Example: " + COMMAND_WORD + " 2 #shopping #food";
- public static final String MESSAGE_SUCCESS = "Tagged entry: %1$s";
- public static final String MESSAGE_UNDO_SUCCESS = "Undo tag entry: %1$s";
+ public static final String MESSAGE_SUCCESS = "Add %1$s to entry: %2$s";
+ public static final String MESSAGE_UNDO_SUCCESS = "Undo add %1$s to entry: %2$s";
+ public static final String MESSAGE_ALREADY_EXISTS = "All specified tags already exist on entry: %1$s";
private final int targetIndex;
private final UniqueTagList tagsToAdd;
private Entry taskToTag;
+ private LocalDateTime originalLastModifiedTime;
public TagCommand(int targetIndex, Set tags) throws IllegalValueException {
this.targetIndex = targetIndex;
@@ -169,81 +217,138 @@ public class TagCommand extends UndoableCommand {
@Override
public CommandResult execute() {
assert model != null;
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
+ assert !tagsToAdd.isEmpty(); //should be handled in the parser
+ if (getCommandState()==CommandState.PRE_EXECUTION) {
+ UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ }
+
+ taskToTag = lastShownList.get(targetIndex - 1);
+ originalLastModifiedTime = taskToTag.getLastModifiedTime();
+ tagsToAdd.removeFrom(taskToTag.getTags());
+ }
+
+ if (tagsToAdd.isEmpty()){
indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ return new CommandResult(String.format(MESSAGE_ALREADY_EXISTS, taskToTag));
}
-
- taskToTag = lastShownList.get(targetIndex - 1);
- tagsToAdd.removeFrom(taskToTag.getTags());
-
try {
model.tagTask(taskToTag, tagsToAdd);
} catch (EntryNotFoundException e) {
assert false : "The target entry cannot be missing";
}
- setExecutionIsSuccessful();
- return new CommandResult(String.format(MESSAGE_SUCCESS, taskToTag));
+ setUndoable();
+ return new CommandResult(String.format(MESSAGE_SUCCESS, tagsToAdd, taskToTag));
}
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState() != CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
- };
+ }
assert model != null;
assert taskToTag != null;
assert tagsToAdd != null;
try {
model.untagTask(taskToTag, tagsToAdd);
+ model.updateLastModifiedTime(taskToTag, originalLastModifiedTime);
} catch (EntryNotFoundException enfe) {
assert false : "The target entry cannot be missing";
}
- return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, taskToTag));
+ setRedoable();
+ return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, tagsToAdd, taskToTag));
}
}
```
###### /java/seedu/address/logic/commands/UndoableCommand.java
``` java
+/**
+ * Represents a command which changes to the model can be undone using the undo command
+ * and redone using the redo command
+ */
public abstract class UndoableCommand extends Command {
- public String MESSAGE_UNDO_FAIL = "Command cannot be undone before it is successfully executed!";
- protected boolean executionIsSuccessful=false;
+ public enum CommandState {UNDOABLE, REDOABLE, PRE_EXECUTION};
+
+ public String MESSAGE_UNDO_FAIL = "Command cannot be undone before it is successfully executed!";
+ public String MESSAGE_REDO_FAIL = "Command cannot be redone before it is successfully unexecuted!";
+ public CommandState commandState = CommandState.PRE_EXECUTION;
public abstract CommandResult unexecute();
- public void setExecutionIsSuccessful() {
- executionIsSuccessful=true;
+ /**
+ * Re-executes the command which was previously undone.
+ */
+ public CommandResult reExecute() {
+ if (getCommandState()!=CommandState.REDOABLE){
+ return new CommandResult(MESSAGE_UNDO_FAIL);
+ };
+ return execute();
+ }
+
+ public void setUndoable() {
+ commandState = CommandState.UNDOABLE;
}
- public boolean getExecutionIsSuccessful() {
- return executionIsSuccessful;
+ public void setRedoable() {
+ commandState = CommandState.REDOABLE;
+ }
+
+ public CommandState getCommandState() {
+ return commandState;
}
}
```
###### /java/seedu/address/logic/commands/UndoableCommandHistory.java
``` java
+/**
+ * Stack of successfully executed undoable commands.
+ */
public class UndoableCommandHistory {
- Deque commandInternalQueue = new ArrayDeque();
+ Deque commandInternalUndoQueue = new ArrayDeque();
+ Deque commandInternalRedoQueue = new ArrayDeque();
public static class UndoableCommandNotFoundException extends Exception {};
- public void push(UndoableCommand undoableCommand) {
- commandInternalQueue.addFirst(undoableCommand);
+ /** Push a new Undoable command to history.
+ * Resets the redo queue since the redo commands are no longer in sync.
+ */
+ public void pushToHistory(UndoableCommand undoableCommand) {
+ commandInternalRedoQueue = new ArrayDeque();
+ commandInternalUndoQueue.addFirst(undoableCommand);
+ }
+
+ public void pushToRedoStack(UndoableCommand undoableCommand) {
+ commandInternalRedoQueue.addFirst(undoableCommand);
+ }
+
+ public void pushToUndoStack(UndoableCommand undoableCommand) {
+ commandInternalUndoQueue.addFirst(undoableCommand);
+ }
+
+ public UndoableCommand getFromUndoStack() throws UndoableCommandNotFoundException {
+ if (commandInternalUndoQueue.isEmpty()){
+ throw new UndoableCommandNotFoundException();
+ }
+ return commandInternalUndoQueue.poll();
}
- public UndoableCommand getMostRecentUndoableCommand() throws UndoableCommandNotFoundException {
- if (commandInternalQueue.isEmpty()){
+ public UndoableCommand getFromRedoStack() throws UndoableCommandNotFoundException {
+ if (commandInternalRedoQueue.isEmpty()){
throw new UndoableCommandNotFoundException();
}
- return commandInternalQueue.poll();
+ return commandInternalRedoQueue.poll();
}
}
```
###### /java/seedu/address/logic/commands/UndoCommand.java
``` java
+/**
+ * Undo the previous undoable command.
+ */
public class UndoCommand extends Command {
public static final String COMMAND_WORD = "undo";
@@ -260,8 +365,12 @@ public class UndoCommand extends Command {
@Override
public CommandResult execute() {
try {
- UndoableCommand undoableCommand = undoableCommandHistory.getMostRecentUndoableCommand();
- return undoableCommand.unexecute();
+ UndoableCommand undoableCommand = undoableCommandHistory.getFromUndoStack();
+ CommandResult undoableCommandResult = undoableCommand.unexecute();
+ if (undoableCommand.getCommandState() == CommandState.REDOABLE) {
+ undoableCommandHistory.pushToRedoStack(undoableCommand);
+ }
+ return undoableCommandResult;
} catch (UndoableCommandNotFoundException e) {
return new CommandResult(MESSAGE_FAILURE);
}
@@ -270,11 +379,14 @@ public class UndoCommand extends Command {
```
###### /java/seedu/address/logic/commands/UnmarkCommand.java
``` java
+/**
+ * Unmarks an entry.
+ */
public class UnmarkCommand extends UndoableCommand {
public static final String COMMAND_WORD = "unmark";
public static final String MESSAGE_USAGE = COMMAND_WORD
- + ": Unmarks the entry as completed. "
+ + ": Unmarks the entry. "
+ "Identified by the index number used in the last entry listing.\n"
+ "Parameters: INDEX (must be a positive integer)\n"
+ "Example: " + COMMAND_WORD + " 1";
@@ -285,39 +397,42 @@ public class UnmarkCommand extends UndoableCommand {
private final int targetIndex;
private Entry entryToUnmark;
private boolean originalIsMarked;
+ private LocalDateTime originalLastModifiedTime;
public UnmarkCommand(int targetIndex) {
this.targetIndex = targetIndex;
}
-
@Override
public CommandResult execute() {
-
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
- indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ assert model != null;
+ if (getCommandState()==CommandState.PRE_EXECUTION) {
+ UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ }
+
+ entryToUnmark = lastShownList.get(targetIndex - 1);
+ originalLastModifiedTime = entryToUnmark.getLastModifiedTime();
+ originalIsMarked= entryToUnmark.isMarked();
}
- entryToUnmark = lastShownList.get(targetIndex - 1);
- originalIsMarked= entryToUnmark.isMarked();
-
try {
model.unmarkTask(entryToUnmark);
} catch (EntryNotFoundException pnfe) {
assert false : "The target entry cannot be missing";
}
- setExecutionIsSuccessful();
+ setUndoable();
return new CommandResult(String.format(MESSAGE_SUCCESS, entryToUnmark));
}
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState() != CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
- };
+ }
assert model != null;
assert entryToUnmark != null;
@@ -327,16 +442,20 @@ public class UnmarkCommand extends UndoableCommand {
} else {
model.unmarkTask(entryToUnmark);
}
+ model.updateLastModifiedTime(entryToUnmark, originalLastModifiedTime);
} catch (EntryNotFoundException enfe) {
assert false : "The target entry cannot be missing";
}
-
+ setRedoable();
return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, entryToUnmark));
}
}
```
###### /java/seedu/address/logic/commands/UntagCommand.java
``` java
+/*
+ * Remove tags from an entry.
+ */
public class UntagCommand extends UndoableCommand {
public static final String COMMAND_WORD = "untag";
@@ -344,13 +463,15 @@ public class UntagCommand extends UndoableCommand {
public static final String MESSAGE_USAGE = COMMAND_WORD
+ ": Removes tags from task. "
+ "Parameters: TASK_ID TAG[,...] "
- + "Example: " + COMMAND_WORD + " 2 shopping, food";
+ + "Example: " + COMMAND_WORD + " 2 #shopping #food";
- public static final String MESSAGE_SUCCESS = "Removed tags from entry: %1$s";
- public static final String MESSAGE_UNDO_SUCCESS = "Undo remove tags from entry: %1$s";
+ public static final String MESSAGE_SUCCESS = "Removed %1$s from entry: %2$s";
+ public static final String MESSAGE_UNDO_SUCCESS = "Undo remove %1$s from entry: %2$s";
+ public static final String MESSAGE_NON_EXISTENT = "None of the specified tags exist in the entry: %1$s";
private final int targetIndex;
private Entry taskToUntag;
+ private LocalDateTime originalLastModifiedTime;
private final UniqueTagList tagsToRemove;
@@ -365,41 +486,52 @@ public class UntagCommand extends UndoableCommand {
}
@Override
- public CommandResult execute() {
- UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
-
- if (lastShownList.size() < targetIndex) {
- indicateAttemptToExecuteIncorrectCommand();
- return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ public CommandResult execute() {
+ assert model != null;
+ assert !tagsToRemove.isEmpty(); //should be handled in the parser
+ if (getCommandState()==CommandState.PRE_EXECUTION) {
+ UnmodifiableObservableList lastShownList = model.getFilteredPersonList();
+
+ if (lastShownList.size() < targetIndex) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(Messages.MESSAGE_INVALID_ENTRY_DISPLAYED_INDEX);
+ }
+
+ taskToUntag = lastShownList.get(targetIndex - 1);
+ originalLastModifiedTime = taskToUntag.getLastModifiedTime();
+ tagsToRemove.retainAll(taskToUntag.getTags());
}
-
- taskToUntag = lastShownList.get(targetIndex - 1);
- tagsToRemove.retainAll(taskToUntag.getTags());
+ if (tagsToRemove.isEmpty()){
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(String.format(MESSAGE_NON_EXISTENT, taskToUntag));
+ }
try {
model.untagTask(taskToUntag, tagsToRemove);
} catch (EntryNotFoundException e) {
assert false : "The target entry cannot be missing";
}
- setExecutionIsSuccessful();
- return new CommandResult(String.format(MESSAGE_SUCCESS, taskToUntag));
+ setUndoable();
+ return new CommandResult(String.format(MESSAGE_SUCCESS, tagsToRemove, taskToUntag));
}
@Override
public CommandResult unexecute() {
- if (!executionIsSuccessful){
+ if (getCommandState() != CommandState.UNDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
- };
+ }
assert model != null;
assert taskToUntag != null;
assert tagsToRemove != null;
try {
model.tagTask(taskToUntag, tagsToRemove);
+ model.updateLastModifiedTime(taskToUntag, originalLastModifiedTime);
} catch (EntryNotFoundException enfe) {
assert false : "The target entry cannot be missing";
}
- return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, taskToUntag));
+ setRedoable();
+ return new CommandResult(String.format(MESSAGE_UNDO_SUCCESS, tagsToRemove, taskToUntag));
}
}
@@ -409,13 +541,58 @@ public class UntagCommand extends UndoableCommand {
else if (command instanceof UndoCommand) {
((UndoCommand) command).setData(model, undoableCommandHistory);
}
+ else if (command instanceof RedoCommand) {
+ ((RedoCommand) command).setData(model, undoableCommandHistory);
+ }
else {
command.setData(model);
}
CommandResult commandResult = command.execute();
if (command instanceof UndoableCommand &&
- ((UndoableCommand) command).getExecutionIsSuccessful()) {
- undoableCommandHistory.push((UndoableCommand) command);
+ ((UndoableCommand) command).getCommandState() == CommandState.UNDOABLE) {
+ undoableCommandHistory.pushToHistory((UndoableCommand) command);
}
+
+ commandHistoryManager.appendCommand(commandText);
+ commandHistoryManager.resetPosition();
+
+```
+###### /java/seedu/address/model/task/EntryViewComparator.java
+``` java
+/**
+ * Comparator for the list view.
+ */
+public class EntryViewComparator implements Comparator{
+ @Override
+ public int compare(Entry o1, Entry o2) {
+ LocalDateTime thisDateTime = o1.getComparableTime();
+ LocalDateTime oDateTime = o2.getComparableTime();
+ int compareValue = thisDateTime.compareTo(oDateTime);
+ if (compareValue==0) {
+ return o1.getTitle().toString().compareTo(o2.getTitle().toString());
+ }
+ return thisDateTime.compareTo(oDateTime);
+ }
+
+}
+```
+###### /java/seedu/address/model/task/Event.java
+``` java
+ @Override
+ public LocalDateTime getComparableTime() {
+ return startTime.get();
+ }
+```
+###### /java/seedu/address/model/task/Task.java
+``` java
+ @Override
+ public LocalDateTime getComparableTime() {
+ if (isFloatingTask()) {
+ return lastModifiedTime.get();
+ }
+ return deadline.get();
+ }
+
+}
```
diff --git a/collated/main/A0126539Y.md b/collated/main/A0126539Y.md
index f874a60d7856..f06be20eaa69 100644
--- a/collated/main/A0126539Y.md
+++ b/collated/main/A0126539Y.md
@@ -42,7 +42,7 @@
* @throws IllegalValueException if any of the raw values are invalid
*/
public AddCommand(String title, LocalDateTime startTime, LocalDateTime endTime, Set tags, String description)
- throws IllegalValueException {
+ throws IllegalValueException, IllegalArgumentException {
final Set tagSet = new HashSet<>();
for (String tagName : tags) {
tagSet.add(new Tag(tagName));
@@ -55,7 +55,8 @@
endTime,
new UniqueTagList(tagSet),
false,
- description
+ description,
+ LocalDateTime.now()
);
}
else if (startTime == null){
@@ -64,7 +65,8 @@
endTime,
new UniqueTagList(tagSet),
false,
- description
+ description,
+ LocalDateTime.now()
);
}
else if (startTime != null && endTime == null) {
@@ -163,6 +165,19 @@ public class OptionCommand extends Command{
}
```
###### /java/seedu/address/model/PredicateBuilder.java
+``` java
+ private Predicate addTypePredicateIfExist(Predicate pred, String entryType) throws IllegalValueException {
+ Predicate result = pred;
+ if (entryType != null && !entryType.isEmpty()) {
+ if (!entryType.equalsIgnoreCase(TypeQualifier.EVENT_TYPE_STRING) && !entryType.equalsIgnoreCase(TypeQualifier.TASK_TYPE_STRING)) {
+ throw new IllegalValueException(TypeQualifier.INVALID_TYPE_MESSAGE);
+ }
+ result = result.and(buildTypePredicate(entryType));
+ }
+ return result;
+ }
+```
+###### /java/seedu/address/model/PredicateBuilder.java
``` java
private class DateAfterQualifier implements Qualifier {
private LocalDateTime startDate;
@@ -224,6 +239,37 @@ public class OptionCommand extends Command{
}
}
+```
+###### /java/seedu/address/model/PredicateBuilder.java
+``` java
+ private class TypeQualifier implements Qualifier {
+ private String type;
+ private static final String TASK_TYPE_STRING = "task";
+ private static final String EVENT_TYPE_STRING = "event";
+ private static final String INVALID_TYPE_MESSAGE = "Invalid type filtering. Please use either 'task' or 'event' as value.";
+
+ TypeQualifier(String type) {
+ this.type = type;
+ }
+
+ @Override
+ public boolean run(Entry entry) {
+ switch (type) {
+ case TASK_TYPE_STRING:
+ return entry instanceof Task;
+ case EVENT_TYPE_STRING:
+ return entry instanceof Event;
+ default:
+ return false;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Type of: " + type;
+ }
+ }
+
```
###### /java/seedu/address/model/task/Event.java
``` java
@@ -231,19 +277,26 @@ public final class Event extends Entry{
protected ObjectProperty startTime;
protected ObjectProperty endTime;
static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("EEE, MMM d 'at' HH:mm");
+ private static final String INVALID_START_END_TIME = "Invalid start and end time. i.e: start time is after end time.";
- public Event(Title title, LocalDateTime startTime, LocalDateTime endTime, UniqueTagList tags, boolean isMarked, String description) {
- assert !CollectionUtil.isAnyNull(title, tags, description, startTime, endTime);
+ public Event(Title title, LocalDateTime startTime, LocalDateTime endTime, UniqueTagList tags, boolean isMarked, String description,
+ LocalDateTime lastModifiedTime) throws IllegalArgumentException{
+ assert !CollectionUtil.isAnyNull(title, tags, description, startTime, endTime, lastModifiedTime);
+ if(startTime.isAfter(endTime)) {
+ throw new IllegalArgumentException(INVALID_START_END_TIME);
+ }
this.title = new SimpleObjectProperty<>(Title.copy(title));
this.tags = new SimpleObjectProperty<>(new UniqueTagList(tags));
this.isMarked = new SimpleBooleanProperty(Boolean.valueOf(isMarked));
this.description = new SimpleStringProperty(description);
this.startTime = new SimpleObjectProperty<>(startTime);
this.endTime = new SimpleObjectProperty<>(endTime);
+ this.lastModifiedTime = new SimpleObjectProperty<>(lastModifiedTime);
}
- public Event(Entry entry) {
- this(entry.getTitle(), ((Event)entry).getStartTime(), ((Event)entry).getEndTime(), entry.getTags(), entry.isMarked(), entry.getDescription());
+ public Event(Entry entry) throws IllegalArgumentException{
+ this(entry.getTitle(), ((Event)entry).getStartTime(), ((Event)entry).getEndTime(), entry.getTags(), entry.isMarked(),
+ entry.getDescription(), entry.getLastModifiedTime());
}
public LocalDateTime getStartTime() {
@@ -303,7 +356,6 @@ public final class Event extends Entry{
&& this.isSameStateAs((Event) other));
}
- @Override
```
###### /java/seedu/address/model/task/Task.java
``` java
@@ -312,7 +364,7 @@ public class Task extends Entry {
protected ObjectProperty deadline;
- public Task(Title title, LocalDateTime deadline, UniqueTagList tags, boolean isMarked, String description) {
+ public Task(Title title, LocalDateTime deadline, UniqueTagList tags, boolean isMarked, String description, LocalDateTime lastModifiedTime) {
assert !CollectionUtil.isAnyNull(title, tags, description);
this.title = new SimpleObjectProperty<>(Title.copy(title));
this.tags = new SimpleObjectProperty<>(new UniqueTagList(tags));
@@ -323,13 +375,14 @@ public class Task extends Entry {
} else {
this.deadline = new SimpleObjectProperty<>();
}
+ this.lastModifiedTime = new SimpleObjectProperty<>(lastModifiedTime);
}
/**
* Every field must be present and not null.
*/
public Task(Title title, UniqueTagList tags) {
- this(title, null, tags, false, "");
+ this(title, null, tags, false, "", LocalDateTime.MIN);
}
@@ -337,7 +390,7 @@ public class Task extends Entry {
* Copy constructor.
*/
public Task(Entry source) {
- this(source.getTitle(), null, source.getTags(), source.isMarked(), source.getDescription());
+ this(source.getTitle(), null, source.getTags(), source.isMarked(), source.getDescription(), source.getLastModifiedTime());
if (source instanceof Task) {
setDeadline(((Task)source).getDeadline()) ;
}
@@ -383,8 +436,11 @@ public class Task extends Entry {
&& other.getAsText().equals(this.getAsText())
&& other.isMarked() == this.isMarked());
}
+
+ public boolean isFloatingTask() {
+ return deadline.get() == null;
+ }
- @Override
```
###### /java/seedu/address/model/task/Update.java
``` java
diff --git a/collated/main/A0126539Yreused.md b/collated/main/A0126539Yreused.md
new file mode 100644
index 000000000000..ecb262726a0e
--- /dev/null
+++ b/collated/main/A0126539Yreused.md
@@ -0,0 +1,7 @@
+# A0126539Yreused
+###### /java/seedu/address/model/PredicateBuilder.java
+``` java
+ private Predicate buildTypePredicate(String entryType) {
+ return new PredicateExpression(new TypeQualifier(entryType))::satisfies;
+ }
+```
diff --git a/collated/main/A0127828W.md b/collated/main/A0127828W.md
index caf61a821449..ad2d9f5eab66 100644
--- a/collated/main/A0127828W.md
+++ b/collated/main/A0127828W.md
@@ -1,4 +1,81 @@
# A0127828W
+###### /java/seedu/address/logic/commands/CommandHistory.java
+``` java
+ *
+ * Maintains an internal data structure to keep track of input commands
+ * Use a pointer to indicate which command should be returned when getter methods are called
+ */
+public class CommandHistory {
+ private ArrayList commandHistory;
+ private Integer commandHistoryIndex;
+
+ /**
+ * Initialize command history
+ * A commandHistoryIndex of -1 indicates that it has reached the limit
+ */
+ public CommandHistory() {
+ commandHistory = new ArrayList<>();
+ commandHistoryIndex = -1;
+ }
+
+ /**
+ * Append a new command, represented by a string, to an internal storage
+ * @param string
+ */
+ public void appendCommand(String string) {
+ if (string == null || string.isEmpty()) {
+ return;
+ }
+ commandHistory.add(string);
+ commandHistoryIndex++;
+ }
+
+ /**
+ * Return the previous command in the command history
+ * @return
+ */
+ public String getPreviousCommand() {
+ if (isAtOldestCommand()) {
+ return null;
+ }
+ commandHistoryIndex--;
+ String returnCommand = commandHistory.get(commandHistoryIndex);
+
+ return returnCommand;
+ }
+
+ /**
+ * Return the next command in the command history
+ * @return
+ */
+ public String getNextCommand() {
+ if (isAtNewestCommand()) {
+ return "";
+ }
+ commandHistoryIndex++;
+ String returnCommand = commandHistory.get(commandHistoryIndex);
+
+ return returnCommand;
+ }
+
+ /**
+ * Reset the position of pointer to the latest command
+ */
+ public void resetPosition() {
+ commandHistoryIndex = commandHistory.size();
+ }
+
+ private boolean isAtOldestCommand() {
+ return commandHistory.isEmpty()
+ || commandHistoryIndex == 0;
+ }
+
+ private boolean isAtNewestCommand() {
+ return commandHistory.isEmpty()
+ || commandHistoryIndex == commandHistory.size() - 1;
+ }
+}
+```
###### /java/seedu/address/logic/commands/ListCommand.java
``` java
*/
@@ -6,6 +83,8 @@ public class ListCommand extends Command {
public static final String COMMAND_WORD = "list";
+ public static final String LIST_ALL_COMMAND_WORD = "list-all";
+
public static final String MESSAGE_SUCCESS = "Listed all entries";
public static final String MESSAGE_USAGE = COMMAND_WORD + ": List all entries whose titles contain any of "
@@ -20,16 +99,22 @@ public class ListCommand extends Command {
public static final String AFTER_FLAG = "after/";
public static final String BEFORE_FLAG = "before/";
public static final String ON_FLAG = "on/";
+ public static final String TYPE_FLAG = "type/";
private Set keywords;
private Set tags;
private LocalDateTime startDate;
private LocalDateTime endDate;
private LocalDateTime onDate;
+ private final boolean includeCompleted;
+ private final String entryType;
private final PredicateBuilder predicateBuilder = new PredicateBuilder();
- public ListCommand() {}
+ public ListCommand(boolean includeCompleted, String entryType) {
+ this.includeCompleted = includeCompleted;
+ this.entryType = entryType;
+ }
public void setKeywords(Set keywords) {
this.keywords = keywords;
@@ -56,10 +141,15 @@ public class ListCommand extends Command {
if (isListAll()) {
return showAll();
} else {
- Predicate predicate = predicateBuilder.buildPredicate(keywords, tags, startDate, endDate, onDate);
- model.updateFilteredEntryListPredicate(predicate);
+ try {
+ Predicate predicate = predicateBuilder.buildPredicate(keywords, tags, startDate, endDate, onDate, includeCompleted, entryType);
+ model.updateFilteredEntryListPredicate(predicate);
+ return new CommandResult(getMessageForPersonListShownSummary(model.getFilteredPersonList().size()));
+ } catch (IllegalValueException e) {
+ indicateAttemptToExecuteIncorrectCommand();
+ return new CommandResult(e.getMessage());
+ }
- return new CommandResult(getMessageForPersonListShownSummary(model.getFilteredPersonList().size()));
}
}
@@ -77,7 +167,8 @@ public class ListCommand extends Command {
&& (tags == null || tags.isEmpty())
&& startDate == null
&& endDate == null
- && onDate == null;
+ && onDate == null
+ && includeCompleted;
}
}
```
@@ -118,58 +209,38 @@ public class ListCommand extends Command {
* full command args string
* @return the prepared command
*/
- private Command prepareList(String args) {
+ private Command prepareList(String args, boolean includeCompleted) {
// Guard statement
if (args.isEmpty()) {
- return new ListCommand();
+ return new ListCommand(includeCompleted, "");
}
- final ArgumentTokenizer argsTokenizer = new ArgumentTokenizer(startDatePrefix, endDatePrefix, onDatePrefix, tagPrefix);
+ final ArgumentTokenizer argsTokenizer = new ArgumentTokenizer(startDatePrefix, endDatePrefix, onDatePrefix, tagPrefix, typePrefix);
argsTokenizer.tokenize(args);
try {
- ListCommand listCommand = new ListCommand();
+ String typeString = unwrapOptionalStringOrEmpty(argsTokenizer.getValue(typePrefix));
+ ListCommand listCommand = new ListCommand(includeCompleted, typeString);
// keywords delimited by whitespace
- Optional keywordsString = argsTokenizer.getPreamble();
- if (keywordsString.isPresent()) {
- String[] keywords = keywordsString.get().split("\\s+");
-
- Set keywordSet = new HashSet<>(Arrays.asList(keywords));
- keywordSet.removeIf(s -> "".equals(s));
- listCommand.setKeywords(keywordSet);
- }
+ setKeywords(argsTokenizer, listCommand);
String onDateString = unwrapOptionalStringOrEmpty(argsTokenizer.getValue(onDatePrefix));
String startDateString = unwrapOptionalStringOrEmpty(argsTokenizer.getValue(startDatePrefix));
String endDateString = unwrapOptionalStringOrEmpty(argsTokenizer.getValue(endDatePrefix));
Set tags = getTagsFromArgs(argsTokenizer);
- if (!tags.isEmpty()) {
- listCommand.setTags(tags);
- }
+ setTags(listCommand, tags);
// Ranged search and specific-day search should be mutually exclusive
- if (!onDateString.isEmpty() && (!startDateString.isEmpty() || !endDateString.isEmpty())) {
+ if (isNotMutuallyExclusiveDates(onDateString, startDateString, endDateString)) {
return new IncorrectCommand(String.format(MESSAGE_INVALID_COMMAND_FORMAT, ListCommand.MESSAGE_MUTUALLY_EXCLUSIVE_OPTIONS));
}
- if (onDateString.isEmpty()) {
- final LocalDateTime startDate = getLocalDateTimeFromArgument(startDateString);
- LocalDateTime endDate = getLocalDateTimeFromArgument(endDateString);
- if (endDate != null) {
- endDate = endDate.plusDays(1).minusSeconds(1);
- }
-
- if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
- return new IncorrectCommand(ListCommand.MESSAGE_INVALID_DATE);
- }
-
- listCommand.setStartDate(startDate);
- listCommand.setEndDate(endDate);
+ if (onDateString.isEmpty() && !setValidDateRange(listCommand, startDateString, endDateString)) {
+ return new IncorrectCommand(ListCommand.MESSAGE_INVALID_DATE);
} else {
final LocalDateTime onDate = getLocalDateTimeFromArgument(onDateString);
-
listCommand.setOnDate(onDate);
}
@@ -179,6 +250,49 @@ public class ListCommand extends Command {
}
}
+ private void setKeywords(ArgumentTokenizer argsTokenizer, ListCommand listCommand) {
+ Optional keywordsString = argsTokenizer.getPreamble();
+ if (keywordsString.isPresent()) {
+ String[] keywords = keywordsString.get().split("\\s+");
+
+ Set keywordSet = new HashSet<>(Arrays.asList(keywords));
+ keywordSet.removeIf(s -> "".equals(s));
+ listCommand.setKeywords(keywordSet);
+ }
+ }
+
+ private boolean isNotMutuallyExclusiveDates(String onDateString, String startDateString, String endDateString) {
+ return !onDateString.isEmpty() && (!startDateString.isEmpty() || !endDateString.isEmpty());
+ }
+
+ /**
+ * Return false if the provided dates are invalid
+ * Return true and set date attributes to listCommand otherwise
+ *
+ * @throws IllegalValueException
+ */
+ private boolean setValidDateRange(ListCommand listCommand, String startDateString, String endDateString) throws IllegalValueException {
+ final LocalDateTime startDate = getLocalDateTimeFromArgument(startDateString);
+ LocalDateTime endDate = getLocalDateTimeFromArgument(endDateString);
+ if (endDate != null) {
+ endDate = endDate.plusDays(1).minusSeconds(1);
+ }
+
+ if (startDate != null && endDate != null && startDate.isAfter(endDate)) {
+ return false;
+ }
+
+ listCommand.setStartDate(startDate);
+ listCommand.setEndDate(endDate);
+ return true;
+ }
+
+ private void setTags(ListCommand listCommand, Set tags) {
+ if (!tags.isEmpty()) {
+ listCommand.setTags(tags);
+ }
+ }
+
private static String unwrapOptionalStringOrEmpty(Optional optional) {
if (optional.isPresent()) {
return optional.get();
@@ -205,141 +319,66 @@ public class PredicateBuilder {
/**
* Return a chained Predicate from all the conditions indicated in params
* @param keywords
+ * @param tags
* @param startDate
* @param endDate
* @param onDate
- * @param tags
+ * @param entryType TODO
* @return pred the chained Predicate
+ * @throws IllegalValueException TODO
*/
- public Predicate buildPredicate(Set keywords, Set tags, LocalDateTime startDate, LocalDateTime endDate, LocalDateTime onDate) {
+ public Predicate buildPredicate(Set keywords, Set tags, LocalDateTime startDate, LocalDateTime endDate, LocalDateTime onDate, boolean includeCompleted, String entryType) throws IllegalValueException {
// Initial predicate
Predicate pred = e -> true;
- if (keywords != null && !keywords.isEmpty()) {
- pred = pred.and(buildKeywordsPredicate(keywords));
- }
-
- if (tags != null && !tags.isEmpty()) {
- pred = pred.and(buildTagsPredicate(tags));
- }
-
- if (onDate != null) {
- pred = pred.and(buildOnPredicate(onDate));
- } else {
- if (startDate != null) {
- pred = pred.and(buildAfterPredicate(startDate));
- }
- if (endDate != null) {
- pred = pred.and(buildBeforePredicate(endDate));
- }
- }
+ pred = addCompletedPredicateIfExist(pred, includeCompleted);
+ pred = addKeywordsPredicateIfExist(pred, keywords);
+ pred = addTagPredicateIfExist(pred, tags);
+ pred = addDatePredicateIfExist(pred, onDate, startDate, endDate);
+ pred = addTypePredicateIfExist(pred, entryType);
return pred;
}
-
- private Predicate buildKeywordsPredicate(Set keywords) {
- return new PredicateExpression(new TitleQualifier(keywords))::satisfies;
- }
-
- private Predicate buildTagsPredicate(Set tags) {
- return new PredicateExpression(new TagsQualifier(tags))::satisfies;
- }
-
- private Predicate buildBeforePredicate(LocalDateTime endDate) {
- return new PredicateExpression(new DateBeforeQualifier(endDate))::satisfies;
- }
-
- private Predicate buildAfterPredicate(LocalDateTime startDate) {
- return new PredicateExpression(new DateAfterQualifier(startDate))::satisfies;
- }
-
- private Predicate buildOnPredicate(LocalDateTime onDate) {
- return new PredicateExpression(new DateOnQualifier(onDate))::satisfies;
- }
- //@author A0127828W-reused
- //========== Inner classes/interfaces used for filtering ==================================================
-
- interface Expression {
- boolean satisfies(Entry entry);
- String toString();
- }
-
- private class PredicateExpression implements Expression {
-
- private final Qualifier qualifier;
-
- PredicateExpression(Qualifier qualifier) {
- this.qualifier = qualifier;
- }
-
- @Override
- public boolean satisfies(Entry entry) {
- return qualifier.run(entry);
- }
-
- @Override
- public String toString() {
- return qualifier.toString();
+
+ private Predicate addCompletedPredicateIfExist(Predicate pred, boolean includeCompleted) {
+ Predicate result = pred;
+ if (!includeCompleted) {
+ result = result.and(buildCompletedPredicate(includeCompleted));
}
+ return result;
}
-
- interface Qualifier {
- boolean run(Entry entry);
- String toString();
- }
-
- //@author A0127828W
- private class TitleQualifier implements Qualifier {
- private Set titleKeyWords;
-
- TitleQualifier(Set nameKeyWords) {
- this.titleKeyWords = nameKeyWords;
- }
-
- @Override
- public boolean run(Entry entry) {
- return titleKeyWords.stream()
- .filter(keyword -> StringUtil.containsIgnoreCase(entry.getTitle().fullTitle, keyword))
- .findAny()
- .isPresent();
- }
-
- @Override
- public String toString() {
- return "name=" + String.join(", ", titleKeyWords);
+
+ private Predicate addKeywordsPredicateIfExist(Predicate pred, Set keywords) {
+ Predicate result = pred;
+ if (keywords != null && !keywords.isEmpty()) {
+ result = result.and(buildKeywordsPredicate(keywords));
}
+ return result;
}
-
- private class TagsQualifier implements Qualifier {
- private Set tags;
-
- TagsQualifier(Set tags) {
- this.tags = tags;
+
+ private Predicate addTagPredicateIfExist(Predicate pred, Set tags) {
+ Predicate result = pred;
+ if (tags != null && !tags.isEmpty()) {
+ result = result.and(buildTagsPredicate(tags));
}
-
- @Override
- public boolean run(Entry entry) {
- Set tags = new HashSet<>();
- try {
- for (String t: this.tags) {
- tags.add(new Tag(t));
- }
- } catch (IllegalValueException ive) {
- System.err.println(MESSAGE_TAG_CONSTRAINTS);
- return false;
+ return result;
+ }
+
+ private Predicate addDatePredicateIfExist(Predicate pred, LocalDateTime onDate, LocalDateTime startDate, LocalDateTime endDate) {
+ Predicate result = pred;
+ if (onDate != null) {
+ result = result.and(buildOnPredicate(onDate));
+ } else {
+ if (startDate != null) {
+ result = result.and(buildAfterPredicate(startDate));
+ }
+ if (endDate != null) {
+ result = result.and(buildBeforePredicate(endDate));
}
-
- return tags.stream()
- .filter(tag -> entry.getTags().contains(tag))
- .findAny()
- .isPresent();
- }
-
- @Override
- public String toString() {
- return "tags=" + String.join(", ", tags);
}
+ return result;
}
+
```
###### /java/seedu/address/model/PredicateBuilder.java
``` java
@@ -376,6 +415,31 @@ public class PredicateBuilder {
return "Due on: " + onDate.toString();
}
}
+
+```
+###### /java/seedu/address/model/PredicateBuilder.java
+``` java
+ private class CompletedQualifier implements Qualifier {
+ private boolean includeCompleted;
+
+ CompletedQualifier(boolean includeCompleted) {
+ this.includeCompleted = includeCompleted;
+ }
+
+ @Override
+ public boolean run(Entry entry) {
+ if (includeCompleted) {
+ return true;
+ } else {
+ return !entry.isMarked();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "Include completed: " + includeCompleted;
+ }
+ }
}
```
###### /java/seedu/address/model/task/Event.java
@@ -388,7 +452,7 @@ public class PredicateBuilder {
Date interpreted = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
return dateTime.format(DATE_TIME_FORMATTER);
}
-}
+
```
###### /java/seedu/address/model/task/Task.java
``` java
@@ -399,5 +463,32 @@ public class PredicateBuilder {
Date interpreted = Date.from(dateTime.atZone(ZoneId.systemDefault()).toInstant());
return prettyTime.format(interpreted);
}
-}
+
+```
+###### /java/seedu/address/ui/CommandArea.java
+``` java
+ @FXML
+ private void handleKeyPressedCommandArea(KeyEvent event) {
+ switch (event.getCode()) {
+ case UP:
+ fillUpCommandLine(commandHistoryManager.getPreviousCommand());
+ logger.info("UP key entered");
+ break;
+ case DOWN:
+ fillUpCommandLine(commandHistoryManager.getNextCommand());
+ logger.info("DOWN key entered");
+ break;
+ }
+ }
+
+ /**
+ * Fill the command line with an input string
+ */
+ private void fillUpCommandLine(String command) {
+ if (command == null) {
+ logger.info("Reached command history limit.");
+ return;
+ }
+ cmdLine.setText(command);
+ }
```
diff --git a/collated/test/A0121501E.md b/collated/test/A0121501E.md
new file mode 100644
index 000000000000..6b58c17cde03
--- /dev/null
+++ b/collated/test/A0121501E.md
@@ -0,0 +1,678 @@
+# A0121501E
+###### \java\seedu\address\logic\LogicManagerTest.java
+``` java
+ @Test
+ public void execute_tag_withoutTagnameArgs_errorMessageShown() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Task t1Copy = helper.generateTask(1);
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(String.format(MESSAGE_INVALID_COMMAND_FORMAT, TagCommand.MESSAGE_USAGE));
+
+ assertCommandBehavior("tag 1", expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_tag_allowAlphanumericSpaceUnderscore() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+
+ Tag tag1 = new Tag("tag10");
+ Tag tag2 = new Tag("tag_10");
+ Tag tag3 = new Tag("tag 10");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2, tag3);
+
+ Task t1Copy = helper.generateTask(1);
+ t1Copy.addTags(tags);
+
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(TagCommand.MESSAGE_SUCCESS, tags, t1Copy);
+ assertCommandBehavior(helper.generateTagCommand(tags, 1),
+ expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_tag_allowAddExistingTags() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("tag2");
+ Tag tag3 = new Tag("tag3");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2, tag3);
+
+ Task t1Copy = helper.generateTask(1);
+ t1Copy.addTags(tags);
+
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(TagCommand.MESSAGE_SUCCESS, new UniqueTagList(tag3), t1Copy);
+ assertCommandBehavior(helper.generateTagCommand(tags, 1),
+ expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_tagOnlyExistingTags_errorMessageShown() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("tag2");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2);
+
+ Task t1Copy = helper.generateTask(1);
+ t1Copy.addTags(tags);
+
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(TagCommand.MESSAGE_ALREADY_EXISTS, t1Copy);
+ assertCommandBehavior(helper.generateTagCommand(tags, 1),
+ expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_untagInvalidArgsFormat_errorMessageShown() throws Exception {
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, UntagCommand.MESSAGE_USAGE);
+ assertIncorrectIndexFormatBehaviorForCommand("untag", expectedMessage);
+ }
+
+ @Test
+ public void execute_untagInvalidTagFormat_errorMessageShown() throws Exception {
+ String expectedMessage = String.format(Tag.MESSAGE_TAG_CONSTRAINTS);
+
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ List expectedList = helper.generateSortedEntryList(t1);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+
+ assertCommandBehavior("untag 1 " + TAG_FLAG + "**", expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_untagWithoutTagnameArgs_errorMessageShown() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Task t1Copy = helper.generateTask(1);
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(MESSAGE_INVALID_COMMAND_FORMAT, UntagCommand.MESSAGE_USAGE);
+
+ assertCommandBehavior("untag 1", expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_untag_allowAlphanumericSpaceUnderscore() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("tag_1");
+ Tag tag3 = new Tag("tag 1");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2, tag3);
+
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Task t1Copy = helper.generateTask(1);
+ t1Copy.removeTags(tags);
+
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ helper.addToAddressBook(expectedAB, new UniqueTagList(tag1));
+
+ String expectedMessage = String.format(UntagCommand.MESSAGE_SUCCESS, new UniqueTagList(tag1), t1Copy);
+ assertCommandBehavior(helper.generateUntagCommand(tags, 1),
+ expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_untagNonExistentTags_NonExistentMessageShown() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task t1 = helper.generateTask(1);
+ helper.addToModel(model, helper.generateEntryList(t1));
+
+ Tag tag3 = new Tag("tag3"); // Does not exist
+ UniqueTagList tags = new UniqueTagList(tag3);
+
+ Task t1Copy = helper.generateTask(1);
+
+ List expectedList = helper.generateSortedEntryList(t1Copy);
+ TaskManager expectedAB = helper.generateTodoList(expectedList);
+ String expectedMessage = String.format(UntagCommand.MESSAGE_NON_EXISTENT, t1Copy);
+ assertCommandBehavior(helper.generateUntagCommand(tags, 1),
+ expectedMessage, expectedAB, expectedList);
+ }
+
+ @Test
+ public void execute_mark_successful() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeMarked = helper.generateTask(1);
+ Task toBeMarkedCopy = helper.generateTask(1);
+
+ TaskManager expectedAB = new TaskManager();
+ toBeMarkedCopy.mark();
+ expectedAB.addTask(toBeMarkedCopy);
+
+ model.addTask(toBeMarked);
+ assertCommandBehavior("mark 1",
+ String.format(MarkCommand.MESSAGE_SUCCESS, toBeMarked),
+ expectedAB,
+ new ArrayList<>());
+ }
+
+ @Test
+ public void execute_mark_alreadyMarked() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task alreadyMarked = helper.generateTask(1);
+ alreadyMarked.mark();
+ Task toBeMarkedCopy = helper.generateTask(1);
+
+ TaskManager expectedAB = new TaskManager();
+ toBeMarkedCopy.mark();
+ expectedAB.addTask(toBeMarkedCopy);
+
+ model.addTask(alreadyMarked);
+ logic.execute(ListCommand.LIST_ALL_COMMAND_WORD);
+
+ assertCommandBehavior("mark 1",
+ String.format(MarkCommand.MESSAGE_SUCCESS, alreadyMarked),
+ expectedAB,
+ new ArrayList<>());
+ }
+
+ @Test
+ public void execute_unmark_successful() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeUnmarked = helper.generateTask(1);
+ Task toBeUnmarkedCopy = helper.generateTask(1);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeUnmarkedCopy);
+
+ model.addTask(toBeUnmarked);
+ assertCommandBehavior("unmark 1",
+ String.format(UnmarkCommand.MESSAGE_SUCCESS, toBeUnmarked),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_unmark_alreadyUnmarked() throws Exception {
+ TestDataHelper helper = new TestDataHelper();
+ Task alreadyUnmarked = helper.generateTask(1);
+ Task alreadyUnmarkedCopy = helper.generateTask(1);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(alreadyUnmarkedCopy);
+
+ model.addTask(alreadyUnmarked);
+ assertCommandBehavior("unmark 1",
+ String.format(UnmarkCommand.MESSAGE_SUCCESS, alreadyUnmarked),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+ //###################
+ //# Undo test cases #
+ //###################
+ @Test
+ public void execute_undoWithNoUndoableCommandsInHistory_errorMessageShown() throws Exception {
+ // setup expectations
+ TaskManager expectedAB = new TaskManager();
+
+ // command before undo
+ logic.execute("list");
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ UndoCommand.MESSAGE_FAILURE,
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoAdd_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.taskWithTags();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeAdded);
+ expectedAB.removeEntry(toBeAdded);
+
+ // commands before undo
+ logic.execute(helper.generateAddCommand(toBeAdded));
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(AddCommand.MESSAGE_UNDO_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoDelete_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task task1 = helper.generateTask(1);
+ Task task2 = helper.generateTask(2);
+ Task task3 = helper.generateTask(3);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(task1);
+ expectedAB.addTask(task2);
+ expectedAB.addTask(task3);
+ expectedAB.removeEntry(task2);
+ expectedAB.addTask(task2);
+
+ model.addTask(task1);
+ model.addTask(task2);
+ model.addTask(task3);
+ // command to undo
+ logic.execute("delete 2");
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(DeleteCommand.MESSAGE_UNDO_DELETE_PERSON_SUCCESS, task2),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoEdit_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toEdit = helper.taskWithTags();
+ Task toEditCopy = helper.taskWithTags();
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toEditCopy);
+
+ model.addTask(toEdit);
+ // command to undo
+ logic.execute(helper.generateEditCommand(toEdit, 1, "New Title"));
+
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(EditCommand.MESSAGE_UNDO_SUCCESS, toEditCopy),
+ expectedAB, expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoMark_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeMarked = helper.taskWithTags();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeMarked);
+
+ model.addTask(toBeMarked);
+ // command to undo
+ logic.execute("mark 1");
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(MarkCommand.MESSAGE_UNDO_SUCCESS, toBeMarked),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoUnmark_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeUnmarked = helper.taskWithTags();
+ toBeUnmarked.mark();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeUnmarked);
+
+ model.addTask(toBeUnmarked);
+ // command to undo
+ logic.execute(ListCommand.LIST_ALL_COMMAND_WORD);
+ logic.execute("unmark 1");
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(UnmarkCommand.MESSAGE_UNDO_SUCCESS, toBeUnmarked),
+ expectedAB,
+ new ArrayList<>());
+ }
+
+ @Test
+ public void execute_undoTag_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeTagged = helper.taskWithTags();
+ Task toBeTaggedCopy = helper.taskWithTags();
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("newTag");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeTagged);
+ expectedAB.addTag(tag2);
+
+ model.addTask(toBeTagged);
+ // command to undo
+ logic.execute(helper.generateTagCommand(tags, 1));
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(TagCommand.MESSAGE_UNDO_SUCCESS, new UniqueTagList(tag2), toBeTaggedCopy),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoUntag_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeUntagged = helper.taskWithTags();
+ Task toBeUntaggedCopy = helper.taskWithTags();
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("newTag");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeUntagged);
+
+ model.addTask(toBeUntagged);
+ // command to undo
+ logic.execute(helper.generateUntagCommand(tags, 1));
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ String.format(UntagCommand.MESSAGE_UNDO_SUCCESS, new UniqueTagList(tag1), toBeUntaggedCopy),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoClear_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task task1 = helper.generateTask(1);
+ Task task2 = helper.generateTask(2);
+ Task task3 = helper.generateTask(3);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(task1);
+ expectedAB.addTask(task2);
+ expectedAB.addTask(task3);
+
+ model.addTask(task1);
+ model.addTask(task2);
+ model.addTask(task3);
+ // command to undo
+ logic.execute("clear");
+ // execute command and verify result
+ assertCommandBehavior("undo",
+ ClearCommand.MESSAGE_UNDO_SUCCESS,
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_undoMultipleCommands() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task task1 = helper.generateTask(1);
+ Task task2 = helper.generateTask(2); // To be marked
+ Task task2Copy = helper.generateTask(2);
+ Task task3 = helper.generateTask(3);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(task1);
+ expectedAB.addTask(task2);
+ expectedAB.addTask(task3);
+ expectedAB.removeEntry(task2);
+ expectedAB.addTask(task2);
+
+ model.addTask(task1);
+ model.addTask(task2);
+
+ // command to undo
+ logic.execute(helper.generateAddCommand(task3));
+ logic.execute(ListCommand.LIST_ALL_COMMAND_WORD); //non-undoable command.
+ logic.execute("mark 2"); //changes the lastModifiedDate which changes the order
+ logic.execute(ListCommand.LIST_ALL_COMMAND_WORD); //non-undoable command.
+ logic.execute("delete 3");
+
+ // Undo "delete 2"
+ assertCommandBehavior("undo",
+ String.format(DeleteCommand.MESSAGE_UNDO_DELETE_PERSON_SUCCESS, task2),
+ expectedAB,
+ Arrays.asList(task1, task3));
+
+ // Undo "mark 2"
+ assertCommandBehavior("undo",
+ String.format(MarkCommand.MESSAGE_UNDO_SUCCESS, task2Copy),
+ expectedAB,
+ expectedAB.getTaskList());
+
+ // Undo add task
+ expectedAB.removeEntry(task3);
+ assertCommandBehavior("undo",
+ String.format(AddCommand.MESSAGE_UNDO_SUCCESS, task3),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+
+ //###################
+ //# Redo test cases #
+ //###################
+ @Test
+ public void execute_redoWithNoUndoableCommandsInHistory_errorMessageShown() throws Exception {
+ // setup expectations
+ TaskManager expectedAB = new TaskManager();
+
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ RedoCommand.MESSAGE_FAILURE,
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoAdd_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeAdded = helper.taskWithTags();
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeAdded);
+
+ // commands before undo
+ logic.execute(helper.generateAddCommand(toBeAdded));
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(AddCommand.MESSAGE_SUCCESS, toBeAdded),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoDelete_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task task1 = helper.generateTask(1);
+ Task task2 = helper.generateTask(2);
+ Task task3 = helper.generateTask(3);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(task1);
+ expectedAB.addTask(task2);
+ expectedAB.addTask(task3);
+ expectedAB.removeEntry(task2);
+
+ model.addTask(task1);
+ model.addTask(task2);
+ model.addTask(task3);
+ // command to undo
+ logic.execute("delete 2");
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(DeleteCommand.MESSAGE_DELETE_PERSON_SUCCESS, task2),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoEdit_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toEdit = helper.taskWithTags();
+ Task toEditCopy = helper.taskWithTags();
+
+ TaskManager expectedAB = new TaskManager();
+ toEditCopy.setTitle(new Title("New Title"));
+ expectedAB.addTask(toEditCopy);
+
+ model.addTask(toEdit);
+ // command to undo
+ logic.execute(helper.generateEditCommand(toEdit, 1, "New Title"));
+ logic.execute("undo");
+
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(EditCommand.MESSAGE_SUCCESS, toEditCopy),
+ expectedAB, expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoMark_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeMarked = helper.taskWithTags();
+ Task toBeMarkedCopy = helper.taskWithTags();
+ TaskManager expectedAB = new TaskManager();
+ toBeMarkedCopy.mark();
+ expectedAB.addTask(toBeMarkedCopy);
+
+ model.addTask(toBeMarked);
+ // command to undo
+ logic.execute("mark 1");
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(MarkCommand.MESSAGE_SUCCESS, toBeMarked),
+ expectedAB,
+ new ArrayList<>());
+ }
+
+ @Test
+ public void execute_redoUnmark_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeUnmarked = helper.taskWithTags();
+ Task toBeUnmarkedCopy = helper.taskWithTags();
+ TaskManager expectedAB = new TaskManager();
+ toBeUnmarked.mark();
+ expectedAB.addTask(toBeUnmarkedCopy);
+
+ model.addTask(toBeUnmarked);
+ // command to undo
+ logic.execute(ListCommand.LIST_ALL_COMMAND_WORD);
+ logic.execute("unmark 1");
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(UnmarkCommand.MESSAGE_SUCCESS, toBeUnmarked),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoTag_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeTagged = helper.taskWithTags();
+ Task toBeTaggedCopy = helper.taskWithTags();
+
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("newTag");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2);
+ toBeTaggedCopy.addTags(tags);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeTaggedCopy);
+
+ model.addTask(toBeTagged);
+ // command to undo
+ logic.execute(helper.generateTagCommand(tags, 1));
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(TagCommand.MESSAGE_SUCCESS, new UniqueTagList(tag2), toBeTaggedCopy),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoUntag_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task toBeUntagged = helper.taskWithTags();
+ Task toBeUntaggedCopy = helper.taskWithTags();
+ Tag tag1 = new Tag("tag1");
+ Tag tag2 = new Tag("newTag");
+ UniqueTagList tags = new UniqueTagList(tag1, tag2);
+
+ TaskManager expectedAB = new TaskManager();
+ expectedAB.addTask(toBeUntaggedCopy);
+ toBeUntaggedCopy.removeTags(tags);
+
+ model.addTask(toBeUntagged);
+ // command to undo
+ logic.execute(helper.generateUntagCommand(tags, 1));
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ String.format(UntagCommand.MESSAGE_SUCCESS, new UniqueTagList(tag1), toBeUntaggedCopy),
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+
+ @Test
+ public void execute_redoClear_successful() throws Exception {
+ // setup expectations
+ TestDataHelper helper = new TestDataHelper();
+ Task task1 = helper.generateTask(1);
+ Task task2 = helper.generateTask(2);
+ Task task3 = helper.generateTask(3);
+
+ TaskManager expectedAB = new TaskManager();
+
+ model.addTask(task1);
+ model.addTask(task2);
+ model.addTask(task3);
+ // command to undo
+ logic.execute("clear");
+ logic.execute("undo");
+ // execute command and verify result
+ assertCommandBehavior("redo",
+ ClearCommand.MESSAGE_SUCCESS,
+ expectedAB,
+ expectedAB.getTaskList());
+ }
+```
+###### \java\seedu\address\testutil\TestUtil.java
+``` java
+ /**
+ * Adds persons to the array of sorted persons.
+ * @param persons A array of persons.
+ * @param personsToAdd The persons that are to be added to the sorted list.
+ * @return The modified array of sorted persons.
+ */
+ public static TestEntry[] addPersonsToSortedList(final TestEntry[] persons, TestEntry... personsToAdd) {
+ TestEntry[] testEntry = addPersonsToList(persons, personsToAdd);
+ Arrays.sort(testEntry, new EntryViewComparator());
+ return testEntry;
+ }
+```
diff --git a/collated/test/A0127828W.md b/collated/test/A0127828W.md
index e97c54f7edab..4d34880f278b 100644
--- a/collated/test/A0127828W.md
+++ b/collated/test/A0127828W.md
@@ -7,6 +7,7 @@ public class EventTest {
private Title title;
private LocalDateTime startTime;
private LocalDateTime endTime;
+ private LocalDateTime lastModifiedTime;
private UniqueTagList tags;
private String description;
private boolean isMarked;
@@ -20,7 +21,8 @@ public class EventTest {
tags = new UniqueTagList(new Tag("tag1"));
isMarked = false;
description = "Description";
- testEvent = new Event(title, startTime, endTime, tags, isMarked, description);
+ lastModifiedTime = LocalDateTime.parse("2016-10-10T10:00:00");
+ testEvent = new Event(title, startTime, endTime, tags, isMarked, description, lastModifiedTime);
} catch (IllegalValueException ive) {
ive.printStackTrace();
}
@@ -33,10 +35,10 @@ public class EventTest {
@Test(expected = ClassCastException.class)
public void createEventFromEntry() {
- Entry eventEntry = new Event(title, startTime, endTime, tags, isMarked, description);
+ Entry eventEntry = new Event(title, startTime, endTime, tags, isMarked, description, lastModifiedTime);
assertTrue(new Event(eventEntry) instanceof Event);
- Entry taskEntry = new Task(title, endTime, tags, isMarked, description);
+ Entry taskEntry = new Task(title, endTime, tags, isMarked, description, lastModifiedTime);
// Should throw exception
new Event(taskEntry);
}
@@ -71,7 +73,7 @@ public class EventTest {
@Test
public void isSameStateAs() {
- Event otherEvent = new Event(title, startTime, endTime, tags, isMarked, description);
+ Event otherEvent = new Event(title, startTime, endTime, tags, isMarked, description, lastModifiedTime);
assertTrue(testEvent.isSameStateAs(otherEvent));
}
diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md
index 51bb275bf404..97f9d4dbf119 100644
--- a/docs/DeveloperGuide.md
+++ b/docs/DeveloperGuide.md
@@ -1,3 +1,4 @@
+
# Developer Guide
* [Setting Up](#setting-up)
@@ -112,6 +113,7 @@ 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.
+
### Logic component
@@ -225,6 +227,7 @@ Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use,
See [UsingGradle.md](UsingGradle.md) to learn how to use Gradle for build automation.
+
### Continuous Integration
We use [Travis CI](https://travis-ci.org/) to perform _Continuous Integration_ on our projects.
@@ -289,17 +292,17 @@ MSS
2. TodoList add that particular floating task into Storage and save it
Use Case Ends
-
+
*Extensions*
1a. Entry is entered with invalid properties e.g. title
> 1a1. TodoList warns user that the entry is invalid and does not add it into Storage
->
+>
> Use Case Ends
1b. Entry already exists
> 1b1. TodoList warns user that the entry already exists and does not add it into Storage
->
+>
> Use Case Ends
**Use case: Create an event**
@@ -342,6 +345,7 @@ MSS
>
> Use Case Ends
+
**Use Case: List all entries**
Actors: User
diff --git a/docs/UserGuide.md b/docs/UserGuide.md
index 573e15a8be83..d692490120af 100644
--- a/docs/UserGuide.md
+++ b/docs/UserGuide.md
@@ -2,7 +2,7 @@ User guide
===================
## Getting Started
-
+
Our to-do application primarily takes in input using a command line interface. It supports the basic creation, reading, updating and deletion of entries. You may add entries with or without deadlines. These are known as tasks. Entries with a start and/or end time are known as events.
@@ -40,6 +40,7 @@ delete 1
+
## Command Summary
@@ -53,9 +54,12 @@ delete 1
|delete|`delete `|
|mark|`mark `|
|unmark|`unmark `|
+|undo|`undo`|
+|redo|`redo`|
|help|`help []`|
|option|`option [/ ...]`|
+
## Commands
#### Adding
@@ -72,6 +76,7 @@ Examples:
- `add CS2105 Assignment 1 end/2016-10-10 10:00`
+
#### Listing
```
list [[keywords] [[after/] [before/] | [on/]] [# ...] [recurrence=] [desc=]] [type/{entry, task}]
@@ -91,6 +96,7 @@ If you want to include completed entries in your search, replace `list` with `li
- `list-all buy banana`
+
#### Tagging
```
tag # [#...]
@@ -108,6 +114,7 @@ Delete tag(s) from a particular entry with a specified id using `untag`
Duplicated tags will only be added once
+
#### Editing
```
edit [title/new title] [start/ end/] [#...] [r/ ] [desc/]
@@ -123,7 +130,7 @@ Examples:
- `edit 13 #yearly`
-
+
#### Deleting
```
delete
@@ -136,6 +143,7 @@ Examples:
- `Delete 42`
+
#### Marking
```
mark
@@ -151,6 +159,24 @@ Examples:
- `unmark 42`
+
+#### Undo
+
+```
+undo
+```
+
+Undo the latest change to the todo list. Handles every changes, including `clear`.
+
+#### Redo
+
+```
+redo
+```
+
+Redo the latest command reverted with `undo`.
+
+
#### Help
```
help []
@@ -160,6 +186,7 @@ Show available commands and how to use them
Help is also shown if you enter an incorrect command e.g. abcd
+
#### option
```
option [/ ...]
@@ -170,6 +197,7 @@ Examples:
- `config save/data/MyNewLocation.xml`
-#### Misc
+
+## Misc
You can use UP and DOWN keys to browse through your past commands in the session.
diff --git a/src/main/java/seedu/address/logic/commands/MarkCommand.java b/src/main/java/seedu/address/logic/commands/MarkCommand.java
index 1216f5171ff8..10b1cdbf1d04 100644
--- a/src/main/java/seedu/address/logic/commands/MarkCommand.java
+++ b/src/main/java/seedu/address/logic/commands/MarkCommand.java
@@ -9,6 +9,9 @@
import seedu.address.model.task.UniqueTaskList.EntryNotFoundException;
//@@author A0121501E
+/**
+ * Marks an entry as completed.
+ */
public class MarkCommand extends UndoableCommand {
public static final String COMMAND_WORD = "mark";
diff --git a/src/main/java/seedu/address/logic/commands/RedoCommand.java b/src/main/java/seedu/address/logic/commands/RedoCommand.java
index aa364d892daa..f39af67bae6b 100644
--- a/src/main/java/seedu/address/logic/commands/RedoCommand.java
+++ b/src/main/java/seedu/address/logic/commands/RedoCommand.java
@@ -4,10 +4,10 @@
import seedu.address.logic.commands.UndoableCommandHistory.UndoableCommandNotFoundException;
import seedu.address.model.Model;
+//@@author A0121501E
/**
* Redo the previous undo command.
*/
-//@@author A0121501E
public class RedoCommand extends Command {
public static final String COMMAND_WORD = "redo";
diff --git a/src/main/java/seedu/address/logic/commands/TagCommand.java b/src/main/java/seedu/address/logic/commands/TagCommand.java
index 80848eab5e61..90ce9da9e9df 100644
--- a/src/main/java/seedu/address/logic/commands/TagCommand.java
+++ b/src/main/java/seedu/address/logic/commands/TagCommand.java
@@ -13,10 +13,10 @@
import seedu.address.model.tag.Tag;
import seedu.address.model.tag.UniqueTagList;
+//@@author A0121501E
/*
- * Add tags to an entry.
+ * Add tags to an entry. Does not overwrite existing tags.
*/
-//@@author A0121501E
public class TagCommand extends UndoableCommand {
public static final String COMMAND_WORD = "tag";
diff --git a/src/main/java/seedu/address/logic/commands/UndoCommand.java b/src/main/java/seedu/address/logic/commands/UndoCommand.java
index c68f31258aae..deb652825294 100644
--- a/src/main/java/seedu/address/logic/commands/UndoCommand.java
+++ b/src/main/java/seedu/address/logic/commands/UndoCommand.java
@@ -4,10 +4,10 @@
import seedu.address.logic.commands.UndoableCommandHistory.UndoableCommandNotFoundException;
import seedu.address.model.Model;
+//@@author A0121501E
/**
* Undo the previous undoable command.
*/
-//@@author A0121501E
public class UndoCommand extends Command {
public static final String COMMAND_WORD = "undo";
diff --git a/src/main/java/seedu/address/logic/commands/UndoableCommand.java b/src/main/java/seedu/address/logic/commands/UndoableCommand.java
index 74a8457764cf..84f353d9ea9a 100644
--- a/src/main/java/seedu/address/logic/commands/UndoableCommand.java
+++ b/src/main/java/seedu/address/logic/commands/UndoableCommand.java
@@ -1,10 +1,10 @@
package seedu.address.logic.commands;
+//@@author A0121501E
/**
* Represents a command which changes to the model can be undone using the undo command
* and redone using the redo command
*/
-//@@author A0121501E
public abstract class UndoableCommand extends Command {
public enum CommandState {UNDOABLE, REDOABLE, PRE_EXECUTION};
@@ -14,6 +14,9 @@ public enum CommandState {UNDOABLE, REDOABLE, PRE_EXECUTION};
public CommandState commandState = CommandState.PRE_EXECUTION;
public abstract CommandResult unexecute();
+ /**
+ * Re-executes the command which was previously undone.
+ */
public CommandResult reExecute() {
if (getCommandState()!=CommandState.REDOABLE){
return new CommandResult(MESSAGE_UNDO_FAIL);
diff --git a/src/main/java/seedu/address/logic/commands/UndoableCommandHistory.java b/src/main/java/seedu/address/logic/commands/UndoableCommandHistory.java
index a18dae506798..8d3a99d30ac3 100644
--- a/src/main/java/seedu/address/logic/commands/UndoableCommandHistory.java
+++ b/src/main/java/seedu/address/logic/commands/UndoableCommandHistory.java
@@ -3,10 +3,10 @@
import java.util.ArrayDeque;
import java.util.Deque;
+//@@author A0121501E
/**
* Stack of successfully executed undoable commands.
*/
-//@@author A0121501E
public class UndoableCommandHistory {
Deque commandInternalUndoQueue = new ArrayDeque();
Deque