19package org.sleuthkit.autopsy.timeline.ui;
21import java.util.logging.Level;
22import javafx.beans.binding.BooleanBinding;
23import javafx.beans.property.BooleanProperty;
24import javafx.beans.property.SimpleBooleanProperty;
25import javafx.fxml.FXML;
26import javafx.geometry.Point2D;
27import javafx.geometry.Pos;
28import javafx.scene.Cursor;
29import javafx.scene.chart.Axis;
30import javafx.scene.control.Button;
31import javafx.scene.control.Label;
32import javafx.scene.control.Tooltip;
33import javafx.scene.image.Image;
34import javafx.scene.image.ImageView;
35import javafx.scene.input.MouseButton;
36import javafx.scene.input.MouseEvent;
37import javafx.scene.layout.BorderPane;
38import javafx.scene.paint.Color;
39import javafx.scene.shape.Rectangle;
40import org.controlsfx.control.Notifications;
41import org.controlsfx.control.action.Action;
42import org.controlsfx.control.action.ActionUtils;
43import org.joda.time.DateTime;
44import org.joda.time.Interval;
45import org.openide.util.NbBundle;
46import org.sleuthkit.autopsy.coreutils.Logger;
47import org.sleuthkit.autopsy.timeline.FXMLConstructor;
48import org.sleuthkit.autopsy.timeline.TimeLineController;
49import org.sleuthkit.datamodel.TskCoreException;
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);
100 @SuppressWarnings(
"this-escape")
103 this.controller =
chart.getController();
109 assert
startLabel != null :
"fx:id=\"startLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
110 assert
endLabel != null :
"fx:id=\"endLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
111 assert
closeButton != null :
"fx:id=\"closeButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
112 assert
zoomButton != null :
"fx:id=\"zoomButton\" was not injected: check your FXML file 'IntervalSelector.fxml'.";
114 setMaxHeight(USE_PREF_SIZE);
115 setMinHeight(USE_PREF_SIZE);
116 setMaxWidth(USE_PREF_SIZE);
117 setMinWidth(USE_PREF_SIZE);
120 closeButton.visibleProperty().bind(showingControls);
121 closeButton.managedProperty().bind(showingControls);
122 zoomButton.visibleProperty().bind(showingControls);
123 zoomButton.managedProperty().bind(showingControls);
125 widthProperty().addListener(observable -> {
129 bottomBorder.setCenter(
new Rectangle(10, 10, Color.TRANSPARENT));
133 BorderPane.setAlignment(
zoomButton, Pos.BOTTOM_CENTER);
138 setOnMouseMoved(mouseMove -> {
140 final double diffX = getLayoutX() - parentMouse.getX();
142 setCursor(Cursor.W_RESIZE);
143 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
144 setCursor(Cursor.E_RESIZE);
146 setCursor(Cursor.HAND);
151 setOnMousePressed(mousePress -> {
153 final double diffX = getLayoutX() - parentMouse.getX();
159 }
else if (Math.abs(diffX + getWidth()) <=
HALF_STROKE) {
164 mousePress.consume();
167 setOnMouseReleased((MouseEvent mouseRelease) -> {
169 mouseRelease.consume();
172 setOnMouseDragged(mouseDrag -> {
174 double deltaX = mouseDrag.getScreenX() -
startDragX;
205 setOnMouseClicked(mouseClick -> {
206 if (mouseClick.getButton() == MouseButton.SECONDARY) {
207 chart.clearIntervalSelector();
208 }
else if (mouseClick.getClickCount() >= 2) {
210 mouseClick.consume();
219 return getParent().sceneToLocal(
new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()));
223 "IntervalSelector.zoomToSelectedInterval.errorMessage=Error zooming in to the selected interval."})
228 Interval interval =
adjustInterval(start.isBefore(end) ?
new Interval(start, end) :
new Interval(end, start));
231 }
catch (TskCoreException ex) {
232 Notifications.create().owner(getScene().getWindow())
233 .text(Bundle.IntervalSelector_zoomToSelectedInterval_errorMessage())
235 logger.log(Level.SEVERE,
"Error zooming in to the selected interval.");
267 @NbBundle.Messages(value = {
"# {0} - start timestamp",
268 "# {1} - end timestamp",
269 "Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range:\n{0} to {1}.\n\nRight-click to close."})
276 Tooltip.uninstall(
this,
tooltip);
277 tooltip =
new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString));
278 Tooltip.install(
this,
tooltip);
298 return chart.getXAxis().getValueForDisplay(
chart.getXAxis().parentToLocal(displayX, 0).getX());
312 private class ZoomToSelectedIntervalAction
extends Action {
314 @NbBundle.Messages(
"IntervalSelector.ZoomAction.name=Zoom")
315 ZoomToSelectedIntervalAction() {
316 super(Bundle.IntervalSelector_ZoomAction_name());
322 private class ClearSelectedIntervalAction
extends Action {
324 @NbBundle.Messages(
"IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval")
325 ClearSelectedIntervalAction() {
327 setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText());
329 setEventHandler(ationEvent ->
chart.clearIntervalSelector());
synchronized static Logger getLogger(String name)
static void construct(Node node, String fxmlFileName)
abstract String formatSpan(final X date)
final BooleanProperty isDragging
void zoomToSelectedInterval()
DragPosition dragPosition
Point2D getLocalMouseCoords(MouseEvent mouseEvent)
abstract Interval adjustInterval(Interval interval)
static final Image ZOOM_TO_INTERVAL_ICON
static final Logger logger
IntervalSelector(IntervalSelectorProvider< X > chart)
static final double HALF_STROKE
static final Image CLEAR_INTERVAL_ICON
final TimeLineController controller
final IntervalSelectorProvider< X > chart
static final double STROKE_WIDTH
X getValueForDisplay(final double displayX)
abstract DateTime parseDateTime(X date)
TimeLineController getController()
void setIntervalSelector(IntervalSelector<? extends X > newIntervalSelector)
IntervalSelector< X > newIntervalSelector()
void clearIntervalSelector()
IntervalSelector<? extends X > getIntervalSelector()