NOW LOADING

Dateline

Overview

The dateline is a control that displays the actual dates (Mo, Tu, We, …) in cells that are placed on one or more rows. The dateline is timezone-aware, keeps track of currently selected time intervals and the current hover time interval. It also fires events whenever the visible time range changes (e.g., after scrolling left or right).

Dateline

Scale Resolutions

The dateline can display one to five rows. Each row is called a “dateline scale” and each one of these scales displays a “resolution.” A resolution consists of a temporal unit (e.g., day, week, month), a pattern for formatting, and a quantity. The quantity is needed to specify resolutions like “5 minutes,” “15 minutes,” and so on. The entire list of resolutions that are currently shown by the dateline can be retrieved by calling getScaleResolutions().

One example for a use of this method is given by the system layer GridLinesLayer. It calls this method to use the resolutions to calculate the locations of the vertical grid lines. For this the Resolution class offers the methods truncate() to go to the beginning of a unit (e.g., the beginning of a day) and increment() to go to the next unit (e.g., the next day). For more information on Resolution please go to the dateline model documentation.

Primary Temporal Unit

A dateline with three scales could, for example, display the resolutions “month,” “week,” and “day.” The smallest resolution “day” gets displayed at the bottom of the dateline. The temporal unit ChronoUnit.DAYS that is used by this resolution is also called “primary temporal unit.” The current value of this unit is stored in the read-only property primaryTemporalUnit. The value of this property is used when querying activities from activity repositories. This way the repository can decide how fine-grained the result of its invocation will be or if certain activities will not be shown at all.

One example for a good use of the primary temporal unit is the WeekendCalendar class. It implements Calendar, which is an extension of ActivityRepository. The purpose of the WeekendCalendar is to return the weekend days (Saturday, Sunday) for a given time interval. When it gets invoked, it will not return anything if the primary temporal unit is too large or too small. It makes no sense to return weekend information if the user is currently looking at minutes or decades.

Timezone

The dateline needs to know for which timezone it is displaying the dates (e.g., EST or GMT). Hence, it features the property zoneIdProperty(). It is writable and can be set via setZoneId(). The value of this property can be made visible in the control by calling setZoneId(true).

Selection Model

The dateline control allows the user to perform single or multiple selections of time intervals by clicking the primary mouse button while pressing the shortcut modifier key (CTRL on Windows / Linux, Option on Mac). Whether single or multiple selection is supported depends on the value of selectionModeProperty().

Only those intervals can be selected that are currently visible in any one of the rows / scales. So if the dateline is currently showing weeks and days, then the user can only select entire weeks or entire days. This list of selected intervals can be retrieved by calling getSelectedTimeIntervals().

Hover Time Interval

When the mouse cursor hovers over the dateline, it also implies that it is hovering over a time interval. Depending on the resolution shown in the dateline row / scale at the given mouse location, the interval might be an entire week or a single day. Whatever it is, the interval will be stored in the read-only property hoverTimeIntervalProperty().

Events

Applications can listen to scrolling events fired by the dateline when they need to react to any changes in the currently visible time range. This is done by passing an event listener to the setOnVisibleRangeChanged() method or by calling addEventListener(DatelineScrollingEvent.ANY, myListener).

Cell Factory

The dateline control is capable of displaying different types of temporal units. ChronoUnit (Mon, Tue, Wed, …) and SimpleUnit (1, 2, 3, 4, …) are supported by default. Each unit type has its own visual representation. To accommodate for this, the dateline control delegates the creation of dateline cells to a pluggable factory that was previously mapped to a specific temporal unit type.

setCellFactory(SimpleUnit.class, unit -> new SimpleUnitDatelineCell());
setCellFactory(ChronoUnit.class, unit -> new ChronoUnitDatelineCell());

Dateline Model

The dateline model provides the dateline control with various pieces of information so that it can lay itself out correctly.

  • Resolutions – a resolution defines which temporal unit to show (e.g., hours) and how to format it. It also contains the information whether it can be shown in a top, bottom, or middle scale. Each model usually defines a long list of such resolutions. The more resolutions are defined, the more flexible the dateline control becomes when it comes to zooming in and out.
  • Time Zones – the dateline control allows the user to switch between different time zones. The model defines which zones are available.
  • Scale Count – the dateline control is composed of a set of dateline scales (top, bottom, several middle scales). The model can be used to define the currently visible, the minimum, and the maximum number of scales that the user can choose to see.
  • Temporal Units – the dateline control calls back onto the model to look up the “next” temporal unit after it has either failed or succeeded to create a scale for the current unit.

