Skip to content

Latest commit

 

History

History
816 lines (706 loc) · 27.2 KB

A0148037E.md

File metadata and controls

816 lines (706 loc) · 27.2 KB

A0148037E

\java\seedu\geekeep\commons\core\TaskCategory.java
package seedu.geekeep.commons.core;

public enum TaskCategory {
    ALL,
    FINISHED,
    UPCOMING;
}
\java\seedu\geekeep\commons\events\model\SwitchTaskCategoryEvent.java
package seedu.geekeep.commons.events.model;

import seedu.geekeep.commons.core.TaskCategory;
import seedu.geekeep.commons.events.BaseEvent;

/** Indicates it is time to switch the tabs in mainwindow*/
public class SwitchTaskCategoryEvent extends BaseEvent {
    public TaskCategory category;

    public SwitchTaskCategoryEvent(TaskCategory category) {
        this.category = category;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }
}
\java\seedu\geekeep\commons\events\ui\TaskPanelSelectionChangedEvent.java
package seedu.geekeep.commons.events.ui;

import seedu.geekeep.commons.events.BaseEvent;
import seedu.geekeep.model.task.ReadOnlyTask;

/**
 * Represents a selection change in the Task List Panel
 */
public class TaskPanelSelectionChangedEvent extends BaseEvent {


    private final ReadOnlyTask newSelection;

    public TaskPanelSelectionChangedEvent(ReadOnlyTask newSelection) {
        this.newSelection = newSelection;
    }

    @Override
    public String toString() {
        return this.getClass().getSimpleName();
    }

    public ReadOnlyTask getNewSelection() {
        return newSelection;
    }
}
\java\seedu\geekeep\logic\commands\FindCommand.java
package seedu.geekeep.logic.commands;

import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import seedu.geekeep.commons.exceptions.IllegalValueException;
import seedu.geekeep.model.tag.Tag;
import seedu.geekeep.model.tag.UniqueTagList;
import seedu.geekeep.model.task.DateTime;

/**
 * Finds and lists all tasks in GeeKeep whose title contains any of the argument keywords. Keyword matching is case
 * sensitive.
 */
public class FindCommand extends Command {

    public static final String COMMAND_WORD = "find";

    public static final String MESSAGE_USAGE = COMMAND_WORD + ": Finds all tasks which titles contain any of "
            + "the specified keywords (case-sensitive)\n"
            + "and/or which date is within the specified time duration "
            + "and/or which tag list contains any of the specified tags.\n "
            + "Parameters: [KEYWORD...] [a/AFTER_DATETIME] [b/BEFORE_DATETIME] [t/TAGS...]\n"
            + "Example: " + COMMAND_WORD + " CS3230 Midterm a/04-04-17 0000 t/exam";
    public static final String MESSAGE_TIME_CONSTRAINTS = "The before date and time must be later than "
            + "the after date and time.";

    public static final String MESSAGE_SUCCESS = "\nGeeKeep is showing all the tasks which:\n";

    private final Set<String> keywords;
    private final UniqueTagList tags;
    private final DateTime earliestTime;
    private final DateTime latestTime;

    //Message with all the filter information added.
    private String datailedSuccessMsg = MESSAGE_SUCCESS;

    public FindCommand(Set<String> keywords, Optional<String> earliestTime,
            Optional<String> latestTime,
            Set<String> tags) throws IllegalValueException {
        this.keywords = keywords;

        final Set<Tag> tagSet = new HashSet<>();
        for (String tagName : tags) {
            tagSet.add(new Tag(tagName));
        }
        this.tags = new UniqueTagList(tagSet);

        if (earliestTime.isPresent()) {
            this.earliestTime = new DateTime(earliestTime.get());
        } else {
            this.earliestTime = DateTime.getMin();
        }

        if (latestTime.isPresent()) {
            this.latestTime = new DateTime(latestTime.get());
        } else {
            this.latestTime = DateTime.getMax();
        }

        if (this.earliestTime.compare(this.latestTime) > 0) {
            throw new IllegalValueException(MESSAGE_TIME_CONSTRAINTS);
        }

        addFilterMessage(keywords, earliestTime, latestTime, tags);
    }

