diff --git a/README.md b/README.md index 448687e2e4af..7a50760482c3 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ +# Address Book (Level 4) + [![Build Status](https://travis-ci.org/se-edu/addressbook-level4.svg?branch=master)](https://travis-ci.org/se-edu/addressbook-level4) +[![Build status](https://ci.appveyor.com/api/projects/status/3boko2x2vr5cc3w2?svg=true)](https://ci.appveyor.com/project/damithc/addressbook-level4) [![Coverage Status](https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master)](https://coveralls.io/github/se-edu/addressbook-level4?branch=master) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/fc0b7775cf7f4fdeaf08776f3d8e364a)](https://www.codacy.com/app/damith/addressbook-level4?utm_source=github.com&utm_medium=referral&utm_content=se-edu/addressbook-level4&utm_campaign=Badge_Grade) -# Address Book (Level 4) -
* This is a desktop Address Book application. It has a GUI but most of the user interactions happen using diff --git a/_config.yml b/_config.yml new file mode 100644 index 000000000000..c4192631f25b --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-cayman \ No newline at end of file diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 000000000000..4660f313ab8b --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,18 @@ +# AppVeyor configuration file +# For more details see https://www.appveyor.com/docs/build-configuration/ + +# Call on gradle to build and run tests +# --no-daemon: Prevent the daemon from launching to prevent file-in-use errors +# when we cache the ~/.gradle directory +build_script: + - gradlew.bat --no-daemon assemble checkstyleMain checkstyleTest + +test_script: + - appveyor-retry gradlew.bat --no-daemon headless allTests + +environment: + JAVA_HOME: C:\Program Files\Java\jdk1.8.0 # Use 64-bit Java + +# Files/folders to preserve between builds to speed them up +cache: + - C:\Users\appveyor\.gradle diff --git a/build.gradle b/build.gradle index 74669b84902c..7f149dbb7d14 100644 --- a/build.gradle +++ b/build.gradle @@ -8,8 +8,12 @@ plugins { id "com.github.kt3k.coveralls" version "2.4.0" id "com.github.johnrengelman.shadow" version '1.2.3' + id 'application' } +// Specifies the entry point of the application +mainClassName = 'seedu.address.MainApp' + allprojects { apply plugin: 'idea' apply plugin: 'java' @@ -78,15 +82,19 @@ allprojects { srcDir 'src/main/resources' } } + test { + java { + srcDir 'src/test/java' + } + resources { + srcDir 'src/test/resources' + } + } } shadowJar { archiveName = "addressbook.jar" - manifest { - attributes "Main-Class": "seedu.address.MainApp" - } - destinationDir = file("${buildDir}/jar/") } } @@ -96,8 +104,8 @@ task wrapper(type: Wrapper) { } task coverage(type: JacocoReport) { - sourceDirectories = files(allprojects.sourceSets.main.allSource.srcDirs) - classDirectories = files(allprojects.sourceSets.main.output) + sourceDirectories = files(allprojects.sourceSets.main.allSource.srcDirs, allprojects.sourceSets.test.allSource.srcDirs) + classDirectories = files(allprojects.sourceSets.main.output, allprojects.sourceSets.test.output) executionData = files(allprojects.jacocoTestReport.executionData) afterEvaluate { classDirectories = files(classDirectories.files.collect { @@ -111,7 +119,7 @@ task coverage(type: JacocoReport) { } coveralls { - sourceDirs = allprojects.sourceSets.main.allSource.srcDirs.flatten() + sourceDirs = files(allprojects.sourceSets.main.allSource.srcDirs, allprojects.sourceSets.test.allSource.srcDirs).flatten() jacocoReportPath = "${buildDir}/reports/jacoco/coverage/coverage.xml" } diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index c786caa1b993..afef652d6091 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -14,7 +14,10 @@ - + + + + @@ -215,11 +218,21 @@ --> + RCURLY, SL, SLIST, SL_ASSIGN, SR_ASSIGN, STAR, STAR_ASSIGN"/> + + + + + + + + + + @@ -236,6 +249,13 @@ + + + + + + + diff --git a/docs/DeveloperGuide.md b/docs/DeveloperGuide.md index c8fb444cfc2e..93a583d85be4 100644 --- a/docs/DeveloperGuide.md +++ b/docs/DeveloperGuide.md @@ -1,10 +1,15 @@ -# Developer Guide +# AddressBook Level 4 - Developer Guide + +By : `Team SE-EDU`      Since: `Jun 2016`      Licence: `MIT` + +--- + +1. [Setting Up](#setting-up) +2. [Design](#design) +3. [Implementation](#implementation) +4. [Testing](#testing) +5. [Dev Ops](#dev-ops) -* [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) @@ -12,9 +17,9 @@ * [Appendix E : Product Survey](#appendix-e--product-survey) -## Setting up +## 1. Setting up -#### Prerequisites +### 1.1. Prerequisites 1. **JDK `1.8.0_60`** or later
@@ -28,7 +33,7 @@ 5. **Checkstyle Plug-in** plugin from the Eclipse Marketplace -#### Importing the project into Eclipse +### 1.2. 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 @@ -43,7 +48,7 @@ (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. -#### Configuring Checkstyle +### 1.3. Configuring Checkstyle 1. Click `Project` -> `Properties` -> `Checkstyle` -> `Local Check Configurations` -> `New...` 2. Choose `External Configuration File` under `Type` 3. Enter an arbitrary configuration name e.g. addressbook @@ -54,7 +59,7 @@ > Note to click on the `files from packages` text after ticking in order to enable the `Change...` button -#### Troubleshooting project setup +### 1.4. Troubleshooting project setup **Problem: Eclipse reports compile errors after new commits are pulled from Git** * Reason: Eclipse fails to recognize new files that appeared due to the Git pull. @@ -63,29 +68,33 @@ **Problem: Eclipse reports some required libraries missing** * Reason: Required libraries may not have been downloaded during the project import. -* Solution: [Run tests using Gardle](UsingGradle.md) once (to refresh the libraries). +* Solution: [Run tests using Gradle](UsingGradle.md) once (to refresh the libraries). -## Design +## 2. Design -### Architecture +### 2.1. Architecture
+_Figure 2.1.1 : Architecture Diagram_ + The **_Architecture Diagram_** given above explains the high-level design of the App. Given below is a quick overview of each component. +> Tip: The `.pptx` files used to create diagrams in this document can be found in the [diagrams](diagrams/) folder. + `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. +* At app launch: Initializes the components in the correct sequence, and connects them up with each other. +* At shut down: Shuts down the components and invokes 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)) +* `EventsCenter` : 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. +The rest of the App consists of four components. +* [**`UI`**](#ui-component) : The UI of the 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. @@ -97,20 +106,23 @@ Each of the four components 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.

+_Figure 2.1.2 : Class Diagram of the Logic Component_ ##### Events-Driven nature of the design The _Sequence Diagram_ below shows how the components interact for the scenario where the user issues the command `delete 1`. - +
+_Figure 2.1.3a : Component interactions for `delete 1` command (part 1)_ >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.
- +
+_Figure 2.1.3b : Component interactions for `delete 1` command (part 2)_ > 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 @@ -118,9 +130,12 @@ being saved to the hard disk and the status bar of the UI being updated to refle The sections below give more details of each component. -### UI component +### 2.2. UI component + +Author: Alice Bee
+_Figure 2.2.1 : Structure of the UI Component_ **API** : [`Ui.java`](../src/main/java/seedu/address/ui/Ui.java) @@ -138,9 +153,12 @@ 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 +### 2.3. Logic component + +Author: Bernard Choo
+_Figure 2.3.1 : Structure of the Logic Component_ **API** : [`Logic.java`](../src/main/java/seedu/address/logic/Logic.java) @@ -152,10 +170,14 @@ The `UI` component, Given below is the Sequence Diagram for interactions within the `Logic` component for the `execute("delete 1")` API call.

+_Figure 2.3.1 : Interactions Inside the Logic Component for the `delete 1` Command_ -### Model component +### 2.4. Model component + +Author: Cynthia Dharman
+_Figure 2.4.1 : Structure of the Model Component_ **API** : [`Model.java`](../src/main/java/seedu/address/model/Model.java) @@ -166,9 +188,12 @@ The `Model`, 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 +### 2.5. Storage component + +Author: Darius Foong
+_Figure 2.5.1 : Structure of the Storage Component_ **API** : [`Storage.java`](../src/main/java/seedu/address/storage/Storage.java) @@ -176,13 +201,13 @@ 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 +### 2.6. Common classes Classes used by multiple components are in the `seedu.addressbook.commons` package. -## Implementation +## 3. Implementation -### Logging +### 3.1. Logging We are using `java.util.logging` package for logging. The `LogsCenter` class is used to manage the logging levels and logging destinations. @@ -201,13 +226,13 @@ and logging destinations. * `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 +### 3.2. Configuration Certain properties of the application can be controlled (e.g App name, logging level) through the configuration file (default: `config.json`): -## Testing +## 4. Testing Tests can be found in the `./src/test/java` folder. @@ -235,14 +260,14 @@ We have two types of tests: how the are connected together.
e.g. `seedu.address.logic.LogicManagerTest` -#### Headless GUI Testing +##### 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. -#### Troubleshooting tests +### 4.1. Troubleshooting tests **Problem: Tests fail because NullPointException when AssertionError is expected** * Reason: Assertions are not enabled for JUnit tests. This can happen if you are not using a recent Eclipse version (i.e. _Neon_ or later) @@ -250,27 +275,27 @@ Thanks to the [TestFX](https://github.com/TestFX/TestFX) library we use, [here](http://stackoverflow.com/questions/2522897/eclipse-junit-ea-vm-option).
Delete run configurations created when you ran tests earlier. -## Dev Ops +## 5. Dev Ops -### Build Automation +### 5.1. Build Automation See [UsingGradle.md](UsingGradle.md) to learn how to use Gradle for build automation. -### Continuous Integration +### 5.2. 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. +We use [Travis CI](https://travis-ci.org/) and [AppVeyor](https://www.appveyor.com/) to perform _Continuous Integration_ on our projects. +See [UsingTravis.md](UsingTravis.md) and [UsingAppVeyor.md](UsingAppVeyor.md) for more details. -### Making a Release +### 5.3. 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. [Create a new release using GitHub](https://help.github.com/articles/creating-releases/) - and upload the JAR file your created. + and upload the JAR file you created. -### Managing Dependencies +### 5.4. 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_ @@ -325,9 +350,9 @@ Use case ends. ## Appendix C : Non Functional Requirements 1. Should work on any [mainstream OS](#mainstream-os) as long as it has Java `1.8.0_60` or higher installed. -2. Should be able to hold up to 1000 persons. -3. Should come with automated unit tests and open source code. -4. Should favor DOS style commands over Unix-style commands. +2. Should be able to hold up to 1000 persons without a noticeable sluggishness in performance for typical usage. +3. A user with above average typing speed for regular English text (i.e. not code, not system admin commands) + should be able to accomplish most of the tasks faster using commands than using the mouse. {More to be added} @@ -343,5 +368,17 @@ Use case ends. ## Appendix E : Product Survey -{TODO: Add a summary of competing products} +**Product Name** + +Author: ... + +Pros: + +* ... +* ... + +Cons: + +* ... +* ... diff --git a/docs/LearningOutcomes.md b/docs/LearningOutcomes.md index e6f403f3956c..018b06a279f3 100644 --- a/docs/LearningOutcomes.md +++ b/docs/LearningOutcomes.md @@ -132,11 +132,14 @@ Note [how the AddressBook app uses Travis to perform Continuous Integration](Usi Note how our CI server [Travis uses Coveralls to report code coverage](UsingTravis.md). ([![Coverage Status](https://coveralls.io/repos/github/se-edu/addressbook-level4/badge.svg?branch=master)](https://coveralls.io/github/se-edu/addressbook-level4?branch=master)) +After setting up Coveralls for your project, you can visit Coveralls website to find details about the +coverage of code pushed to your repo. [Here](https://coveralls.io/github/se-edu/addressbook-level4?branch=master) is an example. #### Exercise: Use EclEmma to measure coverage locally * Install the [EclEmma Eclipse Plugin](http://www.eclemma.org/) in your computer and use that to find code that - is not covered by the tests. + is not covered by the tests. This plugin can help you to find coverage details even before you push code + to the remote repo. ------------------------------------------------------------------------------------------------------ diff --git a/docs/UserGuide.md b/docs/UserGuide.md index 507ba15dc108..9c30b71f6b0a 100644 --- a/docs/UserGuide.md +++ b/docs/UserGuide.md @@ -1,11 +1,11 @@ # User Guide -* [Quick Start](#quick-start) -* [Features](#features) -* [FAQ](#faq) -* [Command Summary](#command-summary) +1. [Quick Start](#quick-start) +2. [Features](#features) +3. [FAQ](#faq) +4. [Command Summary](#command-summary) -## Quick Start +## 1. Quick Start 0. Ensure you have Java version `1.8.0_60` or later installed in your Computer.
> Having any Java 8 version is not enough.
@@ -27,7 +27,7 @@ 6. Refer to the [Features](#features) section below for details of each command.
-## Features +## 2. Features > **Command Format** > * Words in `UPPER_CASE` are the parameters. @@ -35,12 +35,12 @@ > * Items with `...` after them can have multiple instances. > * Parameters can be in any order. -#### Viewing help : `help` +### 2.1. Viewing help : `help` Format: `help` > Help is also shown if you enter an incorrect command e.g. `abcd` -#### Adding a person: `add` +### 2.2. Adding a person: `add` Adds a person to the address book
Format: `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...` @@ -50,11 +50,11 @@ Examples: * `add John Doe p/98765432 e/johnd@gmail.com a/John street, block 123, #01-01` * `add Betsy Crowe t/friend e/betsycrowe@gmail.com a/Newgate Prison p/1234567 t/criminal` -#### Listing all persons : `list` +### 2.3. Listing all persons : `list` Shows a list of all persons in the address book.
Format: `list` -#### Finding all persons containing any keyword in their name: `find` +### 2.4. Finding all persons containing any keyword in their name: `find` Finds persons whose names contain any of the given keywords.
Format: `find KEYWORD [MORE_KEYWORDS]` @@ -71,7 +71,7 @@ Examples: * `find Betsy Tim John`
Returns Any person having names `Betsy`, `Tim`, or `John` -#### Deleting a person : `delete` +### 2.5. Deleting a person : `delete` Deletes the specified person from the address book. Irreversible.
Format: `delete INDEX` @@ -87,7 +87,7 @@ Examples: `delete 1`
Deletes the 1st person in the results of the `find` command. -#### Select a person : `select` +### 2.6. Select a person : `select` Selects the person identified by the index number used in the last person listing.
Format: `select INDEX` @@ -103,25 +103,25 @@ Examples: `select 1`
Selects the 1st person in the results of the `find` command. -#### Clearing all entries : `clear` +### 2.7. Clearing all entries : `clear` Clears all entries from the address book.
Format: `clear` -#### Exiting the program : `exit` +### 2.8. Exiting the program : `exit` Exits the program.
Format: `exit` -#### Saving the data +### 2.9. Saving the data Address book data are saved in the hard disk automatically after any command that changes the data.
There is no need to save manually. -## FAQ +## 3. FAQ **Q**: How do I transfer my data to another Computer?
**A**: Install the app in the other computer and overwrite the empty data file it creates with the file that contains the data of your previous Address Book folder. -## Command Summary +## 4. Command Summary * **Add** `add NAME p/PHONE_NUMBER e/EMAIL a/ADDRESS [t/TAG]...`
e.g. `add James Ho p/22224444 e/jamesho@gmail.com a/123, Clementi Rd, 1234665 t/friend t/colleague` diff --git a/docs/UsingAppVeyor.md b/docs/UsingAppVeyor.md new file mode 100644 index 000000000000..95ee496983b7 --- /dev/null +++ b/docs/UsingAppVeyor.md @@ -0,0 +1,77 @@ +# AppVeyor + +[AppVeyor](https://www.appveyor.com/) is a _Continuous Integration_ platform for GitHub projects. +It runs its builds on Windows virtual machines. + +AppVeyor can run the project's tests automatically whenever new code is pushed to the repo. +This ensures that existing functionality and features have not been broken on Windows by the changes. + +The current AppVeyor setup performs the following things whenever someone pushes code to the repo: + +* Runs the `gradlew.bat headless allTests` command. + +* Automatically retries the build up to 3 times if a task fails. + +If you would like to customize your AppVeyor build further, you can learn more about AppVeyor from the +[AppVeyor Documentation](https://www.appveyor.com/docs/). + +## Setting up AppVeyor + +1. Fork the repo to your own organization. + +2. Go to https://ci.appveyor.com/, and under `Login`, click on `GitHub` to login with your GitHub account. + Enter your GitHub account details if needed. + + ![Click on GitHub in the login page](images/appveyor/login.png) + +3. After logging in, you will be brought to your projects dashboard. Click on `NEW PROJECT`. + + ![Click on "NEW PROJECT" in the projects dashboard](images/appveyor/add-project-1.png) + +4. You will be brought to the `Select repository` page. Select `GitHub`. + + * On your first usage of AppVeyor, you will need to give AppVeyor authorization to your GitHub account. + Click on `Authorize GitHub`. + + ![Click on Authorize GitHub](images/appveyor/add-project-2.png) + + * This will bring you to a GitHub page that manages the access of third-party applications to your repositories. + + Depending on whether you are the owner of the repository, you can either grant access: + + ![Grant Access](images/grant_access.png) + + Or request access: + + ![Request Access](images/request_access.png) + +5. AppVeyor will then list the repositories you have access to in your GitHub account. + Find the repository you want to set AppVeyor up on, and then click `ADD`. + + ![Click "Add" on the repository you want to set AppVeyor up on](images/appveyor/add-project-3.png) + +6. AppVeyor will then be activated on that repository. + To see the CI in action, push a commit to any branch! + + * Go to the repository and see the pushed commit. There should be an icon which will link you to the AppVeyor build: + + ![Commit build](images/appveyor/ci-pending.png) + + * As the build is run on a remote machine, we can only examine the logs it produces: + + ![AppVeyor build](images/appveyor/ci-log.png) + +7. Update the link to the "build status" badge at the top of `README.md` to point to the AppVeyor build status of your own repo. + + * To find your build status badge URL, + first go to your project settings by clicking on the "Settings" icon: + + ![Click on project settings](images/appveyor/project-settings-1.png) + + * Then go to the `Badges` section of your project settings by clicking on it: + + ![Click on "Badges"](images/appveyor/project-settings-2.png) + + * Then copy and paste the markdown code for your `master` branch to your `README.md` file: + + ![Copy and paste the markdown code for your `master` branch to your `README.md` file](images/appveyor/project-settings-3.png) diff --git a/docs/UsingGradle.md b/docs/UsingGradle.md index 4eac109ce89d..e0fe66c5b4bf 100644 --- a/docs/UsingGradle.md +++ b/docs/UsingGradle.md @@ -47,6 +47,14 @@ If we package only our own class files into the JAR file, it will not work prope Therefore, we package all dependencies into a single JAR files, creating what is also known as a _fat_ JAR file. To create a fat JAR fil, we use the Gradle plugin [shadow jar](https://github.com/johnrengelman/shadow). +## Running the application + +* **`run`**
+ Builds and runs the application. + +* **`runShadow`**
+ Builds the application as a fat JAR, and then runs it. + ## Running code style checks * **`checkstyleMain`**
diff --git a/docs/images/appveyor/add-project-1.png b/docs/images/appveyor/add-project-1.png new file mode 100755 index 000000000000..a244bce01461 Binary files /dev/null and b/docs/images/appveyor/add-project-1.png differ diff --git a/docs/images/appveyor/add-project-2.png b/docs/images/appveyor/add-project-2.png new file mode 100755 index 000000000000..7578f8214a8b Binary files /dev/null and b/docs/images/appveyor/add-project-2.png differ diff --git a/docs/images/appveyor/add-project-3.png b/docs/images/appveyor/add-project-3.png new file mode 100755 index 000000000000..c38f521621fd Binary files /dev/null and b/docs/images/appveyor/add-project-3.png differ diff --git a/docs/images/appveyor/ci-log.png b/docs/images/appveyor/ci-log.png new file mode 100644 index 000000000000..40e0f5a52d92 Binary files /dev/null and b/docs/images/appveyor/ci-log.png differ diff --git a/docs/images/appveyor/ci-pending.png b/docs/images/appveyor/ci-pending.png new file mode 100644 index 000000000000..a7d2359994fd Binary files /dev/null and b/docs/images/appveyor/ci-pending.png differ diff --git a/docs/images/appveyor/login.png b/docs/images/appveyor/login.png new file mode 100755 index 000000000000..e5719a546048 Binary files /dev/null and b/docs/images/appveyor/login.png differ diff --git a/docs/images/appveyor/project-settings-1.png b/docs/images/appveyor/project-settings-1.png new file mode 100755 index 000000000000..b772b3f6842d Binary files /dev/null and b/docs/images/appveyor/project-settings-1.png differ diff --git a/docs/images/appveyor/project-settings-2.png b/docs/images/appveyor/project-settings-2.png new file mode 100755 index 000000000000..5ff7f15e46f6 Binary files /dev/null and b/docs/images/appveyor/project-settings-2.png differ diff --git a/docs/images/appveyor/project-settings-3.png b/docs/images/appveyor/project-settings-3.png new file mode 100755 index 000000000000..30485db350da Binary files /dev/null and b/docs/images/appveyor/project-settings-3.png differ diff --git a/src/main/java/seedu/address/MainApp.java b/src/main/java/seedu/address/MainApp.java index a270b024b975..eac4aea5f4af 100644 --- a/src/main/java/seedu/address/MainApp.java +++ b/src/main/java/seedu/address/MainApp.java @@ -62,7 +62,7 @@ public void init() throws Exception { initEventsCenter(); } - private String getApplicationParameter(String parameterName){ + private String getApplicationParameter(String parameterName) { Map applicationParameters = getParameters().getNamed(); return applicationParameters.get(parameterName); } diff --git a/src/main/java/seedu/address/commons/core/ComponentManager.java b/src/main/java/seedu/address/commons/core/ComponentManager.java index 4bc8564e5824..05a400773ae8 100644 --- a/src/main/java/seedu/address/commons/core/ComponentManager.java +++ b/src/main/java/seedu/address/commons/core/ComponentManager.java @@ -13,7 +13,7 @@ public abstract class ComponentManager { /** * Uses default {@link EventsCenter} */ - public ComponentManager(){ + public ComponentManager() { this(EventsCenter.getInstance()); } @@ -22,7 +22,7 @@ public ComponentManager(EventsCenter eventsCenter) { eventsCenter.registerHandler(this); } - protected void raise(BaseEvent event){ + protected void raise(BaseEvent event) { eventsCenter.post(event); } } diff --git a/src/main/java/seedu/address/commons/core/Config.java b/src/main/java/seedu/address/commons/core/Config.java index ce6994269a77..d4dc73132d5c 100644 --- a/src/main/java/seedu/address/commons/core/Config.java +++ b/src/main/java/seedu/address/commons/core/Config.java @@ -61,10 +61,10 @@ public void setAddressBookName(String addressBookName) { @Override public boolean equals(Object other) { - if (other == this){ + if (other == this) { return true; } - if (!(other instanceof Config)){ //this handles null as well. + if (!(other instanceof Config)) { //this handles null as well. return false; } @@ -83,7 +83,7 @@ public int hashCode() { } @Override - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("App title : " + appTitle); sb.append("\nCurrent log level : " + logLevel); diff --git a/src/main/java/seedu/address/commons/core/GuiSettings.java b/src/main/java/seedu/address/commons/core/GuiSettings.java index 561606aebba0..727a1e6a5e97 100644 --- a/src/main/java/seedu/address/commons/core/GuiSettings.java +++ b/src/main/java/seedu/address/commons/core/GuiSettings.java @@ -42,10 +42,10 @@ public Point getWindowCoordinates() { @Override public boolean equals(Object other) { - if (other == this){ + if (other == this) { return true; } - if (!(other instanceof GuiSettings)){ //this handles null as well. + if (!(other instanceof GuiSettings)) { //this handles null as well. return false; } @@ -63,7 +63,7 @@ public int hashCode() { } @Override - public String toString(){ + public String toString() { StringBuilder sb = new StringBuilder(); sb.append("Width : " + windowWidth + "\n"); sb.append("Height : " + windowHeight + "\n"); diff --git a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java b/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java index 92ee6cf7ba13..87a0a566f29c 100644 --- a/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java +++ b/src/main/java/seedu/address/commons/core/UnmodifiableObservableList.java @@ -26,7 +26,7 @@ public UnmodifiableObservableList(ObservableList backingList) { assert backingList != null; this.backingList = backingList; } - + @Override public final void addListener(ListChangeListener listener) { backingList.addListener(listener); @@ -61,12 +61,12 @@ public final boolean setAll(Object... elements) { public final boolean setAll(Collection col) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean removeAll(Object... elements) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean retainAll(Object... elements) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -108,12 +108,12 @@ public final int size() { public final boolean isEmpty() { return backingList.isEmpty(); } - + @Override public final boolean contains(Object o) { return backingList.contains(o); } - + @Override public final Iterator iterator() { return new Iterator() { @@ -150,7 +150,7 @@ public final T[] toArray(T[] a) { public final boolean add(E o) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean remove(Object o) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -170,12 +170,12 @@ public final boolean addAll(Collection c) { public final boolean addAll(int index, Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean removeAll(Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean retainAll(Collection c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); @@ -190,13 +190,13 @@ public final void replaceAll(UnaryOperator operator) { public final void sort(Comparator c) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final void clear() { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final boolean equals(Object o) { return o == this || backingList.equals(o); @@ -207,7 +207,7 @@ public final int hashCode() { return backingList.hashCode(); } - + @Override public final E get(int index) { return backingList.get(index); @@ -228,12 +228,12 @@ public final void add(int index, Object element) { public final E remove(int index) { throw new UnsupportedOperationException(MUTATION_OP_EXCEPTION_MESSAGE); } - + @Override public final int indexOf(Object o) { return backingList.indexOf(o); } - + @Override public final int lastIndexOf(Object o) { return backingList.lastIndexOf(o); @@ -243,7 +243,7 @@ public final int lastIndexOf(Object o) { public final ListIterator listIterator() { return listIterator(0); } - + @Override public final ListIterator listIterator(int index) { return new ListIterator() { @@ -300,7 +300,7 @@ public final boolean removeIf(Predicate filter) { public final Stream stream() { return (Stream) backingList.stream(); } - + @Override public final void forEach(Consumer action) { backingList.forEach(action); diff --git a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java b/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java index 347a8359e0d5..7db9b5c48ed6 100644 --- a/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java +++ b/src/main/java/seedu/address/commons/events/model/AddressBookChangedEvent.java @@ -8,7 +8,7 @@ public class AddressBookChangedEvent extends BaseEvent { public final ReadOnlyAddressBook data; - public AddressBookChangedEvent(ReadOnlyAddressBook data){ + public AddressBookChangedEvent(ReadOnlyAddressBook data) { this.data = data; } diff --git a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java b/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java index f0a0640ee523..51cf8ce68a76 100644 --- a/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java +++ b/src/main/java/seedu/address/commons/events/storage/DataSavingExceptionEvent.java @@ -14,7 +14,7 @@ public DataSavingExceptionEvent(Exception exception) { } @Override - public String toString(){ + public String toString() { return exception.toString(); } diff --git a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java b/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java index 95377b326fa6..76e08f587e02 100644 --- a/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java +++ b/src/main/java/seedu/address/commons/events/ui/PersonPanelSelectionChangedEvent.java @@ -11,7 +11,7 @@ public class PersonPanelSelectionChangedEvent extends BaseEvent { private final ReadOnlyPerson newSelection; - public PersonPanelSelectionChangedEvent(ReadOnlyPerson newSelection){ + public PersonPanelSelectionChangedEvent(ReadOnlyPerson newSelection) { this.newSelection = newSelection; } diff --git a/src/main/java/seedu/address/commons/util/FxViewUtil.java b/src/main/java/seedu/address/commons/util/FxViewUtil.java index 900efa6bf5c3..9a1a8833f5c7 100644 --- a/src/main/java/seedu/address/commons/util/FxViewUtil.java +++ b/src/main/java/seedu/address/commons/util/FxViewUtil.java @@ -2,6 +2,7 @@ import javafx.scene.Node; import javafx.scene.layout.AnchorPane; +import javafx.stage.Stage; /** * Contains utility methods for JavaFX views @@ -14,4 +15,13 @@ public static void applyAnchorBoundaryParameters(Node node, double left, double AnchorPane.setRightAnchor(node, right); AnchorPane.setTopAnchor(node, top); } + + /** + * Sets the given image as the icon for the given stage. + * @param iconSource e.g. {@code "/images/help_icon.png"} + */ + public static void setStageIcon(Stage stage, String iconSource) { + stage.getIcons().setAll(AppUtil.getImage(iconSource)); + } + } diff --git a/src/main/java/seedu/address/commons/util/StringUtil.java b/src/main/java/seedu/address/commons/util/StringUtil.java index fd7fb71516cb..7f1998fa4c51 100644 --- a/src/main/java/seedu/address/commons/util/StringUtil.java +++ b/src/main/java/seedu/address/commons/util/StringUtil.java @@ -30,7 +30,7 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { String preppedSentence = sentence; String[] wordsInPreppedSentence = preppedSentence.split("\\s+"); - for (String wordInSentence: wordsInPreppedSentence){ + for (String wordInSentence: wordsInPreppedSentence) { if (wordInSentence.equalsIgnoreCase(preppedWord)) return true; } return false; @@ -39,7 +39,7 @@ public static boolean containsWordIgnoreCase(String sentence, String word) { /** * Returns a detailed message of the t, including the stack trace. */ - public static String getDetails(Throwable t){ + public static String getDetails(Throwable t) { assert t != null; StringWriter sw = new StringWriter(); t.printStackTrace(new PrintWriter(sw)); @@ -52,7 +52,7 @@ public static String getDetails(Throwable t){ * null, empty string, "-1", "0", "+1", and " 2 " (untrimmed) "3 0" (contains whitespace). * @param s Should be trimmed. */ - public static boolean isUnsignedInteger(String s){ + public static boolean isUnsignedInteger(String s) { return s != null && s.matches("^0*[1-9]\\d*$"); } } diff --git a/src/main/java/seedu/address/logic/commands/AddCommand.java b/src/main/java/seedu/address/logic/commands/AddCommand.java index 2860a9ab2a85..04c1349b1eb8 100644 --- a/src/main/java/seedu/address/logic/commands/AddCommand.java +++ b/src/main/java/seedu/address/logic/commands/AddCommand.java @@ -26,7 +26,7 @@ public class AddCommand extends Command { private final Person toAdd; /** - * Convenience constructor using raw values. + * Creates an AddCommand using raw values. * * @throws IllegalValueException if any of the raw values are invalid */ diff --git a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java b/src/main/java/seedu/address/logic/commands/IncorrectCommand.java index 491d9cb9da35..0b1960871c34 100644 --- a/src/main/java/seedu/address/logic/commands/IncorrectCommand.java +++ b/src/main/java/seedu/address/logic/commands/IncorrectCommand.java @@ -8,7 +8,7 @@ public class IncorrectCommand extends Command { public final String feedbackToUser; - public IncorrectCommand(String feedbackToUser){ + public IncorrectCommand(String feedbackToUser) { this.feedbackToUser = feedbackToUser; } diff --git a/src/main/java/seedu/address/logic/parser/Parser.java b/src/main/java/seedu/address/logic/parser/Parser.java index 80179b54147b..866a1b2da4c6 100644 --- a/src/main/java/seedu/address/logic/parser/Parser.java +++ b/src/main/java/seedu/address/logic/parser/Parser.java @@ -82,7 +82,7 @@ public Command parseCommand(String userInput) { * @param args full command args string * @return the prepared command */ - private Command prepareAdd(String args){ + private Command prepareAdd(String args) { ArgumentTokenizer argsTokenizer = new ArgumentTokenizer(phoneNumberPrefix, emailPrefix, addressPrefix, tagsPrefix); argsTokenizer.tokenize(args); diff --git a/src/main/java/seedu/address/model/AddressBook.java b/src/main/java/seedu/address/model/AddressBook.java index f50e1572220f..a864e2eb07b0 100644 --- a/src/main/java/seedu/address/model/AddressBook.java +++ b/src/main/java/seedu/address/model/AddressBook.java @@ -33,14 +33,14 @@ public class AddressBook implements ReadOnlyAddressBook { public AddressBook() {} /** - * Persons and Tags are copied into this addressbook + * Creates an AddressBook using the Persons and Tags in the {@code toBeCopied} */ public AddressBook(ReadOnlyAddressBook toBeCopied) { this(toBeCopied.getUniquePersonList(), toBeCopied.getUniqueTagList()); } /** - * Persons and Tags are copied into this addressbook + * Creates an AddressBook by copying the Persons and Tags in the given lists */ public AddressBook(UniquePersonList persons, UniqueTagList tags) { resetData(persons, tags); diff --git a/src/main/java/seedu/address/model/ModelManager.java b/src/main/java/seedu/address/model/ModelManager.java index f77b3987c020..6c0f3edc42ff 100644 --- a/src/main/java/seedu/address/model/ModelManager.java +++ b/src/main/java/seedu/address/model/ModelManager.java @@ -90,7 +90,7 @@ public void updateFilteredListToShowAll() { } @Override - public void updateFilteredPersonList(Set keywords){ + public void updateFilteredPersonList(Set keywords) { updateFilteredPersonList(new PredicateExpression(new NameQualifier(keywords))); } diff --git a/src/main/java/seedu/address/model/UserPrefs.java b/src/main/java/seedu/address/model/UserPrefs.java index 0cbf893ad026..5393df76460d 100644 --- a/src/main/java/seedu/address/model/UserPrefs.java +++ b/src/main/java/seedu/address/model/UserPrefs.java @@ -19,7 +19,7 @@ public void updateLastUsedGuiSetting(GuiSettings guiSettings) { this.guiSettings = guiSettings; } - public UserPrefs(){ + public UserPrefs() { this.setGuiSettings(500, 500, 0, 0); } @@ -47,7 +47,7 @@ public int hashCode() { } @Override - public String toString(){ + public String toString() { return guiSettings.toString(); } diff --git a/src/main/java/seedu/address/model/person/Address.java b/src/main/java/seedu/address/model/person/Address.java index 30099e36bc40..4cb19ecd2291 100644 --- a/src/main/java/seedu/address/model/person/Address.java +++ b/src/main/java/seedu/address/model/person/Address.java @@ -8,7 +8,7 @@ * Guarantees: immutable; is valid as declared in {@link #isValidAddress(String)} */ public class Address { - + public static final String MESSAGE_ADDRESS_CONSTRAINTS = "Person addresses can be in any format"; public static final String ADDRESS_VALIDATION_REGEX = ".+"; diff --git a/src/main/java/seedu/address/model/person/Person.java b/src/main/java/seedu/address/model/person/Person.java index 03ffce7d2e79..d8fc511d722a 100644 --- a/src/main/java/seedu/address/model/person/Person.java +++ b/src/main/java/seedu/address/model/person/Person.java @@ -31,7 +31,7 @@ public Person(Name name, Phone phone, Email email, Address address, UniqueTagLis } /** - * Copy constructor. + * Creates a copy of the given ReadOnlyPerson. */ public Person(ReadOnlyPerson source) { this(source.getName(), source.getPhone(), source.getEmail(), source.getAddress(), source.getTags()); diff --git a/src/main/java/seedu/address/model/person/ReadOnlyPerson.java b/src/main/java/seedu/address/model/person/ReadOnlyPerson.java index d45be4b5fe36..929fa3f70ddd 100644 --- a/src/main/java/seedu/address/model/person/ReadOnlyPerson.java +++ b/src/main/java/seedu/address/model/person/ReadOnlyPerson.java @@ -48,18 +48,4 @@ default String getAsText() { return builder.toString(); } - /** - * Returns a string representation of this Person's tags - */ - default String tagsString() { - final StringBuffer buffer = new StringBuffer(); - final String separator = ", "; - getTags().forEach(tag -> buffer.append(tag).append(separator)); - if (buffer.length() == 0) { - return ""; - } else { - return buffer.substring(0, buffer.length() - separator.length()); - } - } - } diff --git a/src/main/java/seedu/address/model/tag/UniqueTagList.java b/src/main/java/seedu/address/model/tag/UniqueTagList.java index 8914b5a7b151..fa159665cba3 100644 --- a/src/main/java/seedu/address/model/tag/UniqueTagList.java +++ b/src/main/java/seedu/address/model/tag/UniqueTagList.java @@ -26,7 +26,8 @@ public class UniqueTagList implements Iterable { public UniqueTagList() {} /** - * Varargs/array constructor, enforces no nulls or duplicates. + * Creates a UniqueTagList using given tags. + * Enforces no nulls or duplicates. */ public UniqueTagList(Tag... tags) throws DuplicateTagException { assert !CollectionUtil.isAnyNull((Object[]) tags); @@ -38,7 +39,8 @@ public UniqueTagList(Tag... tags) throws DuplicateTagException { } /** - * java collections constructor, enforces no null or duplicate elements. + * Creates a UniqueTagList using given tags. + * Enforces no null or duplicate elements. */ public UniqueTagList(Collection tags) throws DuplicateTagException { CollectionUtil.assertNoNullElements(tags); @@ -49,7 +51,8 @@ public UniqueTagList(Collection tags) throws DuplicateTagException { } /** - * java set constructor, enforces no nulls. + * Creates a UniqueTagList using given tags. + * Enforces no nulls. */ public UniqueTagList(Set tags) { CollectionUtil.assertNoNullElements(tags); @@ -57,14 +60,16 @@ public UniqueTagList(Set tags) { } /** - * Copy constructor, insulates from changes in source. + * Creates a copy of the given list. + * Insulates from changes in source. */ public UniqueTagList(UniqueTagList source) { internalList.addAll(source.internalList); // insulate internal list from changes in argument } /** - * All tags in this list as a Set. This set is mutable and change-insulated against the internal list. + * Returns all tags in this list as a Set. + * This set is mutable and change-insulated against the internal list. */ public Set toSet() { return new HashSet<>(internalList); diff --git a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java b/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java index 10905d644168..bca57788e05f 100644 --- a/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java +++ b/src/main/java/seedu/address/storage/JsonUserPrefsStorage.java @@ -14,7 +14,7 @@ public class JsonUserPrefsStorage implements UserPrefsStorage { private String filePath; - public JsonUserPrefsStorage(String filePath){ + public JsonUserPrefsStorage(String filePath) { this.filePath = filePath; } diff --git a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java index f2167ec201b4..679d70eae6bc 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedPerson.java +++ b/src/main/java/seedu/address/storage/XmlAdaptedPerson.java @@ -27,7 +27,8 @@ public class XmlAdaptedPerson { private List tagged = new ArrayList<>(); /** - * No-arg constructor for JAXB use. + * Constructs an XmlAdaptedPerson. + * This is the no-arg constructor that is required by JAXB. */ public XmlAdaptedPerson() {} diff --git a/src/main/java/seedu/address/storage/XmlAdaptedTag.java b/src/main/java/seedu/address/storage/XmlAdaptedTag.java index de199188c7e1..0c290df993ac 100644 --- a/src/main/java/seedu/address/storage/XmlAdaptedTag.java +++ b/src/main/java/seedu/address/storage/XmlAdaptedTag.java @@ -14,7 +14,8 @@ public class XmlAdaptedTag { public String tagName; /** - * No-arg constructor for JAXB use. + * Constructs an XmlAdaptedTag. + * This is the no-arg constructor that is required by JAXB. */ public XmlAdaptedTag() {} diff --git a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java b/src/main/java/seedu/address/storage/XmlAddressBookStorage.java index bfeda7b006a8..80fa00805068 100644 --- a/src/main/java/seedu/address/storage/XmlAddressBookStorage.java +++ b/src/main/java/seedu/address/storage/XmlAddressBookStorage.java @@ -20,11 +20,11 @@ public class XmlAddressBookStorage implements AddressBookStorage { private String filePath; - public XmlAddressBookStorage(String filePath){ + public XmlAddressBookStorage(String filePath) { this.filePath = filePath; } - public String getAddressBookFilePath(){ + public String getAddressBookFilePath() { return filePath; } diff --git a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java b/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java index 0a2e0508a5af..3e4167f0e245 100644 --- a/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java +++ b/src/main/java/seedu/address/storage/XmlSerializableAddressBook.java @@ -25,7 +25,8 @@ public class XmlSerializableAddressBook implements ReadOnlyAddressBook { private List tags; /** - * Empty constructor required for marshalling + * Creates an empty XmlSerializableAddressBook. + * This empty constructor is required for marshalling. */ public XmlSerializableAddressBook() { persons = new ArrayList<>(); diff --git a/src/main/java/seedu/address/ui/BrowserPanel.java b/src/main/java/seedu/address/ui/BrowserPanel.java index cdd192a615df..85aa2623cc9f 100644 --- a/src/main/java/seedu/address/ui/BrowserPanel.java +++ b/src/main/java/seedu/address/ui/BrowserPanel.java @@ -1,6 +1,7 @@ package seedu.address.ui; import javafx.event.Event; +import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.scene.web.WebView; @@ -13,17 +14,13 @@ /** * The Browser Panel of the App. */ -public class BrowserPanel extends UiPart{ +public class BrowserPanel extends UiPart { private static Logger logger = LogsCenter.getLogger(BrowserPanel.class); - private WebView browser; - - /** - * Constructor is kept private as {@link #load(AnchorPane)} is the only way to create a BrowserPanel. - */ - private BrowserPanel() { + private static final String FXML = "BrowserPanel.fxml"; - } + @FXML + private WebView browser; @Override public void setNode(Node node) { @@ -32,18 +29,18 @@ public void setNode(Node node) { @Override public String getFxmlPath() { - return null; //not applicable + return FXML; } /** - * Factory method for creating a Browser Panel. + * Creates a BrowserPanel. + * This is the factory method for creating a Browser Panel. * This method should be called after the FX runtime is initialized and in FX application thread. * @param placeholder The AnchorPane where the BrowserPanel must be inserted */ - public static BrowserPanel load(AnchorPane placeholder){ + public static BrowserPanel load(AnchorPane placeholder) { logger.info("Initializing browser"); - BrowserPanel browserPanel = new BrowserPanel(); - browserPanel.browser = new WebView(); + BrowserPanel browserPanel = UiPartLoader.loadUiPart(placeholder, new BrowserPanel()); placeholder.setOnKeyPressed(Event::consume); // To prevent triggering events for typing inside the // loaded Web page. FxViewUtil.applyAnchorBoundaryParameters(browserPanel.browser, 0.0, 0.0, 0.0, 0.0); @@ -55,7 +52,7 @@ public void loadPersonPage(ReadOnlyPerson person) { loadPage("https://www.google.com.sg/#safe=off&q=" + person.getName().fullName.replaceAll(" ", "+")); } - public void loadPage(String url){ + public void loadPage(String url) { browser.getEngine().load(url); } diff --git a/src/main/java/seedu/address/ui/CommandBox.java b/src/main/java/seedu/address/ui/CommandBox.java index fae5ff72522e..018956f3020a 100644 --- a/src/main/java/seedu/address/ui/CommandBox.java +++ b/src/main/java/seedu/address/ui/CommandBox.java @@ -6,7 +6,6 @@ import javafx.scene.control.SplitPane; import javafx.scene.control.TextField; import javafx.scene.layout.AnchorPane; -import javafx.stage.Stage; import seedu.address.commons.core.LogsCenter; import seedu.address.commons.events.ui.IncorrectCommandAttemptedEvent; import seedu.address.commons.events.ui.NewResultAvailableEvent; @@ -29,8 +28,8 @@ public class CommandBox extends UiPart { @FXML private TextField commandTextField; - public static CommandBox load(Stage primaryStage, AnchorPane commandBoxPlaceholder, Logic logic) { - CommandBox commandBox = UiPartLoader.loadUiPart(primaryStage, commandBoxPlaceholder, new CommandBox()); + public static CommandBox load(AnchorPane commandBoxPlaceholder, Logic logic) { + CommandBox commandBox = UiPartLoader.loadUiPart(commandBoxPlaceholder, new CommandBox()); commandBox.configure(logic); commandBox.addToPlaceholder(); return commandBox; @@ -88,7 +87,7 @@ private void setStyleToIndicateCorrectCommand() { } @Subscribe - private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event){ + private void handleIncorrectCommandAttempted(IncorrectCommandAttemptedEvent event) { logger.info(LogsCenter.getEventHandlingLogMessage(event, "Invalid command: " + previousCommandText)); setStyleToIndicateIncorrectCommand(); restoreCommandText(); diff --git a/src/main/java/seedu/address/ui/HelpWindow.java b/src/main/java/seedu/address/ui/HelpWindow.java index 45b765ab6a0c..238defddd668 100644 --- a/src/main/java/seedu/address/ui/HelpWindow.java +++ b/src/main/java/seedu/address/ui/HelpWindow.java @@ -26,9 +26,9 @@ public class HelpWindow extends UiPart { private Stage dialogStage; - public static HelpWindow load(Stage primaryStage) { + public static HelpWindow load() { logger.fine("Showing help page about the application."); - HelpWindow helpWindow = UiPartLoader.loadUiPart(primaryStage, new HelpWindow()); + HelpWindow helpWindow = UiPartLoader.loadUiPart(new HelpWindow()); helpWindow.configure(); return helpWindow; } @@ -43,12 +43,12 @@ public String getFxmlPath() { return FXML; } - private void configure(){ + private void configure() { Scene scene = new Scene(mainPane); //Null passed as the parent stage to make it non-modal. dialogStage = createDialogStage(TITLE, null, scene); dialogStage.setMaximized(true); //TODO: set a more appropriate initial size - setIcon(dialogStage, ICON); + FxViewUtil.setStageIcon(dialogStage, ICON); WebView browser = new WebView(); browser.getEngine().load(USERGUIDE_URL); diff --git a/src/main/java/seedu/address/ui/MainWindow.java b/src/main/java/seedu/address/ui/MainWindow.java index 520e56608e29..5dc2d36c36bf 100644 --- a/src/main/java/seedu/address/ui/MainWindow.java +++ b/src/main/java/seedu/address/ui/MainWindow.java @@ -1,16 +1,20 @@ package seedu.address.ui; +import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.Scene; import javafx.scene.control.MenuItem; +import javafx.scene.control.TextInputControl; import javafx.scene.input.KeyCombination; +import javafx.scene.input.KeyEvent; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; import javafx.stage.Stage; import seedu.address.commons.core.Config; import seedu.address.commons.core.GuiSettings; import seedu.address.commons.events.ui.ExitAppRequestEvent; +import seedu.address.commons.util.FxViewUtil; import seedu.address.logic.Logic; import seedu.address.model.UserPrefs; import seedu.address.model.person.ReadOnlyPerson; @@ -26,6 +30,7 @@ public class MainWindow extends UiPart { private static final int MIN_HEIGHT = 600; private static final int MIN_WIDTH = 450; + private Stage primaryStage; private Logic logic; // Independent Ui parts residing in this Ui container @@ -70,16 +75,21 @@ public String getFxmlPath() { return FXML; } + public Stage getPrimaryStage() { + return primaryStage; + } + public static MainWindow load(Stage primaryStage, Config config, UserPrefs prefs, Logic logic) { - MainWindow mainWindow = UiPartLoader.loadUiPart(primaryStage, new MainWindow()); - mainWindow.configure(config.getAppTitle(), config.getAddressBookName(), config, prefs, logic); + MainWindow mainWindow = UiPartLoader.loadUiPart(new MainWindow()); + mainWindow.configure(primaryStage, config.getAppTitle(), config.getAddressBookName(), config, prefs, logic); return mainWindow; } - private void configure(String appTitle, String addressBookName, Config config, UserPrefs prefs, - Logic logic) { + private void configure(Stage primaryStage, String appTitle, String addressBookName, Config config, + UserPrefs prefs, Logic logic) { // Set dependencies + this.primaryStage = primaryStage; this.logic = logic; this.addressBookName = addressBookName; this.config = config; @@ -97,16 +107,45 @@ private void configure(String appTitle, String addressBookName, Config config, U } private void setAccelerators() { - helpMenuItem.setAccelerator(KeyCombination.valueOf("F1")); + setAccelerator(helpMenuItem, KeyCombination.valueOf("F1")); + } + + /** + * Sets the accelerator of a MenuItem. + * @param keyCombination the KeyCombination value of the accelerator + */ + private void setAccelerator(MenuItem menuItem, KeyCombination keyCombination) { + menuItem.setAccelerator(keyCombination); + + /* + * TODO: the code below can be removed once the bug reported here + * https://bugs.openjdk.java.net/browse/JDK-8131666 + * is fixed in later version of SDK. + * + * According to the bug report, TextInputControl (TextField, TextArea) will + * consume function-key events. Because CommandBox contains a TextField, and + * ResultDisplay contains a TextArea, thus some accelerators (e.g F1) will + * not work when the focus is in them because the key event is consumed by + * the TextInputControl(s). + * + * For now, we add following event filter to capture such key events and open + * help window purposely so to support accelerators even when focus is + * in CommandBox or ResultDisplay. + */ + rootLayout.addEventFilter(KeyEvent.KEY_PRESSED, event -> { + if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) { + menuItem.getOnAction().handle(new ActionEvent()); + event.consume(); + } + }); } void fillInnerParts() { browserPanel = BrowserPanel.load(browserPlaceholder); - personListPanel = PersonListPanel.load(primaryStage, getPersonListPlaceholder(), logic.getFilteredPersonList()); - resultDisplay = ResultDisplay.load(primaryStage, getResultDisplayPlaceholder()); - statusBarFooter = StatusBarFooter.load(primaryStage, getStatusbarPlaceholder(), - config.getAddressBookFilePath()); - commandBox = CommandBox.load(primaryStage, getCommandBoxPlaceholder(), logic); + personListPanel = PersonListPanel.load(getPersonListPlaceholder(), logic.getFilteredPersonList()); + resultDisplay = ResultDisplay.load(getResultDisplayPlaceholder()); + statusBarFooter = StatusBarFooter.load(getStatusbarPlaceholder(), config.getAddressBookFilePath()); + commandBox = CommandBox.load(getCommandBoxPlaceholder(), logic); } private AnchorPane getCommandBoxPlaceholder() { @@ -133,6 +172,14 @@ private void setTitle(String appTitle) { primaryStage.setTitle(appTitle); } + /** + * Sets the given image as the icon of the main window. + * @param iconSource e.g. {@code "/images/help_icon.png"} + */ + private void setIcon(String iconSource) { + FxViewUtil.setStageIcon(primaryStage, iconSource); + } + /** * Sets the default size based on user preferences. */ @@ -160,7 +207,7 @@ GuiSettings getCurrentGuiSetting() { @FXML public void handleHelp() { - HelpWindow helpWindow = HelpWindow.load(primaryStage); + HelpWindow helpWindow = HelpWindow.load(); helpWindow.show(); } diff --git a/src/main/java/seedu/address/ui/PersonCard.java b/src/main/java/seedu/address/ui/PersonCard.java index f9af1bdaa97e..571bd0d21433 100644 --- a/src/main/java/seedu/address/ui/PersonCard.java +++ b/src/main/java/seedu/address/ui/PersonCard.java @@ -3,6 +3,7 @@ import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.layout.FlowPane; import javafx.scene.layout.HBox; import seedu.address.model.person.ReadOnlyPerson; @@ -23,7 +24,7 @@ public class PersonCard extends UiPart { @FXML private Label email; @FXML - private Label tags; + private FlowPane tags; private ReadOnlyPerson person; private int displayedIndex; @@ -33,7 +34,7 @@ public static PersonCard load(ReadOnlyPerson person, int displayedIndex) { PersonCard card = new PersonCard(); card.person = person; card.displayedIndex = displayedIndex; - return UiPartLoader.loadUiPart(card); + return UiPartLoader.initUiPart(card); } @FXML @@ -43,7 +44,7 @@ public void initialize() { phone.setText(person.getPhone().value); address.setText(person.getAddress().value); email.setText(person.getEmail().value); - tags.setText(person.tagsString()); + initTags(); } public HBox getLayout() { @@ -59,4 +60,8 @@ public void setNode(Node node) { public String getFxmlPath() { return FXML; } + + private void initTags() { + person.getTags().forEach(tag -> tags.getChildren().add(new Label(tag.tagName))); + } } diff --git a/src/main/java/seedu/address/ui/PersonListPanel.java b/src/main/java/seedu/address/ui/PersonListPanel.java index 4f18bfb53f7f..00224d974d7b 100644 --- a/src/main/java/seedu/address/ui/PersonListPanel.java +++ b/src/main/java/seedu/address/ui/PersonListPanel.java @@ -9,7 +9,6 @@ import javafx.scene.control.SplitPane; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.VBox; -import javafx.stage.Stage; import seedu.address.commons.events.ui.PersonPanelSelectionChangedEvent; import seedu.address.model.person.ReadOnlyPerson; import seedu.address.commons.core.LogsCenter; @@ -43,10 +42,9 @@ public void setPlaceholder(AnchorPane pane) { this.placeHolderPane = pane; } - public static PersonListPanel load(Stage primaryStage, AnchorPane personListPlaceholder, + public static PersonListPanel load(AnchorPane personListPlaceholder, ObservableList personList) { - PersonListPanel personListPanel = - UiPartLoader.loadUiPart(primaryStage, personListPlaceholder, new PersonListPanel()); + PersonListPanel personListPanel = UiPartLoader.loadUiPart(personListPlaceholder, new PersonListPanel()); personListPanel.configure(personList); return personListPanel; } diff --git a/src/main/java/seedu/address/ui/ResultDisplay.java b/src/main/java/seedu/address/ui/ResultDisplay.java index 12723451c7fd..dad04e5d950a 100644 --- a/src/main/java/seedu/address/ui/ResultDisplay.java +++ b/src/main/java/seedu/address/ui/ResultDisplay.java @@ -7,7 +7,6 @@ import javafx.scene.Node; import javafx.scene.control.TextArea; import javafx.scene.layout.AnchorPane; -import javafx.stage.Stage; import seedu.address.commons.core.LogsCenter; import seedu.address.commons.events.ui.NewResultAvailableEvent; import seedu.address.commons.util.FxViewUtil; @@ -33,8 +32,8 @@ public class ResultDisplay extends UiPart { @FXML private TextArea resultDisplay; - public static ResultDisplay load(Stage primaryStage, AnchorPane placeHolder) { - ResultDisplay resultDisplay = UiPartLoader.loadUiPart(primaryStage, placeHolder, new ResultDisplay()); + public static ResultDisplay load(AnchorPane placeHolder) { + ResultDisplay resultDisplay = UiPartLoader.loadUiPart(placeHolder, new ResultDisplay()); resultDisplay.configure(); return resultDisplay; } diff --git a/src/main/java/seedu/address/ui/StatusBarFooter.java b/src/main/java/seedu/address/ui/StatusBarFooter.java index f74f66be6fc9..861cb3735baf 100644 --- a/src/main/java/seedu/address/ui/StatusBarFooter.java +++ b/src/main/java/seedu/address/ui/StatusBarFooter.java @@ -5,7 +5,6 @@ import javafx.scene.Node; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.GridPane; -import javafx.stage.Stage; import org.controlsfx.control.StatusBar; import seedu.address.commons.core.LogsCenter; import seedu.address.commons.events.model.AddressBookChangedEvent; @@ -19,32 +18,27 @@ */ public class StatusBarFooter extends UiPart { private static final Logger logger = LogsCenter.getLogger(StatusBarFooter.class); + + @FXML private StatusBar syncStatus; + @FXML private StatusBar saveLocationStatus; private GridPane mainPane; - @FXML - private AnchorPane saveLocStatusBarPane; - - @FXML - private AnchorPane syncStatusBarPane; - private AnchorPane placeHolder; private static final String FXML = "StatusBarFooter.fxml"; - public static StatusBarFooter load(Stage stage, AnchorPane placeHolder, String saveLocation) { - StatusBarFooter statusBarFooter = UiPartLoader.loadUiPart(stage, placeHolder, new StatusBarFooter()); + public static StatusBarFooter load(AnchorPane placeHolder, String saveLocation) { + StatusBarFooter statusBarFooter = UiPartLoader.loadUiPart(placeHolder, new StatusBarFooter()); statusBarFooter.configure(saveLocation); return statusBarFooter; } public void configure(String saveLocation) { addMainPane(); - addSyncStatus(); setSyncStatus("Not updated yet in this session"); - addSaveLocation(); setSaveLocation("./" + saveLocation); registerAsAnEventHandler(this); } @@ -58,22 +52,10 @@ private void setSaveLocation(String location) { this.saveLocationStatus.setText(location); } - private void addSaveLocation() { - this.saveLocationStatus = new StatusBar(); - FxViewUtil.applyAnchorBoundaryParameters(saveLocationStatus, 0.0, 0.0, 0.0, 0.0); - saveLocStatusBarPane.getChildren().add(saveLocationStatus); - } - private void setSyncStatus(String status) { this.syncStatus.setText(status); } - private void addSyncStatus() { - this.syncStatus = new StatusBar(); - FxViewUtil.applyAnchorBoundaryParameters(syncStatus, 0.0, 0.0, 0.0, 0.0); - syncStatusBarPane.getChildren().add(syncStatus); - } - @Override public void setNode(Node node) { mainPane = (GridPane) node; diff --git a/src/main/java/seedu/address/ui/UiPart.java b/src/main/java/seedu/address/ui/UiPart.java index 492b4eebf61a..f33741c999bd 100644 --- a/src/main/java/seedu/address/ui/UiPart.java +++ b/src/main/java/seedu/address/ui/UiPart.java @@ -7,7 +7,6 @@ import javafx.stage.Stage; import seedu.address.commons.core.EventsCenter; import seedu.address.commons.events.BaseEvent; -import seedu.address.commons.util.AppUtil; /** * Base class for UI parts. @@ -15,17 +14,11 @@ */ public abstract class UiPart { - /** - * The primary stage for the UI Part. - */ - Stage primaryStage; - - /** * Raises the event via {@link EventsCenter#post(BaseEvent)} * @param event */ - protected void raise(BaseEvent event){ + protected void raise(BaseEvent event) { EventsCenter.getInstance().post(event); } @@ -38,22 +31,18 @@ protected void registerAsAnEventHandler(Object handler) { } /** + * Sets the main node of the UiPart. * Override this method to receive the main Node generated while loading the view from the .fxml file. * @param node */ public abstract void setNode(Node node); /** + * Returns the path to the fxml file. * Override this method to return the name of the fxml file. e.g. {@code "MainWindow.fxml"} - * @return */ public abstract String getFxmlPath(); - public void setStage(Stage primaryStage) { - this.primaryStage = primaryStage; - } - - /** * Creates a modal dialog. * @param title Title of the dialog. @@ -70,23 +59,6 @@ protected Stage createDialogStage(String title, Stage parentStage, Scene scene) return dialogStage; } - /** - * Sets the given image as the icon for the primary stage of this UI Part. - * @param iconSource e.g. {@code "/images/help_icon.png"} - */ - protected void setIcon(String iconSource) { - primaryStage.getIcons().add(AppUtil.getImage(iconSource)); - } - - /** - * Sets the given image as the icon for the given stage. - * @param stage - * @param iconSource e.g. {@code "/images/help_icon.png"} - */ - protected void setIcon(Stage stage, String iconSource) { - stage.getIcons().add(AppUtil.getImage(iconSource)); - } - /** * Sets the placeholder for UI parts that reside inside another UI part. * @param placeholder @@ -95,7 +67,4 @@ public void setPlaceholder(AnchorPane placeholder) { //Do nothing by default. } - public Stage getPrimaryStage() { - return primaryStage; - } } diff --git a/src/main/java/seedu/address/ui/UiPartLoader.java b/src/main/java/seedu/address/ui/UiPartLoader.java index 86e0182846eb..7503fa456718 100644 --- a/src/main/java/seedu/address/ui/UiPartLoader.java +++ b/src/main/java/seedu/address/ui/UiPartLoader.java @@ -3,7 +3,6 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Node; import javafx.scene.layout.AnchorPane; -import javafx.stage.Stage; import seedu.address.MainApp; /** @@ -12,37 +11,41 @@ public class UiPartLoader { private static final String FXML_FILE_FOLDER = "/view/"; - public static T loadUiPart(Stage primaryStage, T controllerSeed) { - return loadUiPart(primaryStage, null, controllerSeed); - } - /** * Returns the ui class for a specific UI Part. * - * @param primaryStage The primary stage for the view. - * @param placeholder The placeholder where the loaded Ui Part is added. * @param sampleUiPart The sample of the expected UiPart class. * @param The type of the UiPart */ - public static T loadUiPart(Stage primaryStage, AnchorPane placeholder, T sampleUiPart) { + public static T loadUiPart(T sampleUiPart) { FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource(FXML_FILE_FOLDER + sampleUiPart.getFxmlPath())); Node mainNode = loadLoader(loader, sampleUiPart.getFxmlPath()); - UiPart controller = loader.getController(); - controller.setStage(primaryStage); - controller.setPlaceholder(placeholder); + T controller = loader.getController(); controller.setNode(mainNode); - return (T) controller; + return controller; } /** * Returns the ui class for a specific UI Part. * - * @param seedUiPart The UiPart object to be used as the ui. + * @param placeholder The placeholder where the loaded Ui Part is added. + * @param sampleUiPart The sample of the expected UiPart class. * @param The type of the UiPart */ + public static T loadUiPart(AnchorPane placeholder, T sampleUiPart) { + T controller = loadUiPart(sampleUiPart); + controller.setPlaceholder(placeholder); + return controller; + } - public static T loadUiPart(T seedUiPart) { + /** + * Initializes the FXML scene graph of the provided UI Part. + * + * @param seedUiPart The UiPart object to be used as the ui. + * @param The type of the UiPart + */ + public static T initUiPart(T seedUiPart) { FXMLLoader loader = new FXMLLoader(); loader.setLocation(MainApp.class.getResource(FXML_FILE_FOLDER + seedUiPart.getFxmlPath())); loader.setController(seedUiPart); diff --git a/src/main/resources/view/BrowserPanel.fxml b/src/main/resources/view/BrowserPanel.fxml new file mode 100644 index 000000000000..21c41d4db57f --- /dev/null +++ b/src/main/resources/view/BrowserPanel.fxml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css index 8043b344253a..93f7a7c9b708 100644 --- a/src/main/resources/view/DarkTheme.css +++ b/src/main/resources/view/DarkTheme.css @@ -285,4 +285,18 @@ #filterField, #personListPanel, #personWebpage { -fx-effect: innershadow(gaussian, black, 10, 0, 0, 0); +} + +#tags { + -fx-hgap: 7; + -fx-vgap: 3; +} + +#tags .label { + -fx-text-fill: white; + -fx-background-color: #383838; + -fx-padding: 1 3 1 3; + -fx-border-radius: 2; + -fx-background-radius: 2; + -fx-font-size: 11; } \ No newline at end of file diff --git a/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml b/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml deleted file mode 100644 index bc761118235a..000000000000 --- a/src/main/resources/view/DefaultBrowserPlaceHolderScreen.fxml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - diff --git a/src/main/resources/view/PersonListCard.fxml b/src/main/resources/view/PersonListCard.fxml index 13d4b149651b..a8e0ee4f2a80 100644 --- a/src/main/resources/view/PersonListCard.fxml +++ b/src/main/resources/view/PersonListCard.fxml @@ -1,18 +1,23 @@ - - - + + + + + + + + + - + - - + @@ -28,9 +33,9 @@ -