The dateline model is a typed model. FlexGanttFX ships with two specializations: ChronoUnitDatelineModel and SimpleUnitDatelineModel.

Chrono Unit Dateline Model

The ChronoUnitDatelineModel class is a specialization for the ChronoUnit temporal that is part of JDK 8. It requires scale resolutions of type ChronoUnitResolution. The following listing is the implementation of this model and illustrates how to define and add resolutions and also how the resolution is used to go from one temporal unit to the next.

package com.flexganttfx.model.dateline;
 
import java.time.temporal.ChronoUnit;
 
import static com.flexganttfx.model.dateline.Resolution.Position.*;
import static java.time.temporal.ChronoUnit.*;
 
/**
 * The chrono unit dateline model is a specialization of the dateline model that works
 * in combination with the {@link ChronoUnit}. The chrono unit basically represents standard
 * calendar units ranging from milliseconds to thousands of years.
 *
 * @since 1.0
 */
public final class ChronoUnitDatelineModel extends DatelineModel<ChronoUnit> {
 
    /**
     * Constructs a new dateline model with a long list of predefined
     * resolutions of type {@link ChronoUnitResolution}.
     *
     * @since 1.0
     */
    public ChronoUnitDatelineModel() {
        addResolution(new ChronoUnitResolution(MILLIS, "EEEE, dd. MMMM yyyy, HH:mm:ss:SSS", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MILLIS, "EEEE, dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MILLIS, "E, dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MILLIS, "dd.MM.yy, HH:mm:ss:SSS", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MILLIS, "dd.MM, HH:mm:ss:SSS", 1, TOP));
        addResolution(new ChronoUnitResolution(MILLIS, "SSS", 1, BOTTOM));
        addResolution(new ChronoUnitResolution(MILLIS, "SSS", 5, BOTTOM));
        addResolution(new ChronoUnitResolution(MILLIS, "SSS", 10, BOTTOM));
        addResolution(new ChronoUnitResolution(MILLIS, "SSS", 15, BOTTOM));
 
        addResolution(new ChronoUnitResolution(SECONDS, "EEEE, dd. MMMM yyyy, HH:mm:ss", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(SECONDS, "EEEE, dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(SECONDS, "E, dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(SECONDS, "dd.MM.yy, HH:mm:ss", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(SECONDS, "dd.MM, HH:mm:ss", 1, MIDDLE));
        addResolution(new ChronoUnitResolution(SECONDS, "HH:mm:ss", 1, MIDDLE));
        addResolution(new ChronoUnitResolution(SECONDS, "ss", 1, BOTTOM));
        addResolution(new ChronoUnitResolution(SECONDS, "ss", 5, BOTTOM));
        addResolution(new ChronoUnitResolution(SECONDS, "ss", 10, BOTTOM));
        addResolution(new ChronoUnitResolution(SECONDS, "ss", 15, BOTTOM));
 
        addResolution(new ChronoUnitResolution(MINUTES, "EEEE, dd. MMMM yyyy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MINUTES, "EEEE, dd.MM.yy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MINUTES, "E, dd.MM.yy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MINUTES, "dd.MM.yy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MINUTES, "dd.MM, HH:mm", 1, TOP));
        addResolution(new ChronoUnitResolution(MINUTES, "HH:mm", 1, MIDDLE));
        addResolution(new ChronoUnitResolution(MINUTES, "mm", 1, BOTTOM));
        addResolution(new ChronoUnitResolution(MINUTES, "mm", 5, BOTTOM));
        addResolution(new ChronoUnitResolution(MINUTES, "mm", 10, BOTTOM));
        addResolution(new ChronoUnitResolution(MINUTES, "mm", 15, BOTTOM));
 
        addResolution(new ChronoUnitResolution(HOURS, "EEEE, dd. MMMM yyyy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(HOURS, "EEEE, dd.MM.yy, HH:mm", 1, TOP, BOTTOM, ONLY));
        addResolution(new ChronoUnitResolution(HOURS, "E, dd.MM.yy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(HOURS, "dd.MM.yy, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(HOURS, "dd.MM, HH:mm", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(HOURS, "H:mm", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(HOURS, "H:mm", 3, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(HOURS, "H:mm", 6, MIDDLE, BOTTOM));
 
        addResolution(new ChronoUnitResolution(DAYS, "EEEE d. MMMM yyyy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(DAYS, "EEEE d. MMMM yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(DAYS, "E, d. MMMM yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(DAYS, "E, d. MMMM", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(DAYS, "E, dd.MM.yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(DAYS, "EEEE dd", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(DAYS, "E dd", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(DAYS, "dd.MM", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(DAYS, "dd", 1, BOTTOM));
 
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, EEEE d. MMMM yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, d. MMMM yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, d. MMMM", 1));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, E, dd.MM.yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, dd.MM.yy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w, dd.MM", 1, BOTTOM));
        addResolution(new ChronoUnitResolution(WEEKS, "'W' w", 1, MIDDLE, BOTTOM));
 
        addResolution(new ChronoUnitResolution(MONTHS, "MMMM yyyy", 1, TOP, ONLY));
        addResolution(new ChronoUnitResolution(MONTHS, "MMMM", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(MONTHS, "MMM", 1, MIDDLE, BOTTOM));
        addResolution(new ChronoUnitResolution(MONTHS, "M", 1, MIDDLE, BOTTOM));
 
        addResolution(new ChronoUnitResolution(YEARS, "yyyy", 1));
        addResolution(new ChronoUnitResolution(DECADES, "yyyy", 1));
        addResolution(new ChronoUnitResolution(CENTURIES, "yyyy", 1));
        addResolution(new ChronoUnitResolution(MILLENNIA, "yyyy", 1));
    }
 
    @Override
    public final ChronoUnit nextTemporalUnit(ChronoUnit unit) {
        switch (unit) {
            case NANOS:
                return MICROS;
            case MICROS:
                return MILLIS;
            case MILLIS:
                return SECONDS;
            case SECONDS:
                return MINUTES;
            case MINUTES:
                return HOURS;
            case HOURS:
                return DAYS;
            case DAYS:
                return WEEKS;
            case WEEKS:
                return MONTHS;
            case MONTHS:
                return YEARS;
            case YEARS:
                return DECADES;
            case DECADES:
                return CENTURIES;
            case CENTURIES:
                return MILLENNIA;
            default:
            /*
             * We are ignoring HALF DAYS.
             */
                return null;
        }
    }
}

Simple Unit Dateline Model

The SimpleUnitDatelineModel class is a specialization for the SimpleUnit temporal which ships with FlexGanttFX. It requires scale resolutions of type SimpleUnitResolution. The implementation of this model class below shows why the unit is called “simple.”

package com.flexganttfx.model.dateline;
 
 
import com.flexganttfx.model.util.SimpleUnit;
 
public final class SimpleUnitDatelineModel extends DatelineModel<SimpleUnit> {
 
    public SimpleUnitDatelineModel() {
        for (SimpleUnit unit : SimpleUnit.values()) {
            addResolution(new SimpleUnitResolution(unit, "", 1));
        }
    }
 
    @Override
    public SimpleUnit nextTemporalUnit(SimpleUnit unit) {
        int ordinal = unit.ordinal();
        if (ordinal < SimpleUnit.values().length - 1) {
            return SimpleUnit.values()[ordinal + 1];
        }
        return null;
    }
}

Timezones

The dateline model manages a list of zone IDs, which is used by the UI to create menu items for each ID. This way the user can easily toggle between them. The default list is set up in the DatelineModel class like this:

/**
 * Constructs a new model and populates the list of available zone IDs.
 */
protected DatelineModel() {
    addZoneId("Europe/Berlin");
    addZoneId("America/New_York");
    addZoneId("Australia/Darwin");
    addZoneId("Australia/Sydney");
    addZoneId("America/Argentina/Buenos_Aires");
    addZoneId("Africa/Cairo");
    addZoneId("America/Anchorage");
    addZoneId("America/Sao_Paulo");
    addZoneId("Asia/Dhaka");
    addZoneId("Africa/Harare");
    addZoneId("America/St_Johns");
    addZoneId("America/Chicago");
    addZoneId("Asia/Shanghai");
    addZoneId("Africa/Addis_Ababa")
    addZoneId("Europe/Paris");
    addZoneId("America/Indiana/Indianapolis");
    addZoneId("Asia/Kolkata");
    addZoneId("Asia/Tokyo");
    addZoneId("Pacific/Apia");
    addZoneId("Asia/Yerevan");
    addZoneId("Pacific/Auckland");
    addZoneId("Asia/Karachi");
    addZoneId("America/Phoenix");
    addZoneId("America/Puerto_Rico");
    addZoneId("America/Los_Angeles");
    addZoneId("Pacific/Guadalcanal");
    addZoneId("Asia/Ho_Chi_Minh");
}
Table of Content