    private void addFilterMessage(Set<String> keywords, Optional<String> earliestTime,
            Optional<String> latestTime,
            Set<String> tags) {
        if (!keywords.isEmpty()) {
            datailedSuccessMsg  += "Contains the keyword(s) " + keywords + " in title\n";
        }

        if (earliestTime.isPresent()) {
            datailedSuccessMsg += "Happens after "
                              + earliestTime.get() + "\n";
        }

        if (latestTime.isPresent()) {
            datailedSuccessMsg += "Happens before "
                              + latestTime.get() + "\n";
        }

        if (!tags.isEmpty()) {
            datailedSuccessMsg += "Contains the tags " + tags + "\n";
        }
    }

    public String getDetailedSuccessMsg() {
        return this.datailedSuccessMsg;
    }

    @Override
    public CommandResult execute() {
        model.updateFilteredTaskList(keywords, earliestTime, latestTime, tags);
        return new CommandResult(getMessageForTaskListShownSummary(model.getFilteredTaskList().size())
                                 + getDetailedSuccessMsg());
    }
}
\java\seedu\geekeep\logic\parser\CliSyntax.java
    public static final Prefix PREFIX_BEFORE_DATETIME = new Prefix("b/");
    public static final Prefix PREFIX_AFTER_DATETIME = new Prefix("a/");
\java\seedu\geekeep\logic\parser\FindCommandParser.java
package seedu.geekeep.logic.parser;

import static seedu.geekeep.commons.core.Messages.MESSAGE_INVALID_COMMAND_FORMAT;
import static seedu.geekeep.logic.parser.CliSyntax.PREFIX_AFTER_DATETIME;
import static seedu.geekeep.logic.parser.CliSyntax.PREFIX_BEFORE_DATETIME;
import static seedu.geekeep.logic.parser.CliSyntax.PREFIX_TAG;

import java.util.Arrays;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;

import seedu.geekeep.commons.exceptions.IllegalValueException;
import seedu.geekeep.logic.commands.Command;
import seedu.geekeep.logic.commands.FindCommand;
import seedu.geekeep.logic.commands.IncorrectCommand;

/**
 * Parses input arguments and creates a new FindCommand object
 */
public class FindCommandParser {

    /**
     * Parses the given {@code String} of arguments in the context of the FindCommand
     * and returns an FindCommand object for execution.
     */
    public Command parse(String args) {
        ArgumentTokenizer argsTokenizer =
                    new ArgumentTokenizer(PREFIX_BEFORE_DATETIME, PREFIX_AFTER_DATETIME, PREFIX_TAG);
        argsTokenizer.tokenize(args);
        Optional<String> keywordInTitle = argsTokenizer.getPreamble();
        Optional<String> earliestTime = argsTokenizer.getValue(PREFIX_AFTER_DATETIME);
        Optional<String> latestTime = argsTokenizer.getValue(PREFIX_BEFORE_DATETIME);
        Set<String> tags = ParserUtil.toSet(argsTokenizer.getAllValues(PREFIX_TAG));

        boolean isAnyParameterExisting = keywordInTitle.isPresent() || earliestTime.isPresent()
                || latestTime.isPresent() || !tags.isEmpty();
        if (!isAnyParameterExisting) {
            return new IncorrectCommand(
                    String.format(MESSAGE_INVALID_COMMAND_FORMAT, FindCommand.MESSAGE_USAGE));
        }

        String[] keywords = null;
        Set<String> keywordSet = new HashSet<>();
        if (keywordInTitle.isPresent()) {
            keywords = keywordInTitle.get().split("\\s+"); // keywords delimited by whitespace
            keywordSet = new HashSet<>(Arrays.asList(keywords));
        }

        try {
            return new FindCommand(keywordSet, earliestTime, latestTime, tags);
        } catch (IllegalValueException ive) {
            return new IncorrectCommand(ive.getMessage());
        }
    }

}
\java\seedu\geekeep\logic\parser\ListCommandParser.java
package seedu.geekeep.logic.parser;

import seedu.geekeep.logic.commands.Command;
import seedu.geekeep.logic.commands.IncorrectCommand;
import seedu.geekeep.logic.commands.ListCommand;

