19 package org.sleuthkit.autopsy.timeline.ui;
21 import java.util.logging.Level;
22 import javafx.beans.binding.BooleanBinding;
23 import javafx.beans.property.BooleanProperty;
24 import javafx.beans.property.SimpleBooleanProperty;
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.Notifications;
41 import org.controlsfx.control.action.Action;
42 import org.controlsfx.control.action.ActionUtils;
43 import org.joda.time.DateTime;
44 import org.joda.time.Interval;
45 import org.openide.util.NbBundle;
64 private static final Image
CLEAR_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/cross-script.png", 16, 16,
true,
true,
true);
65 private static final Image
ZOOM_TO_INTERVAL_ICON =
new Image(
"/org/sleuthkit/autopsy/timeline/images/magnifier-zoom-fit.png", 16, 16,
true,
true,
true);
81 private final BooleanProperty
isDragging =
new SimpleBooleanProperty(
false);
108 assert startLabel != null :
"fx:id=\"startLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
109 assert endLabel != null :
"fx:id=\"endLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
110 assert closeButton != null :
"fx:id=\"closeButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
111 assert zoomButton != null :
"fx:id=\"zoomButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
113 setMaxHeight(USE_PREF_SIZE);
114 setMinHeight(USE_PREF_SIZE);
115 setMaxWidth(USE_PREF_SIZE);
116 setMinWidth(USE_PREF_SIZE);
118 BooleanBinding showingControls = zoomButton.hoverProperty().or(bottomBorder.hoverProperty().or(hoverProperty())).and(isDragging.not());
119 closeButton.visibleProperty().bind(showingControls);
120 closeButton.managedProperty().bind(showingControls);
121 zoomButton.visibleProperty().bind(showingControls);
122 zoomButton.managedProperty().bind(showingControls);
124 widthProperty().addListener(observable -> {
126 if (startLabel.getWidth() + zoomButton.getWidth() + endLabel.getWidth() > getWidth() - 10) {
127 this.setCenter(zoomButton);
128 bottomBorder.setCenter(
new Rectangle(10, 10, Color.TRANSPARENT));
130 bottomBorder.setCenter(zoomButton);
132 BorderPane.setAlignment(zoomButton, Pos.BOTTOM_CENTER);
137 setOnMouseMoved(mouseMove -> {
139 final double diffX = getLayoutX() - parentMouse.getX();
141 setCursor(Cursor.W_RESIZE);
142 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
143 setCursor(Cursor.E_RESIZE);
145 setCursor(Cursor.HAND);
150 setOnMousePressed(mousePress -> {
152 final double diffX = getLayoutX() - parentMouse.getX();
153 startDragX = mousePress.getScreenX();
154 startWidth = getWidth();
155 startLeft = getLayoutX();
158 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
163 mousePress.consume();
166 setOnMouseReleased((MouseEvent mouseRelease) -> {
167 isDragging.set(
false);
168 mouseRelease.consume();
171 setOnMouseDragged(mouseDrag -> {
172 isDragging.set(
true);
173 double deltaX = mouseDrag.getScreenX() -
startDragX;
174 switch (dragPosition) {
176 setLayoutX(startLeft + deltaX);
179 if (deltaX > startWidth) {
180 startDragX = mouseDrag.getScreenX();
182 dragPosition = DragPosition.RIGHT;
184 setLayoutX(startLeft + deltaX);
185 setPrefWidth(startWidth - deltaX);
192 dragPosition = DragPosition.LEFT;
193 startDragX = mouseDrag.getScreenX();
196 setPrefWidth(startWidth + deltaX);
204 setOnMouseClicked(mouseClick -> {
205 if (mouseClick.getButton() == MouseButton.SECONDARY) {
207 }
else if (mouseClick.getClickCount() >= 2) {
209 mouseClick.consume();
213 ActionUtils.configureButton(
new ZoomToSelectedIntervalAction(), zoomButton);
214 ActionUtils.configureButton(
new ClearSelectedIntervalAction(), closeButton);
218 return getParent().sceneToLocal(
new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()));
222 "IntervalSelector.zoomToSelectedInterval.errorMessage=Error zooming in to the selected interval."})
227 Interval interval =
adjustInterval(start.isBefore(end) ?
new Interval(start, end) :
new Interval(end, start));
230 }
catch (TskCoreException ex) {
231 Notifications.create().owner(getScene().getWindow())
232 .text(Bundle.IntervalSelector_zoomToSelectedInterval_errorMessage())
234 logger.log(Level.SEVERE,
"Error zooming in to the selected interval.");
255 protected abstract String
formatSpan(
final X date);
266 @NbBundle.Messages(value = {
"# {0} - start timestamp",
267 "# {1} - end timestamp",
268 "Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range:\n{0} to {1}.\n\nRight-click to close."})
272 startLabel.setText(startString);
273 endLabel.setText(endString);
275 Tooltip.uninstall(
this, tooltip);
276 tooltip =
new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString));
277 Tooltip.install(
this, tooltip);
297 return chart.
getXAxis().getValueForDisplay(chart.
getXAxis().parentToLocal(displayX, 0).getX());
313 @NbBundle.Messages(
"IntervalSelector.ZoomAction.name=Zoom")
315 super(Bundle.IntervalSelector_ZoomAction_name());
316 setGraphic(
new ImageView(ZOOM_TO_INTERVAL_ICON));
323 @NbBundle.Messages(
"IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval")
326 setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText());
327 setGraphic(
new ImageView(CLEAR_INTERVAL_ICON));
void clearIntervalSelector()
final TimeLineController controller
abstract Interval adjustInterval(Interval interval)
void setIntervalSelector(IntervalSelector<?extends X > newIntervalSelector)
Point2D getLocalMouseCoords(MouseEvent mouseEvent)
TimeLineController getController()
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
synchronized static Logger getLogger(String name)
X getValueForDisplay(final double displayX)
static void construct(Node node, String fxmlFileName)
static final double HALF_STROKE
static final Logger logger
abstract String formatSpan(final X date)