diff --git a/src/main/java/seedu/address/Main.java b/src/main/java/seedu/address/Main.java
index 052a5068631..6aa3add163f 100644
--- a/src/main/java/seedu/address/Main.java
+++ b/src/main/java/seedu/address/Main.java
@@ -20,6 +20,6 @@
*/
public class Main {
public static void main(String[] args) {
- Application.launch(MainApp.class, args);
+ Application.launch(NewMainApp.class, args);
}
}
diff --git a/src/main/java/seedu/address/NewMainApp.java b/src/main/java/seedu/address/NewMainApp.java
new file mode 100644
index 00000000000..d78900504ab
--- /dev/null
+++ b/src/main/java/seedu/address/NewMainApp.java
@@ -0,0 +1,183 @@
+package seedu.address;
+
+import java.io.IOException;
+import java.nio.file.Path;
+import java.util.Optional;
+import java.util.logging.Logger;
+
+import javafx.application.Application;
+import javafx.stage.Stage;
+import seedu.address.commons.core.Config;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.core.Version;
+import seedu.address.commons.exceptions.DataConversionException;
+import seedu.address.commons.util.ConfigUtil;
+import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.Logic;
+import seedu.address.logic.LogicManager;
+import seedu.address.model.AddressBook;
+import seedu.address.model.Model;
+import seedu.address.model.ModelManager;
+import seedu.address.model.ReadOnlyAddressBook;
+import seedu.address.model.ReadOnlyUserPrefs;
+import seedu.address.model.UserPrefs;
+import seedu.address.model.util.SampleDataUtil;
+import seedu.address.storage.AddressBookStorage;
+import seedu.address.storage.JsonAddressBookStorage;
+import seedu.address.storage.JsonUserPrefsStorage;
+import seedu.address.storage.Storage;
+import seedu.address.storage.StorageManager;
+import seedu.address.storage.UserPrefsStorage;
+import seedu.address.ui.NewUiManager;
+import seedu.address.ui.Ui;
+
+/**
+ * Runs the application.
+ */
+public class NewMainApp extends Application {
+
+ public static final Version VERSION = new Version(0, 6, 0, true);
+
+ private static final Logger logger = LogsCenter.getLogger(MainApp.class);
+
+ protected Ui ui;
+ protected Logic logic;
+ protected Storage storage;
+ protected Model model;
+ protected Config config;
+
+ @Override
+ public void init() throws Exception {
+ logger.info("=============================[ Initializing AddressBook ]===========================");
+ super.init();
+
+ AppParameters appParameters = AppParameters.parse(getParameters());
+ config = initConfig(appParameters.getConfigPath());
+
+ UserPrefsStorage userPrefsStorage = new JsonUserPrefsStorage(config.getUserPrefsFilePath());
+ UserPrefs userPrefs = initPrefs(userPrefsStorage);
+ AddressBookStorage addressBookStorage = new JsonAddressBookStorage(userPrefs.getAddressBookFilePath());
+ storage = new StorageManager(addressBookStorage, userPrefsStorage);
+
+ initLogging(config);
+
+ model = initModelManager(storage, userPrefs);
+
+ logic = new LogicManager(model, storage);
+
+ ui = new NewUiManager(logic);
+ }
+
+ /**
+ * Returns a {@code ModelManager} with the data from {@code storage}'s address book and {@code userPrefs}.
+ * The data from the sample address book will be used instead if {@code storage}'s address book is not found,
+ * or an empty address book will be used instead if errors occur when reading {@code storage}'s address book.
+ */
+ private Model initModelManager(Storage storage, ReadOnlyUserPrefs userPrefs) {
+ Optional addressBookOptional;
+ ReadOnlyAddressBook initialData;
+ try {
+ addressBookOptional = storage.readAddressBook();
+ if (!addressBookOptional.isPresent()) {
+ logger.info("Data file not found. Will be starting with a sample AddressBook");
+ }
+ initialData = addressBookOptional.orElseGet(SampleDataUtil::getSampleAddressBook);
+ } catch (DataConversionException e) {
+ logger.warning("Data file not in the correct format. Will be starting with an empty AddressBook");
+ initialData = new AddressBook();
+ } catch (IOException e) {
+ logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ initialData = new AddressBook();
+ }
+
+ return new ModelManager(initialData, userPrefs);
+ }
+
+ private void initLogging(Config config) {
+ LogsCenter.init(config);
+ }
+
+ /**
+ * Returns a {@code Config} using the file at {@code configFilePath}.
+ * The default file path {@code Config#DEFAULT_CONFIG_FILE} will be used instead
+ * if {@code configFilePath} is null.
+ */
+ protected Config initConfig(Path configFilePath) {
+ Config initializedConfig;
+ Path configFilePathUsed;
+
+ configFilePathUsed = Config.DEFAULT_CONFIG_FILE;
+
+ if (configFilePath != null) {
+ logger.info("Custom Config file specified " + configFilePath);
+ configFilePathUsed = configFilePath;
+ }
+
+ logger.info("Using config file : " + configFilePathUsed);
+
+ try {
+ Optional configOptional = ConfigUtil.readConfig(configFilePathUsed);
+ initializedConfig = configOptional.orElse(new Config());
+ } catch (DataConversionException e) {
+ logger.warning("Config file at " + configFilePathUsed + " is not in the correct format. "
+ + "Using default config properties");
+ initializedConfig = new Config();
+ }
+
+ //Update config file in case it was missing to begin with or there are new/unused fields
+ try {
+ ConfigUtil.saveConfig(initializedConfig, configFilePathUsed);
+ } catch (IOException e) {
+ logger.warning("Failed to save config file : " + StringUtil.getDetails(e));
+ }
+ return initializedConfig;
+ }
+
+ /**
+ * Returns a {@code UserPrefs} using the file at {@code storage}'s user prefs file path,
+ * or a new {@code UserPrefs} with default configuration if errors occur when
+ * reading from the file.
+ */
+ protected UserPrefs initPrefs(UserPrefsStorage storage) {
+ Path prefsFilePath = storage.getUserPrefsFilePath();
+ logger.info("Using prefs file : " + prefsFilePath);
+
+ UserPrefs initializedPrefs;
+ try {
+ Optional prefsOptional = storage.readUserPrefs();
+ initializedPrefs = prefsOptional.orElse(new UserPrefs());
+ } catch (DataConversionException e) {
+ logger.warning("UserPrefs file at " + prefsFilePath + " is not in the correct format. "
+ + "Using default user prefs");
+ initializedPrefs = new UserPrefs();
+ } catch (IOException e) {
+ logger.warning("Problem while reading from the file. Will be starting with an empty AddressBook");
+ initializedPrefs = new UserPrefs();
+ }
+
+ //Update prefs file in case it was missing to begin with or there are new/unused fields
+ try {
+ storage.saveUserPrefs(initializedPrefs);
+ } catch (IOException e) {
+ logger.warning("Failed to save config file : " + StringUtil.getDetails(e));
+ }
+
+ return initializedPrefs;
+ }
+
+ @Override
+ public void start(Stage primaryStage) {
+ logger.info("Starting AddressBook " + MainApp.VERSION);
+ ui.start(primaryStage);
+ }
+
+ @Override
+ public void stop() {
+ logger.info("============================ [ Stopping Address Book ] =============================");
+ try {
+ storage.saveUserPrefs(model.getUserPrefs());
+ } catch (IOException e) {
+ logger.severe("Failed to save preferences " + StringUtil.getDetails(e));
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/FooterBar.java b/src/main/java/seedu/address/ui/FooterBar.java
new file mode 100644
index 00000000000..6f90e3c7f0d
--- /dev/null
+++ b/src/main/java/seedu/address/ui/FooterBar.java
@@ -0,0 +1,24 @@
+package seedu.address.ui;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.layout.Region;
+
+/**
+ * A ui for the footer bar that is displayed at the footer of the application.
+ */
+public class FooterBar extends UiPart {
+
+ private static final String FXML = "FooterBar.fxml";
+
+ @FXML
+ private Label versionNumber;
+
+ /**
+ * Creates a {@code StatusBarFooter} with the given {@code Path}.
+ */
+ public FooterBar(String versionNum) {
+ super(FXML);
+ versionNumber.setText(versionNum);
+ }
+}
diff --git a/src/main/java/seedu/address/ui/LastInputDisplay.java b/src/main/java/seedu/address/ui/LastInputDisplay.java
new file mode 100644
index 00000000000..2aa861c5b52
--- /dev/null
+++ b/src/main/java/seedu/address/ui/LastInputDisplay.java
@@ -0,0 +1,28 @@
+package seedu.address.ui;
+
+import static java.util.Objects.requireNonNull;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.TextArea;
+import javafx.scene.layout.Region;
+
+/**
+ * A ui for the status bar that is displayed at the header of the application.
+ */
+public class LastInputDisplay extends UiPart {
+
+ private static final String FXML = "ResultDisplay.fxml";
+
+ @FXML
+ private TextArea resultDisplay;
+
+ public LastInputDisplay() {
+ super(FXML);
+ }
+
+ public void setLastInput(String feedbackToUser) {
+ requireNonNull(feedbackToUser);
+ resultDisplay.setText(feedbackToUser);
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/NewCommandBox.java b/src/main/java/seedu/address/ui/NewCommandBox.java
new file mode 100644
index 00000000000..81625786d84
--- /dev/null
+++ b/src/main/java/seedu/address/ui/NewCommandBox.java
@@ -0,0 +1,81 @@
+package seedu.address.ui;
+
+import javafx.collections.ObservableList;
+import javafx.fxml.FXML;
+import javafx.scene.control.TextField;
+import javafx.scene.layout.Region;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * The UI component that is responsible for receiving user command inputs.
+ */
+public class NewCommandBox extends UiPart {
+
+ public static final String ERROR_STYLE_CLASS = "error";
+ private static final String FXML = "CommandBox.fxml";
+
+ private final CommandExecutor commandExecutor;
+
+ @FXML
+ private TextField commandTextField;
+
+ /**
+ * Creates a {@code CommandBox} with the given {@code CommandExecutor}.
+ */
+ public NewCommandBox(CommandExecutor commandExecutor) {
+ super(FXML);
+ this.commandExecutor = commandExecutor;
+ // calls #setStyleToDefault() whenever there is a change to the text of the command box.
+ commandTextField.textProperty().addListener((unused1, unused2, unused3) -> setStyleToDefault());
+ }
+
+ /**
+ * Handles the Enter button pressed event.
+ */
+ @FXML
+ private void handleCommandEntered() {
+ try {
+ commandExecutor.execute(commandTextField.getText());
+ } catch (CommandException | ParseException e) {
+ // setStyleToIndicateCommandFailure();
+ }
+
+ commandTextField.setText("");
+ }
+
+ /**
+ * Sets the command box style to use the default style.
+ */
+ private void setStyleToDefault() {
+ commandTextField.getStyleClass().remove(ERROR_STYLE_CLASS);
+ }
+
+ /**
+ * Sets the command box style to indicate a failed command.
+ */
+ private void setStyleToIndicateCommandFailure() {
+ ObservableList styleClass = commandTextField.getStyleClass();
+
+ if (styleClass.contains(ERROR_STYLE_CLASS)) {
+ return;
+ }
+
+ styleClass.add(ERROR_STYLE_CLASS);
+ }
+
+ /**
+ * Represents a function that can execute commands.
+ */
+ @FunctionalInterface
+ public interface CommandExecutor {
+ /**
+ * Executes the command and returns the result.
+ *
+ * @see seedu.address.logic.Logic#execute(String)
+ */
+ CommandResult execute(String commandText) throws CommandException, ParseException;
+ }
+
+}
diff --git a/src/main/java/seedu/address/ui/NewMainWindow.java b/src/main/java/seedu/address/ui/NewMainWindow.java
new file mode 100644
index 00000000000..a70b476ed65
--- /dev/null
+++ b/src/main/java/seedu/address/ui/NewMainWindow.java
@@ -0,0 +1,194 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.TextInputControl;
+import javafx.scene.input.KeyCombination;
+import javafx.scene.input.KeyEvent;
+import javafx.scene.layout.StackPane;
+import javafx.stage.Stage;
+import seedu.address.commons.core.GuiSettings;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.logic.Logic;
+import seedu.address.logic.commands.CommandResult;
+import seedu.address.logic.commands.exceptions.CommandException;
+import seedu.address.logic.parser.exceptions.ParseException;
+
+/**
+ * The Main Window. Provides the basic application layout containing
+ * a menu bar and space where other JavaFX elements can be placed.
+ */
+public class NewMainWindow extends UiPart {
+
+ private static final String FXML = "NewMainWindow.fxml";
+
+ private final Logger logger = LogsCenter.getLogger(getClass());
+
+ private Stage primaryStage;
+ private ResultDisplay resultDisplay;
+ private LastInputDisplay lastInputDisplay;
+ private Logic logic;
+
+ // Independent Ui parts residing in this Ui container
+
+ @FXML
+ private StackPane resultDisplayPlaceHolder;
+
+ @FXML
+ private StackPane tagListPlaceholder;
+
+ @FXML
+ private StackPane lastInputPlaceHolder;
+
+ @FXML
+ private StackPane commandBoxPlaceHolder;
+
+ @FXML
+ private StackPane footerbarPlaceHolder;
+
+ @FXML
+ private MenuItem helpMenuItem;
+
+ /**
+ * Creates a {@code MainWindow} with the given {@code Stage} and {@code Logic}.
+ */
+ public NewMainWindow(Stage primaryStage, Logic logic) {
+ super(FXML, primaryStage);
+
+ // Set dependencies
+ this.primaryStage = primaryStage;
+ this.logic = logic;
+
+ // Configure the UI
+ setWindowDefaultSize(logic.getGuiSettings());
+
+ // setAccelerators();
+ }
+
+ public Stage getPrimaryStage() {
+ return primaryStage;
+ }
+
+ private void setAccelerators() {
+ // 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.
+ */
+ getRoot().addEventFilter(KeyEvent.KEY_PRESSED, event -> {
+ if (event.getTarget() instanceof TextInputControl && keyCombination.match(event)) {
+ menuItem.getOnAction().handle(new ActionEvent());
+ event.consume();
+ }
+ });
+ }
+
+ /**
+ * Fills up all the placeholders of this window.
+ */
+ void fillInnerParts() {
+
+ // result display
+ resultDisplay = new ResultDisplay();
+ resultDisplayPlaceHolder.getChildren().add(resultDisplay.getRoot());
+
+ // last input display
+ lastInputDisplay = new LastInputDisplay();
+ lastInputPlaceHolder.getChildren().add(lastInputDisplay.getRoot());
+
+ // command box
+ NewCommandBox commandBox = new NewCommandBox(this::executeCommand);
+ commandBoxPlaceHolder.getChildren().add(commandBox.getRoot());
+
+ // footer bar
+ FooterBar footerBar = new FooterBar("1.2");
+ footerbarPlaceHolder.getChildren().add(footerBar.getRoot());
+ }
+
+ /**
+ * Sets the default size based on {@code guiSettings}.
+ */
+ private void setWindowDefaultSize(GuiSettings guiSettings) {
+ primaryStage.setHeight(guiSettings.getWindowHeight());
+ primaryStage.setWidth(guiSettings.getWindowWidth());
+ if (guiSettings.getWindowCoordinates() != null) {
+ primaryStage.setX(guiSettings.getWindowCoordinates().getX());
+ primaryStage.setY(guiSettings.getWindowCoordinates().getY());
+ }
+ }
+
+ /**
+ * Opens the help window or focuses on it if it's already opened.
+ */
+ @FXML
+ public void handleHelp() {
+
+ }
+
+ void show() {
+ primaryStage.show();
+ }
+
+ /**
+ * Closes the application.
+ */
+ @FXML
+ private void handleExit() {
+ GuiSettings guiSettings = new GuiSettings(primaryStage.getWidth(), primaryStage.getHeight(),
+ (int) primaryStage.getX(), (int) primaryStage.getY());
+ logic.setGuiSettings(guiSettings);
+ primaryStage.hide();
+ }
+
+ /**
+ * Executes the command and returns the result.
+ *
+ * @see seedu.address.logic.Logic#execute(String)
+ */
+ private CommandResult executeCommand(String commandText) throws CommandException, ParseException {
+ try {
+ CommandResult commandResult = logic.execute(commandText);
+ logger.info("Result: " + commandResult.getFeedbackToUser());
+ lastInputDisplay.setLastInput(commandText);
+ resultDisplay.setFeedbackToUser(commandResult.getFeedbackToUser());
+
+ if (commandResult.isShowHelp()) {
+ handleHelp();
+ }
+
+ if (commandResult.isExit()) {
+ handleExit();
+ }
+
+ return commandResult;
+ } catch (CommandException | ParseException e) {
+ logger.info("Invalid command: " + commandText);
+ lastInputDisplay.setLastInput(commandText);
+ resultDisplay.setFeedbackToUser(e.getMessage());
+ throw e;
+ }
+ }
+}
diff --git a/src/main/java/seedu/address/ui/NewUiManager.java b/src/main/java/seedu/address/ui/NewUiManager.java
new file mode 100644
index 00000000000..5e27ac8c123
--- /dev/null
+++ b/src/main/java/seedu/address/ui/NewUiManager.java
@@ -0,0 +1,89 @@
+package seedu.address.ui;
+
+import java.util.logging.Logger;
+
+import javafx.application.Platform;
+import javafx.scene.control.Alert;
+import javafx.scene.control.Alert.AlertType;
+import javafx.scene.image.Image;
+import javafx.stage.Stage;
+import seedu.address.MainApp;
+import seedu.address.commons.core.LogsCenter;
+import seedu.address.commons.util.StringUtil;
+import seedu.address.logic.Logic;
+
+/**
+ * The manager of the UI component.
+ */
+public class NewUiManager implements Ui {
+
+ public static final String ALERT_DIALOG_PANE_FIELD_ID = "alertDialogPane";
+
+ private static final Logger logger = LogsCenter.getLogger(UiManager.class);
+ private static final String ICON_APPLICATION = "/images/address_book_32.png";
+
+ private Logic logic;
+ private NewMainWindow mainWindow;
+
+ /**
+ * Creates a {@code UiManager} with the given {@code Logic}.
+ */
+ public NewUiManager(Logic logic) {
+ super();
+ this.logic = logic;
+ }
+
+ @Override
+ public void start(Stage primaryStage) {
+ logger.info("Starting UI...");
+
+ //Set the application icon.
+ primaryStage.getIcons().add(getImage(ICON_APPLICATION));
+
+ try {
+ mainWindow = new NewMainWindow(primaryStage, logic);
+ mainWindow.show(); //This should be called before creating other UI parts
+ mainWindow.fillInnerParts();
+
+ } catch (Throwable e) {
+ logger.severe(StringUtil.getDetails(e));
+ showFatalErrorDialogAndShutdown("Fatal error during initializing", e);
+ }
+ }
+
+ private Image getImage(String imagePath) {
+ return new Image(MainApp.class.getResourceAsStream(imagePath));
+ }
+
+ void showAlertDialogAndWait(Alert.AlertType type, String title, String headerText, String contentText) {
+ showAlertDialogAndWait(mainWindow.getPrimaryStage(), type, title, headerText, contentText);
+ }
+
+ /**
+ * Shows an alert dialog on {@code owner} with the given parameters.
+ * This method only returns after the user has closed the alert dialog.
+ */
+ private static void showAlertDialogAndWait(Stage owner, AlertType type, String title, String headerText,
+ String contentText) {
+ final Alert alert = new Alert(type);
+ alert.getDialogPane().getStylesheets().add("view/DarkTheme.css");
+ alert.initOwner(owner);
+ alert.setTitle(title);
+ alert.setHeaderText(headerText);
+ alert.setContentText(contentText);
+ alert.getDialogPane().setId(ALERT_DIALOG_PANE_FIELD_ID);
+ alert.showAndWait();
+ }
+
+ /**
+ * Shows an error alert dialog with {@code title} and error message, {@code e},
+ * and exits the application after the user has closed the alert dialog.
+ */
+ private void showFatalErrorDialogAndShutdown(String title, Throwable e) {
+ logger.severe(title + " " + e.getMessage() + StringUtil.getDetails(e));
+ showAlertDialogAndWait(Alert.AlertType.ERROR, title, e.getMessage(), e.toString());
+ Platform.exit();
+ System.exit(1);
+ }
+
+}
diff --git a/src/main/resources/view/CommandBox.fxml b/src/main/resources/view/CommandBox.fxml
index 09f6d6fe9e4..fa312ff9f6a 100644
--- a/src/main/resources/view/CommandBox.fxml
+++ b/src/main/resources/view/CommandBox.fxml
@@ -2,8 +2,11 @@
+
-
-
+
+
+
+
+
-
diff --git a/src/main/resources/view/FooterBar.fxml b/src/main/resources/view/FooterBar.fxml
new file mode 100644
index 00000000000..7f420ecf82b
--- /dev/null
+++ b/src/main/resources/view/FooterBar.fxml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/NewMainWindow.fxml b/src/main/resources/view/NewMainWindow.fxml
new file mode 100644
index 00000000000..82162cdbe8e
--- /dev/null
+++ b/src/main/resources/view/NewMainWindow.fxml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/view/ResultDisplay.fxml b/src/main/resources/view/ResultDisplay.fxml
index 58d5ad3dc56..a5cf7ad5c63 100644
--- a/src/main/resources/view/ResultDisplay.fxml
+++ b/src/main/resources/view/ResultDisplay.fxml
@@ -2,8 +2,11 @@
+
-
-
+
+