public class ListCommandParser {
    public Command parse(String args) {
        if (!args.isEmpty()) {
            return new IncorrectCommand(ListCommand.MESSAGE_USAGE);
        } else {
            return new ListCommand();
        }
    }
}
\java\seedu\geekeep\model\ModelManager.java
    @Override
    /**
     * Filters the task list by keywords, time and tags.
     * @param keywords, if it is empty, then every task is satisfactory. Otherwise, tasks which
     * don't match any of the keywords will be filtered out.
     * @param earlistTime, the time after which a task should happen.
     * @param latestTime, the time before which a task should happen.
     * @param tags, if it is empty, then every task is satisfactory. Otherwise, tasks which don't
     * contain any of the tags will be filtered out.
     */
    public void updateFilteredTaskList(Set<String> keywords, DateTime earlistTime,
            DateTime latestTime, UniqueTagList tags) {
        updateFilteredTaskList(new PredicateExpression(new TitleQualifier(keywords)),
                               new PredicateExpression(new TimeQualifier(earlistTime, latestTime)),
                               new PredicateExpression(new TagQualifier(tags)));
        raise(new SwitchTaskCategoryEvent(TaskCategory.ALL));
    }
    private void updateFilteredTaskList(Expression... expressions) {
        filteredTasks.setPredicate(task -> {
            boolean isSatisfactory = true;
            for (Expression expression : expressions) {
                isSatisfactory = isSatisfactory && expression.satisfies(task);
            }
            return isSatisfactory;
        });
    }
\java\seedu\geekeep\model\ModelManager.java
    private class TitleQualifier implements Qualifier {
        private Set<String> titleKeyWords;

        TitleQualifier(Set<String> nameKeyWords) {
            this.titleKeyWords = nameKeyWords;
        }

        @Override
        public boolean run(ReadOnlyTask task) {
            if (titleKeyWords.isEmpty()) {
                //every task satisfies, because the qualifier is empty
                return true;
            } else {
                return titleKeyWords.stream()
                        .filter(keyword-> StringUtil.containsWordIgnoreCase(task.getTitle().title, keyword))
                        .findAny().isPresent();
            }
        }
    }

    private class TimeQualifier implements Qualifier {
        private DateTime earliestTime;
        private DateTime latestTime;

        private TimeQualifier(DateTime earliestTime, DateTime latestTime) {
            this.earliestTime = earliestTime;
            this.latestTime = latestTime;
        }

        @Override
        public boolean run(ReadOnlyTask task) {
            return before(task) && after(task);
        }

        private boolean before(ReadOnlyTask task) {
            if (task.isFloatingTask()) { // assuming floating tasks meet all the time requirements.
                return true;
            } else {
                return task.getReferenceDateTime().compare(latestTime) <= 0;
            }
        }

        private boolean after(ReadOnlyTask task) {
            if (task.isFloatingTask()) {
                return true;
            } else {
                return task.getReferenceDateTime().compare(earliestTime) >= 0;
            }
        }
    }

    private class TagQualifier implements Qualifier {
        private UniqueTagList tags;


        private TagQualifier(UniqueTagList tags) {
            this.tags = tags;
        }

        @Override
        public boolean run(ReadOnlyTask task) {
            if (tags.isEmpty()) {
                //every task satisfies, because the qualifier is empty
                return true;
            } else {
                return tags.toSet().stream()
                        .filter(tag -> task.getTags().contains(tag))
                        .findAny().isPresent();
            }
        }
    }
\java\seedu\geekeep\model\tag\UniqueTagList.java
    public boolean isEmpty() {
        return internalList.isEmpty();
    }
\java\seedu\geekeep\model\task\DateTime.java
    //with the constraint of DD-MM-YY, the max date time should be 2099-12-31 2359
    //and the min date time should be 2000-01-01 0000;
    public static final String MAX_TIME = "31-12-99 2359";
    public static final String MIN_TIME = "01-01-00 0000";
\java\seedu\geekeep\model\task\DateTime.java
    /**
     * Returns the min date time GeeKeep currently supports
     */
    public static DateTime getMin() {
        DateTime minDateTime = null;

        //It is guaranteed that there is no exception thrown
        try {
            minDateTime =  new DateTime(MIN_TIME);
        } catch (IllegalValueException e) {
        }
        return minDateTime;
    }

    /**
     * Returns the max date time GeeKeep currently supports
     */
    public static DateTime getMax() {
        DateTime maxDateTime = null;

      //It is guaranteed that there is no exception thrown
        try {
            maxDateTime =  new DateTime(MAX_TIME);
        } catch (IllegalValueException e) {
        }
        return maxDateTime;
    }
