27 package org.sleuthkit.autopsy.timeline.ui.detailview;
 
   29 import java.util.ArrayList;
 
   30 import java.util.Collections;
 
   31 import java.util.List;
 
   32 import javafx.beans.property.ObjectProperty;
 
   33 import javafx.beans.property.ObjectPropertyBase;
 
   34 import javafx.beans.property.ReadOnlyDoubleProperty;
 
   35 import javafx.beans.property.ReadOnlyDoubleWrapper;
 
   36 import javafx.scene.chart.Axis;
 
   37 import org.joda.time.DateTime;
 
   38 import org.joda.time.Interval;
 
   52 final class DateAxis 
extends Axis<DateTime> {
 
   54     private ObjectProperty<DateTime> lowerBound = 
new ObjectPropertyBase<DateTime>() {
 
   56         protected void invalidated() {
 
   57             if (!isAutoRanging()) {
 
   64         public Object getBean() {
 
   69         public String getName() {
 
   79     private DateTime maxDate;
 
   86     private DateTime minDate;
 
   88     private RangeDivisionInfo rangeDivisionInfo;
 
   90     private final ReadOnlyDoubleWrapper tickSpacing = 
new ReadOnlyDoubleWrapper();
 
   92     private final ObjectProperty<DateTime> upperBound = 
new ObjectPropertyBase<DateTime>() {
 
   94         protected void invalidated() {
 
   95             if (!isAutoRanging()) {
 
  102         public Object getBean() {
 
  103             return DateAxis.this;
 
  107         public String getName() {
 
  115         setAutoRanging(
false);
 
  119     public double getDisplayPosition(DateTime date) {
 
  120         final double length = -200 + (getSide().isHorizontal() ? getWidth() : getHeight());
 
  123         double diff = getUpperBound().getMillis() - getLowerBound().getMillis();
 
  127         double range = length - getZeroPosition();
 
  131         double d = (date.getMillis() - getLowerBound().getMillis()) / diff;
 
  134         if (getSide().isVertical()) {
 
  135             return getHeight() - d * range + getZeroPosition();
 
  137             return d * range + getZeroPosition();
 
  148     public final DateTime getLowerBound() {
 
  149         return lowerBound.get();
 
  159     public final void setLowerBound(DateTime date) {
 
  160         lowerBound.set(date);
 
  170     public final DateTime getUpperBound() {
 
  171         return upperBound.get();
 
  181     public final void setUpperBound(DateTime date) {
 
  182         upperBound.set(date);
 
  186     public DateTime getValueForDisplay(
double displayPosition) {
 
  187         final double length = - 200 + (getSide().isHorizontal() ? getWidth() : getHeight());
 
  190         double diff = getUpperBound().getMillis() - getLowerBound().getMillis();
 
  194         double range = length - getZeroPosition();
 
  196         if (getSide().isVertical()) {
 
  199             return new DateTime((
long) ((displayPosition - getZeroPosition() - getHeight()) / -range * diff + getLowerBound().getMillis()), TimeLineController.getJodaTimeZone());
 
  203             return new DateTime((
long) ((displayPosition - getZeroPosition()) / range * diff + getLowerBound().getMillis()), TimeLineController.getJodaTimeZone());
 
  208     public double getZeroPosition() {
 
  213     public void invalidateRange(List<DateTime> list) {
 
  214         super.invalidateRange(list);
 
  216         Collections.sort(list);
 
  217         if (list.isEmpty()) {
 
  218             minDate = maxDate = 
new DateTime();
 
  219         } 
else if (list.size() == 1) {
 
  220             minDate = maxDate = list.get(0);
 
  221         } 
else if (list.size() > 1) {
 
  222             minDate = list.get(0);
 
  223             maxDate = list.get(list.size() - 1);
 
  228     public boolean isValueOnAxis(DateTime date) {
 
  229         return date.getMillis() > getLowerBound().getMillis() && date.getMillis() < getUpperBound().getMillis();
 
  233     public double toNumericValue(DateTime date) {
 
  234         return date.getMillis();
 
  238     public DateTime toRealValue(
double v) {
 
  239         return new DateTime((
long) v);
 
  243     protected Object autoRange(
double length) {
 
  244         if (isAutoRanging()) {
 
  245             return new Interval(minDate, maxDate);
 
  247             if (getLowerBound() == null || getUpperBound() == null) {
 
  255     protected List<DateTime> calculateTickValues(
double length, Object range) {
 
  256         List<DateTime> tickDates = 
new ArrayList<>();
 
  260         rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo((Interval) range);
 
  261         final DateTime lowerBound1 = getLowerBound();
 
  262         final DateTime upperBound1 = getUpperBound();
 
  264         if (lowerBound1 == null || upperBound1 == null) {
 
  267         DateTime lower = lowerBound1.withZone(TimeLineController.getJodaTimeZone());
 
  268         DateTime upper = upperBound1.withZone(TimeLineController.getJodaTimeZone());
 
  270         DateTime current = lower;
 
  272         while (current.isBefore(upper)) {
 
  273             tickDates.add(current);
 
  274             current = current.plus(rangeDivisionInfo.getPeriodSize().getPeriod());
 
  278         tickDates.add(upper);
 
  284         if (tickDates.size() > 2) {
 
  285             DateTime secondDate = tickDates.get(1);
 
  286             DateTime thirdDate = tickDates.get(2);
 
  287             DateTime lastDate = tickDates.get(tickDates.size() - 2);
 
  288             DateTime previousLastDate = tickDates.get(tickDates.size() - 3);
 
  291             if (secondDate.getMillis() - lower.getMillis() < (thirdDate.getMillis() - secondDate.getMillis()) / 2) {
 
  292                 tickDates.remove(lower);
 
  297             if (upper.getMillis() - lastDate.getMillis() < (lastDate.getMillis() - previousLastDate.getMillis()) / 2) {
 
  298                 tickDates.remove(lastDate);
 
  302         if (tickDates.size() >= 2) {
 
  303             tickSpacing.set(getDisplayPosition(tickDates.get(1)) - getDisplayPosition(tickDates.get(0)));
 
  304         } 
else if (tickDates.size() >= 4) {
 
  305             tickSpacing.set(getDisplayPosition(tickDates.get(2)) - getDisplayPosition(tickDates.get(1)));
 
  311     protected Object getRange() {
 
  313         return new Interval(getLowerBound(), getUpperBound());
 
  317     protected String getTickMarkLabel(DateTime date) {
 
  318         return rangeDivisionInfo.getTickFormatter().print(date);
 
  322     protected void layoutChildren() {
 
  323         super.layoutChildren();
 
  327     protected void setRange(Object range, 
boolean animating) {
 
  329         rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo((Interval) range);
 
  331         setLowerBound(
new DateTime(rangeDivisionInfo.getLowerBound()));
 
  332         setUpperBound(
new DateTime(rangeDivisionInfo.getUpperBound()));
 
  335     ReadOnlyDoubleProperty getTickSpacing() {
 
  336         return tickSpacing.getReadOnlyProperty();