19 package org.sleuthkit.autopsy.timeline.ui;
21 import javafx.beans.binding.BooleanBinding;
22 import javafx.beans.property.BooleanProperty;
23 import javafx.beans.property.SimpleBooleanProperty;
24 import javafx.event.ActionEvent;
25 import javafx.fxml.FXML;
26 import javafx.geometry.Point2D;
27 import javafx.geometry.Pos;
28 import javafx.scene.Cursor;
29 import javafx.scene.control.Button;
30 import javafx.scene.control.Label;
31 import javafx.scene.control.Tooltip;
32 import javafx.scene.image.Image;
33 import javafx.scene.image.ImageView;
34 import javafx.scene.input.MouseButton;
35 import javafx.scene.input.MouseEvent;
36 import javafx.scene.layout.BorderPane;
37 import org.controlsfx.control.action.Action;
38 import org.controlsfx.control.action.ActionUtils;
39 import org.joda.time.DateTime;
40 import org.joda.time.Interval;
41 import org.openide.util.NbBundle;
56 private static final Image
ClEAR_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/cross-script.png", 16, 16,
true,
true,
true);
57 private static final Image
ZOOM_TO_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/magnifier-zoom-fit.png", 16, 16,
true,
true,
true);
73 private final BooleanProperty
isDragging =
new SimpleBooleanProperty(
false);
100 assert startLabel != null :
"fx:id=\"startLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
101 assert endLabel != null :
"fx:id=\"endLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
102 assert closeButton != null :
"fx:id=\"closeButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
103 assert zoomButton != null :
"fx:id=\"zoomButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
105 setMaxHeight(USE_PREF_SIZE);
106 setMinHeight(USE_PREF_SIZE);
107 setMaxWidth(USE_PREF_SIZE);
108 setMinWidth(USE_PREF_SIZE);
110 BooleanBinding showingControls = hoverProperty().and(isDragging.not());
111 closeButton.visibleProperty().bind(showingControls);
112 closeButton.managedProperty().bind(showingControls);
113 zoomButton.visibleProperty().bind(showingControls);
114 zoomButton.managedProperty().bind(showingControls);
116 widthProperty().addListener(o -> {
118 if (startLabel.getWidth() + zoomButton.getWidth() + endLabel.getWidth() > getWidth()) {
119 this.setCenter(zoomButton);
121 bottomBorder.setCenter(zoomButton);
123 BorderPane.setAlignment(zoomButton, Pos.BOTTOM_CENTER);
128 setOnMouseMoved(mouseMove -> {
130 final double diffX = getLayoutX() - parentMouse.getX();
132 setCursor(Cursor.W_RESIZE);
133 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
134 setCursor(Cursor.E_RESIZE);
136 setCursor(Cursor.HAND);
141 setOnMousePressed(mousePress -> {
143 final double diffX = getLayoutX() - parentMouse.getX();
144 startDragX = mousePress.getScreenX();
145 startWidth = getWidth();
146 startLeft = getLayoutX();
149 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
154 mousePress.consume();
157 setOnMouseReleased(mouseRelease -> isDragging.set(
false));
158 setOnMouseDragged(mouseDrag -> {
159 isDragging.set(
true);
160 double dX = mouseDrag.getScreenX() -
startDragX;
161 switch (dragPosition) {
163 setLayoutX(startLeft + dX);
166 if (dX > startWidth) {
167 startDragX = mouseDrag.getScreenX();
169 dragPosition = DragPosition.RIGHT;
171 setLayoutX(startLeft + dX);
172 setPrefWidth(startWidth - dX);
179 dragPosition = DragPosition.LEFT;
180 startDragX = mouseDrag.getScreenX();
183 setPrefWidth(startWidth + dX);
191 ActionUtils.configureButton(
new ZoomToSelectedIntervalAction(), zoomButton);
192 ActionUtils.configureButton(
new ClearSelectedIntervalAction(), closeButton);
195 setOnMouseClicked(mosueClick -> {
196 if (mosueClick.getButton() == MouseButton.SECONDARY) {
198 mosueClick.consume();
200 if (mosueClick.getClickCount() >= 2) {
202 mosueClick.consume();
208 return getParent().sceneToLocal(
new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()));
215 Interval i =
adjustInterval(start.isBefore(end) ?
new Interval(start, end) :
new Interval(end, start));
236 protected abstract String
formatSpan(
final X date);
247 @NbBundle.Messages(value = {
"# {0} - start timestamp",
248 "# {1} - end timestamp",
249 "Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range:\n{0} to {1}\nRight-click to clear."})
253 startLabel.setText(startString);
254 endLabel.setText(endString);
256 Tooltip.uninstall(
this, tooltip);
257 tooltip =
new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString));
258 Tooltip.install(
this, tooltip);
278 return chart.
getXAxis().getValueForDisplay(chart.
getXAxis().parentToLocal(display, 0).getX());
294 @NbBundle.Messages(
"IntervalSelector.ZoomAction.name=Zoom")
296 super(Bundle.IntervalSelector_ZoomAction_name());
297 setGraphic(
new ImageView(ZOOM_TO_INTERVAL_ICON));
298 setEventHandler((ActionEvent t) -> {
306 @NbBundle.Messages(
"IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval")
309 setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText());
310 setGraphic(
new ImageView(ClEAR_INTERVAL_ICON));
311 setEventHandler((ActionEvent t) -> {
IntervalSelector(TimeLineChart< X > chart)
void clearIntervalSelector()
final TimeLineController controller
Point2D getLocalMouseCoords(MouseEvent mouseEvent)
TimeLineController getController()
final TimeLineChart< X > chart
abstract Interval adjustInterval(Interval i)
synchronized boolean pushTimeRange(Interval timeRange)
abstract DateTime parseDateTime(X date)
static final Image ClEAR_INTERVAL_ICON
final BooleanProperty isDragging
static final Image ZOOM_TO_INTERVAL_ICON
static final double STROKE_WIDTH
DragPosition dragPosition
void zoomToSelectedInterval()
static void construct(Node node, String fxmlFileName)
static final double HALF_STROKE
X getValueForDisplay(final double display)
abstract String formatSpan(final X date)