\java\seedu\geekeep\model\task\DateTime.java
    public int compare(DateTime otherTime) {
        return dateTime.compareTo(otherTime.dateTime);
    }
\java\seedu\geekeep\model\task\ReadOnlyTask.java
    public int getPriority();
\java\seedu\geekeep\model\task\ReadOnlyTask.java
    /**
     * Get the task's DateTime that is used to compare date time.
     * For events, the startDateTime is used for comparison.
     * For deadlines, the endDateTime is used for comparison.
     * @return DateTime object
     */
    DateTime getReferenceDateTime();

    /**
     * Compares this task's type priority with another.
     * @param otherTask
     * @return a comparator value, negative if less, positive if greater
     */
    public int comparePriority(ReadOnlyTask otherTask);

    /**
     * Compares this task's reference datetime with another in chronological order.
     * @param otherTask
     * @return a comparator value, negative if less, positive if greater
     */
    int compareDate(ReadOnlyTask otherTask);

    /**
     * Compares this task's type priority and reference datetime with another.
     * Compares this task's title with another in lexicographic order if both are floating tasks.
     * @param otherTask
     * @return a comparator value, negative if less, positive if greater
     */
    public int comparePriorityAndDatetimeAndTitle(ReadOnlyTask otherTask);

    /**
     * Compares this task's title with another in lexicographic order.
     * @param otherTask
     * @return a comparator value, negative if less, positive if greater
     */
    public int compareTitle(ReadOnlyTask otherTask);
\java\seedu\geekeep\model\task\Task.java
    @Override
    /**
     * Get the task's priority which determines the ordering of index
     * @return int value of Priority
     */
    public int getPriority() {
        if (isEvent()) {
            return EVENT_PRIORITY;
        } else if (isFloatingTask()) {
            return FLOATING_TASK_PRIORITY;
        } else {
            assert isDeadline();
            return DEADLINE_PRIORITY;
        }
    }
\java\seedu\geekeep\ui\CommandBox.java
    /**
     * Sets the command box style to indicate a failed command.
     */
    private void setStyleToIndicateCommandFailure() {
        ObservableList<String> styleClass = commandTextField.getStyleClass();
        if (!styleClass.contains(ERROR_STYLE_CLASS)) {
            commandTextField.getStyleClass().add(ERROR_STYLE_CLASS);
        }
    }
\java\seedu\geekeep\ui\DeadlineListPanel.java
package seedu.geekeep.ui;

import javafx.collections.ObservableList;
import javafx.scene.layout.AnchorPane;
import seedu.geekeep.commons.core.LogsCenter;
import seedu.geekeep.model.task.ReadOnlyTask;

public class DeadlineListPanel extends TaskListPanel {
    private static final String DEADLINE_FXML = "DeadlineListPanel.fxml";

    public DeadlineListPanel(AnchorPane taskListPlaceholder, ObservableList<ReadOnlyTask> filteredList) {
        super(DEADLINE_FXML, taskListPlaceholder, filteredList);
        this.type = "deadline";
        this.logger = LogsCenter.getLogger(DeadlineListPanel.class);
    }
}
\java\seedu\geekeep\ui\MainWindow.java
    // Independent Ui parts residing in this Ui container
    private FloatingTaskListPanel floatingTaskListPanel;
    private EventListPanel eventListPanel;
    private DeadlineListPanel deadlineListPanel;
    private Config config;

    @FXML
    private AnchorPane commandBoxPlaceholder;

    @FXML
    private MenuItem helpMenuItem;

    @FXML
    private AnchorPane floatingTaskListPanelPlaceholder;

    @FXML
    private AnchorPane eventListPanelPlaceholder;

    @FXML
    private AnchorPane deadlineListPanelPlaceholder;
