Autopsy  3.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
TimeLineChart.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2014 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.timeline.ui;
20 
21 import javafx.event.EventHandler;
22 import javafx.geometry.Point2D;
23 import javafx.scene.Cursor;
24 import javafx.scene.chart.Axis;
25 import javafx.scene.chart.Chart;
26 import javafx.scene.control.Tooltip;
27 import javafx.scene.input.MouseButton;
28 import javafx.scene.input.MouseEvent;
29 import javafx.scene.paint.Color;
30 import javafx.scene.shape.Rectangle;
31 import org.joda.time.DateTime;
32 import org.joda.time.Interval;
33 import org.openide.util.NbBundle;
36 
40 public interface TimeLineChart<X> extends TimeLineView {
41 
43 
45 
54  IntervalSelector<X> newIntervalSelector(double x, Axis<X> axis);
55 
58  void clearIntervalSelector();
59 
67  class ChartDragHandler<X, Y extends Chart & TimeLineChart<X>> implements EventHandler<MouseEvent> {
68 
69  private final Y chart;
70 
71  private final Axis<X> dateAxis;
72 
73  private double startX; //hanlder mainstains position of drag start
74 
75  public ChartDragHandler(Y chart, Axis<X> dateAxis) {
76  this.chart = chart;
77  this.dateAxis = dateAxis;
78  }
79 
80  @Override
81  public void handle(MouseEvent t) {
82  if (t.getButton() == MouseButton.SECONDARY) {
83 
84  if (t.getEventType() == MouseEvent.MOUSE_PRESSED) {
85  //caputure x-position, incase we are repositioning existing selector
86  startX = t.getX();
87  chart.setCursor(Cursor.E_RESIZE);
88  } else if (t.getEventType() == MouseEvent.MOUSE_DRAGGED) {
89  if (chart.getIntervalSelector() == null) {
90  //make new interval selector
91  chart.setIntervalSelector(chart.newIntervalSelector(t.getX(), dateAxis));
92  chart.getIntervalSelector().heightProperty().bind(chart.heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty())));
93  chart.getIntervalSelector().addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
94  if (event.getButton() == MouseButton.SECONDARY) {
95  chart.clearIntervalSelector();
96  event.consume();
97  }
98  });
99  startX = t.getX();
100  } else {
101  //resize/position existing selector
102  if (t.getX() > startX) {
103  chart.getIntervalSelector().setX(startX);
104  chart.getIntervalSelector().setWidth(t.getX() - startX);
105  } else {
106  chart.getIntervalSelector().setX(t.getX());
107  chart.getIntervalSelector().setWidth(startX - t.getX());
108  }
109  }
110  } else if (t.getEventType() == MouseEvent.MOUSE_RELEASED) {
111  chart.setCursor(Cursor.DEFAULT);
112  }
113  t.consume();
114  }
115  }
116  }
117 
126  static abstract class IntervalSelector<X> extends Rectangle {
127 
128  private static final double STROKE_WIDTH = 3;
129 
130  private static final double HALF_STROKE = STROKE_WIDTH / 2;
131 
133  private final Axis<X> dateAxis;
134 
135  protected Tooltip tooltip;
136 
139 
140  private double startLeft;
141 
142  private double startX;
143 
144  private double startWidth;
146 
155  public IntervalSelector(double x, double height, Axis<X> axis, TimeLineController controller) {
156  super(x, 0, x, height);
157  dateAxis = axis;
158  setStroke(Color.BLUE);
159  setStrokeWidth(STROKE_WIDTH);
160  setFill(Color.BLUE.deriveColor(0, 1, 1, 0.5));
161  setOpacity(0.5);
162  widthProperty().addListener(o -> {
163  setTooltip();
164  });
165  xProperty().addListener(o -> {
166  setTooltip();
167  });
168  setTooltip();
169 
170  setOnMouseMoved((MouseEvent event) -> {
171  Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY()));
172  final double diffX = getX() - localMouse.getX();
173  if (Math.abs(diffX) <= HALF_STROKE || Math.abs(diffX + getWidth()) <= HALF_STROKE) {
174  setCursor(Cursor.E_RESIZE);
175  } else {
176  setCursor(Cursor.HAND);
177  }
178  });
179  setOnMousePressed((MouseEvent event) -> {
180  Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY()));
181  final double diffX = getX() - localMouse.getX();
182  startX = event.getX();
183  startWidth = getWidth();
184  startLeft = getX();
185  if (Math.abs(diffX) <= HALF_STROKE) {
186  dragPosition = IntervalSelector.DragPosition.LEFT;
187  } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) {
188  dragPosition = IntervalSelector.DragPosition.RIGHT;
189  } else {
190  dragPosition = IntervalSelector.DragPosition.CENTER;
191  }
192  });
193  setOnMouseDragged((MouseEvent event) -> {
194  double dX = event.getX() - startX;
195  switch (dragPosition) {
196  case CENTER:
197  setX(startLeft + dX);
198  break;
199  case LEFT:
200  setX(startLeft + dX);
201  setWidth(startWidth - dX);
202  break;
203  case RIGHT:
204  setWidth(startWidth + dX);
205  break;
206  }
207  });
208  //have to add handler rather than use convenience methods so that charts can listen for dismisal click
209  addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
210 
211  public void handle(MouseEvent event) {
212  if (event.getClickCount() >= 2) {
213  //convert to DateTimes, using max/min if null(off axis)
214  DateTime start = parseDateTime(getSpanStart());
215  DateTime end = parseDateTime(getSpanEnd());
216 
217  Interval i = adjustInterval(start.isBefore(end) ? new Interval(start, end) : new Interval(end, start));
218 
219  controller.pushTimeRange(i);
220  }
221  }
222  });
223  }
224 
232  protected abstract Interval adjustInterval(Interval i);
233 
240  protected abstract String formatSpan(final X date);
241 
247  protected abstract DateTime parseDateTime(X date);
248 
249  private void setTooltip() {
250  final X start = getSpanStart();
251  final X end = getSpanEnd();
252  Tooltip.uninstall(this, tooltip);
253  tooltip = new Tooltip(
254  NbBundle.getMessage(this.getClass(), "Timeline.ui.TimeLineChart.tooltip.text", formatSpan(start),
255  formatSpan(end)));
256  Tooltip.install(this, tooltip);
257  }
258 
261  public X getSpanEnd() {
262  return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMaxX(), 0).getX());
263  }
264 
267  public X getSpanStart() {
268  return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMinX(), 0).getX());
269  }
270 
273  private enum DragPosition {
274 
275  LEFT, CENTER, RIGHT
276  }
277  }
278 }
void setIntervalSelector(IntervalSelector<?extends X > newIntervalSelector)
IntervalSelector(double x, double height, Axis< X > axis, TimeLineController controller)
IntervalSelector< X > newIntervalSelector(double x, Axis< X > axis)
IntervalSelector<?extends X > getIntervalSelector()

Copyright © 2012-2015 Basis Technology. Generated on: Mon Oct 19 2015
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.