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.chart.Axis;
30 import javafx.scene.control.Button;
31 import javafx.scene.control.Label;
32 import javafx.scene.control.Tooltip;
33 import javafx.scene.image.Image;
34 import javafx.scene.image.ImageView;
35 import javafx.scene.input.MouseButton;
36 import javafx.scene.input.MouseEvent;
37 import javafx.scene.layout.BorderPane;
38 import javafx.scene.paint.Color;
39 import javafx.scene.shape.Rectangle;
40 import org.controlsfx.control.action.Action;
41 import org.controlsfx.control.action.ActionUtils;
42 import org.joda.time.DateTime;
43 import org.joda.time.Interval;
44 import org.openide.util.NbBundle;
59 private static final Image
CLEAR_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/cross-script.png", 16, 16,
true,
true,
true);
60 private static final Image
ZOOM_TO_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/magnifier-zoom-fit.png", 16, 16,
true,
true,
true);
76 private final BooleanProperty
isDragging =
new SimpleBooleanProperty(
false);
103 assert startLabel != null :
"fx:id=\"startLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
104 assert endLabel != null :
"fx:id=\"endLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
105 assert closeButton != null :
"fx:id=\"closeButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
106 assert zoomButton != null :
"fx:id=\"zoomButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
108 setMaxHeight(USE_PREF_SIZE);
109 setMinHeight(USE_PREF_SIZE);
110 setMaxWidth(USE_PREF_SIZE);
111 setMinWidth(USE_PREF_SIZE);
113 BooleanBinding showingControls = zoomButton.hoverProperty().or(bottomBorder.hoverProperty().or(hoverProperty())).and(isDragging.not());
114 closeButton.visibleProperty().bind(showingControls);
115 closeButton.managedProperty().bind(showingControls);
116 zoomButton.visibleProperty().bind(showingControls);
117 zoomButton.managedProperty().bind(showingControls);
119 widthProperty().addListener(o -> {
121 if (startLabel.getWidth() + zoomButton.getWidth() + endLabel.getWidth() > getWidth() - 10) {
122 this.setCenter(zoomButton);
123 bottomBorder.setCenter(
new Rectangle(10, 10, Color.TRANSPARENT));
125 bottomBorder.setCenter(zoomButton);
127 BorderPane.setAlignment(zoomButton, Pos.BOTTOM_CENTER);
132 setOnMouseMoved(mouseMove -> {
134 final double diffX = getLayoutX() - parentMouse.getX();
136 setCursor(Cursor.W_RESIZE);
137 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
138 setCursor(Cursor.E_RESIZE);
140 setCursor(Cursor.HAND);
145 setOnMousePressed(mousePress -> {
147 final double diffX = getLayoutX() - parentMouse.getX();
148 startDragX = mousePress.getScreenX();
149 startWidth = getWidth();
150 startLeft = getLayoutX();
153 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
158 mousePress.consume();
161 setOnMouseReleased((MouseEvent mouseRelease) -> {
162 isDragging.set(
false);
163 mouseRelease.consume();;
166 setOnMouseDragged(mouseDrag -> {
167 isDragging.set(
true);
168 double dX = mouseDrag.getScreenX() -
startDragX;
169 switch (dragPosition) {
171 setLayoutX(startLeft + dX);
174 if (dX > startWidth) {
175 startDragX = mouseDrag.getScreenX();
177 dragPosition = DragPosition.RIGHT;
179 setLayoutX(startLeft + dX);
180 setPrefWidth(startWidth - dX);
187 dragPosition = DragPosition.LEFT;
188 startDragX = mouseDrag.getScreenX();
191 setPrefWidth(startWidth + dX);
199 setOnMouseClicked(mouseClick -> {
200 if (mouseClick.getButton() == MouseButton.SECONDARY) {
202 }
else if (mouseClick.getClickCount() >= 2) {
204 mouseClick.consume();
208 ActionUtils.configureButton(
new ZoomToSelectedIntervalAction(), zoomButton);
209 ActionUtils.configureButton(
new ClearSelectedIntervalAction(), closeButton);
213 return getParent().sceneToLocal(
new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()));
220 Interval i =
adjustInterval(start.isBefore(end) ?
new Interval(start, end) :
new Interval(end, start));
241 protected abstract String
formatSpan(
final X date);
252 @NbBundle.Messages(value = {
"# {0} - start timestamp",
253 "# {1} - end timestamp",
254 "Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range:\n{0} to {1}.\n\nRight-click to close."})
258 startLabel.setText(startString);
259 endLabel.setText(endString);
261 Tooltip.uninstall(
this, tooltip);
262 tooltip =
new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString));
263 Tooltip.install(
this, tooltip);
283 return chart.
getXAxis().getValueForDisplay(chart.
getXAxis().parentToLocal(display, 0).getX());
299 @NbBundle.Messages(
"IntervalSelector.ZoomAction.name=Zoom")
301 super(Bundle.IntervalSelector_ZoomAction_name());
302 setGraphic(
new ImageView(ZOOM_TO_INTERVAL_ICON));
303 setEventHandler((ActionEvent t) -> {
311 @NbBundle.Messages(
"IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval")
314 setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText());
315 setGraphic(
new ImageView(CLEAR_INTERVAL_ICON));
316 setEventHandler((ActionEvent t) -> {
void clearIntervalSelector()
final TimeLineController controller
void setIntervalSelector(IntervalSelector<?extends X > newIntervalSelector)
Point2D getLocalMouseCoords(MouseEvent mouseEvent)
TimeLineController getController()
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
IntervalSelector<?extends X > getIntervalSelector()
IntervalSelector< X > newIntervalSelector()
static final double STROKE_WIDTH
DragPosition dragPosition
void zoomToSelectedInterval()
IntervalSelector(IntervalSelectorProvider< X > chart)
final IntervalSelectorProvider< X > chart
static void construct(Node node, String fxmlFileName)
static final double HALF_STROKE
X getValueForDisplay(final double display)
abstract String formatSpan(final X date)