\java\seedu\geekeep\ui\MainWindow.java
    void fillInnerParts() {
        eventListPanel = new EventListPanel(getEventListPlaceholder(),
                logic.getFilteredEventList());

        floatingTaskListPanel = new FloatingTaskListPanel(getFloatingTaskListPlaceholder(),
                logic.getFilteredFloatingTaskList());

        deadlineListPanel = new DeadlineListPanel(getDeadlineListPlaceholder(),
                logic.getFilteredDeadlineList());

        new ResultDisplay(getResultDisplayPlaceholder());

        new StatusBarFooter(getStatusbarPlaceholder(), config.getGeekeepFilePath());

        new CommandBox(getCommandBoxPlaceholder(), logic);
    }

    private AnchorPane getCommandBoxPlaceholder() {
        return commandBoxPlaceholder;
    }

    private AnchorPane getStatusbarPlaceholder() {
        return statusbarPlaceholder;
    }

    private AnchorPane getResultDisplayPlaceholder() {
        return resultDisplayPlaceholder;
    }

    private AnchorPane getFloatingTaskListPlaceholder() {
        return floatingTaskListPanelPlaceholder;
    }

    private AnchorPane getEventListPlaceholder() {
        return eventListPanelPlaceholder;
    }

    private AnchorPane getDeadlineListPlaceholder() {
        return deadlineListPanelPlaceholder;
    }
\java\seedu\geekeep\ui\TaskListPanel.java
package seedu.geekeep.ui;

import java.util.logging.Logger;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.fxml.FXML;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TabPane;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Region;
import seedu.geekeep.commons.core.TaskCategory;
import seedu.geekeep.commons.events.ui.TaskPanelSelectionChangedEvent;
import seedu.geekeep.commons.util.FxViewUtil;
import seedu.geekeep.model.task.ReadOnlyTask;

/**
 * Panel containing the list of tasks.
 */
public class TaskListPanel extends UiPart<Region> {
    //Corresponding tab indices in the TabPane for different tabs.
    private static final int ALL_TAB = 0;
    private static final int UPCOMING_TAB = 1;
    private static final int FINISHED_TAB = 2;

    private ListView<ReadOnlyTask> currentListView;

    protected String type;
    protected Logger logger = null;

    @FXML
    private TabPane tabPanePlaceHolder;

    @FXML
    private ListView<ReadOnlyTask> allListView;

    @FXML
    private ListView<ReadOnlyTask> upcomingListView;

    @FXML
    private ListView<ReadOnlyTask> finishedListView;

    public TaskListPanel(String fxml, AnchorPane taskListPlaceholder,
            ObservableList<ReadOnlyTask> filteredList) {
        super(fxml);
        currentListView = allListView;
        setConnections(filteredList, allListView);
        setConnections(filteredList, finishedListView);
        setConnections(filteredList, upcomingListView);
        addToPlaceholder(taskListPlaceholder);
        selectTab(ALL_TAB);
    }

    private void setConnections(ObservableList<ReadOnlyTask> taskList,
            ListView<ReadOnlyTask> taskListView) {
        taskListView.setItems(taskList);
        taskListView.setCellFactory(listView -> new TaskListViewCell());
        setEventHandlerForSelectionChangeEvent(taskListView);
    }

    private void addToPlaceholder(AnchorPane placeHolderPane) {
        SplitPane.setResizableWithParent(placeHolderPane, false);
        FxViewUtil.applyAnchorBoundaryParameters(getRoot(), 0.0, 0.0, 0.0, 0.0);
        placeHolderPane.getChildren().add(getRoot());
    }

    private void setEventHandlerForSelectionChangeEvent(ListView<ReadOnlyTask> taskListView) {
        taskListView.getSelectionModel().selectedItemProperty()
                .addListener((observable, oldValue, newValue) -> {
                    if (newValue != null) {
                        logger.fine("Selection in task list panel changed to : '" + newValue + "'");
                        raise(new TaskPanelSelectionChangedEvent(newValue));
                    }
                });
    }

    public void scrollTo(int index) {
        Platform.runLater(() -> {
            currentListView.scrollTo(index);
            currentListView.getSelectionModel().clearAndSelect(index);
        });
    }

    public void clearSelection() {
        currentListView.getSelectionModel().clearSelection();
    }

    public void switchListView(TaskCategory category) {
        switch (category) {
        case ALL:
            selectTab(ALL_TAB);
            currentListView = allListView;
            break;
        case FINISHED:
            selectTab(FINISHED_TAB);
            currentListView = finishedListView;
            break;
        default:
            assert category == TaskCategory.UPCOMING;
            selectTab(UPCOMING_TAB);
            currentListView = upcomingListView;
            break;
        }
        logger.info("Switched to " + category + " in " + type);
    }

