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).

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());
The dateline model provides the dateline control with various pieces of information so that it can lay itself out correctly.
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");
}