    public void selectTab(int tab) {
        tabPanePlaceHolder.getTabs().get(tab).setDisable(false);
        tabPanePlaceHolder.getSelectionModel().select(tab);
        for (int i = 0; i < 3; i++) {
            if (i != tab) {
                tabPanePlaceHolder.getTabs().get(i).setDisable(true);
            }
        }
    }

    class TaskListViewCell extends ListCell<ReadOnlyTask> {

        protected int getSourceIndex() {
            FilteredList<ReadOnlyTask> filteredList = (FilteredList<ReadOnlyTask>) getListView().getItems();
            return filteredList.getSourceIndex(getIndex());
        }

        @Override
        protected void updateItem(ReadOnlyTask task, boolean empty) {
            super.updateItem(task, empty);

            if (empty || task == null) {
                setGraphic(null);
                setText(null);
            } else {
                setGraphic(new TaskCard(task, getSourceIndex() + 1).getRoot());
            }
        }
    }

}
\java\seedu\geekeep\ui\UiManager.java
    @Subscribe
    private void handleSwitchTaskCategoryEvent(SwitchTaskCategoryEvent event) {
        logger.info(LogsCenter.getEventHandlingLogMessage(event));
        mainWindow.getFloatingTaskListPanel().switchListView(event.category);
        mainWindow.getDeadlineListPanel().switchListView(event.category);
        mainWindow.getEventListPanel().switchListView(event.category);
    }

}
\resources\view\DeadlineListPanel.fxml
<?import com.jfoenix.controls.JFXTabPane?>
<?import java.net.URL?>
<?import javafx.scene.control.ListView?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>

<VBox xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
    <stylesheets>
        <URL value="@DarkTheme.css" />
        <URL value="@Extensions.css" />
    </stylesheets>
    <children>
      <JFXTabPane fx:id="tabPanePlaceHolder" styleClass="deadlineTab" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
        <tabs>
            <Tab text="All">
               <content>
                  <AnchorPane>
                     <children>
                        <ListView fx:id="allListView" layoutY="34.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="34.0" />
                        <Text fill="#f1f1f1" layoutX="4.0" layoutY="25.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Deadlines:">
                           <font>
                              <Font name="Segoe UI Semibold" size="19.0" />
                           </font>
                        </Text>
                     </children>
                  </AnchorPane>
               </content>
            </Tab>
          <Tab text="Upcoming">
               <content>
                  <AnchorPane>
                     <children>
                        <ListView fx:id="upcomingListView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="34.0" />
                        <Text fill="#f1f1f1" layoutX="4.0" layoutY="25.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Deadlines:">
                           <font>
                              <Font name="Segoe UI Semibold" size="19.0" />
                           </font>
                        </Text>
                     </children>
                  </AnchorPane>
               </content>
            </Tab>
          <Tab text="Finished">
               <content>
                  <AnchorPane>
                     <children>
                        <ListView fx:id="finishedListView" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="34.0" />
                        <Text fill="#f1f1f1" layoutX="4.0" layoutY="25.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Deadlines:">
                           <font>
                              <Font name="Segoe UI Semibold" size="19.0" />
                           </font>
                        </Text>
                     </children>
                  </AnchorPane>
               </content>
            </Tab>
        </tabs>
      </JFXTabPane>
    </children>
</VBox>
\resources\view\MainWindow.fxml
      <SplitPane id="splitPane" fx:id="splitPane" dividerPositions="0.1, 0.4, 0.7" VBox.vgrow="ALWAYS">
         <items>
            <VBox fx:id="eventList">
                <padding>
                    <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
                </padding>
               <children>
                  <AnchorPane fx:id="eventListPanelPlaceholder" VBox.vgrow="ALWAYS" />
               </children>
            </VBox>
            <VBox fx:id="ftaskList" layoutX="10.0" layoutY="10.0">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
               <children>
                  <AnchorPane fx:id="floatingTaskListPanelPlaceholder" VBox.vgrow="ALWAYS" />
               </children>
            </VBox>
            <VBox fx:id="deadlineList" layoutX="10.0" layoutY="10.0">
               <padding>
                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
               </padding>
               <children>
                  <AnchorPane fx:id="deadlineListPanelPlaceholder" VBox.vgrow="ALWAYS" />
               </children>
            </VBox>
         </items>
      </SplitPane>