Autopsy 4.22.1
Graphical digital forensics platform for The Sleuth Kit and other tools.
IntervalSelector.java
Go to the documentation of this file.
1/*
2 * Autopsy Forensic Browser
3 *
4 * Copyright 2014-18 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 */
19package org.sleuthkit.autopsy.timeline.ui;
20
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;
50
60public abstract class IntervalSelector<X> extends BorderPane {
61
62 private static final Logger logger = Logger.getLogger(IntervalSelector.class.getName());
63
64 private static final Image CLEAR_INTERVAL_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/cross-script.png", 16, 16, true, true, true); //NON-NLS
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); //NON-NLS
66 private static final double STROKE_WIDTH = 3;
67 private static final double HALF_STROKE = STROKE_WIDTH / 2;
68
73
74 private Tooltip tooltip;
77 private double startLeft;
78 private double startDragX;
79 private double startWidth;
80
81 private final BooleanProperty isDragging = new SimpleBooleanProperty(false);
84
85 @FXML
86 private Label startLabel;
87
88 @FXML
89 private Label endLabel;
90
91 @FXML
92 private Button closeButton;
93
94 @FXML
95 private Button zoomButton;
96
97 @FXML
98 private BorderPane bottomBorder;
99
100 @SuppressWarnings("this-escape")
102 this.chart = chart;
103 this.controller = chart.getController();
104 FXMLConstructor.construct(this, IntervalSelector.class, "IntervalSelector.fxml"); // NON-NLS
105 }
106
107 @FXML
108 void initialize() {
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'.";
113
114 setMaxHeight(USE_PREF_SIZE);
115 setMinHeight(USE_PREF_SIZE);
116 setMaxWidth(USE_PREF_SIZE);
117 setMinWidth(USE_PREF_SIZE);
118
119 BooleanBinding showingControls = zoomButton.hoverProperty().or(bottomBorder.hoverProperty().or(hoverProperty())).and(isDragging.not());
120 closeButton.visibleProperty().bind(showingControls);
121 closeButton.managedProperty().bind(showingControls);
122 zoomButton.visibleProperty().bind(showingControls);
123 zoomButton.managedProperty().bind(showingControls);
124
125 widthProperty().addListener(observable -> {
127 if (startLabel.getWidth() + zoomButton.getWidth() + endLabel.getWidth() > getWidth() - 10) {
128 this.setCenter(zoomButton);
129 bottomBorder.setCenter(new Rectangle(10, 10, Color.TRANSPARENT));
130 } else {
131 bottomBorder.setCenter(zoomButton);
132 }
133 BorderPane.setAlignment(zoomButton, Pos.BOTTOM_CENTER);
134 });
135 layoutXProperty().addListener(observable -> this.updateStartAndEnd());
137
138 setOnMouseMoved(mouseMove -> {
139 Point2D parentMouse = getLocalMouseCoords(mouseMove);
140 final double diffX = getLayoutX() - parentMouse.getX();
141 if (Math.abs(diffX) <= HALF_STROKE) {
142 setCursor(Cursor.W_RESIZE);
143 } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) {
144 setCursor(Cursor.E_RESIZE);
145 } else {
146 setCursor(Cursor.HAND);
147 }
148 mouseMove.consume();
149 });
150
151 setOnMousePressed(mousePress -> {
152 Point2D parentMouse = getLocalMouseCoords(mousePress);
153 final double diffX = getLayoutX() - parentMouse.getX();
154 startDragX = mousePress.getScreenX();
155 startWidth = getWidth();
156 startLeft = getLayoutX();
157 if (Math.abs(diffX) <= HALF_STROKE) {
158 dragPosition = IntervalSelector.DragPosition.LEFT;
159 } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) {
160 dragPosition = IntervalSelector.DragPosition.RIGHT;
161 } else {
162 dragPosition = IntervalSelector.DragPosition.CENTER;
163 }
164 mousePress.consume();
165 });
166
167 setOnMouseReleased((MouseEvent mouseRelease) -> {
168 isDragging.set(false);
169 mouseRelease.consume();
170 });
171
172 setOnMouseDragged(mouseDrag -> {
173 isDragging.set(true);
174 double deltaX = mouseDrag.getScreenX() - startDragX;
175 switch (dragPosition) {
176 case CENTER:
177 setLayoutX(startLeft + deltaX);
178 break;
179 case LEFT:
180 if (deltaX > startWidth) {
181 startDragX = mouseDrag.getScreenX();
182 startWidth = 0;
184 } else {
185 setLayoutX(startLeft + deltaX);
186 setPrefWidth(startWidth - deltaX);
187 autosize();
188 }
189 break;
190 case RIGHT:
191 Point2D parentMouse = getLocalMouseCoords(mouseDrag);
192 if (parentMouse.getX() < startLeft) {
194 startDragX = mouseDrag.getScreenX();
195 startWidth = 0;
196 } else {
197 setPrefWidth(startWidth + deltaX);
198 autosize();
199 }
200 break;
201 }
202 mouseDrag.consume();
203 });
204
205 setOnMouseClicked(mouseClick -> {
206 if (mouseClick.getButton() == MouseButton.SECONDARY) {
207 chart.clearIntervalSelector();
208 } else if (mouseClick.getClickCount() >= 2) {
210 mouseClick.consume();
211 }
212 });
213
214 ActionUtils.configureButton(new ZoomToSelectedIntervalAction(), zoomButton);
215 ActionUtils.configureButton(new ClearSelectedIntervalAction(), closeButton);
216 }
217
218 private Point2D getLocalMouseCoords(MouseEvent mouseEvent) {
219 return getParent().sceneToLocal(new Point2D(mouseEvent.getSceneX(), mouseEvent.getSceneY()));
220 }
221
222 @NbBundle.Messages({
223 "IntervalSelector.zoomToSelectedInterval.errorMessage=Error zooming in to the selected interval."})
224 private void zoomToSelectedInterval() {
225 //convert to DateTimes, using max/min if null(off axis)
226 DateTime start = parseDateTime(getSpanStart());
227 DateTime end = parseDateTime(getSpanEnd());
228 Interval interval = adjustInterval(start.isBefore(end) ? new Interval(start, end) : new Interval(end, start));
229 try {
230 controller.pushTimeRange(interval);
231 } catch (TskCoreException ex) {
232 Notifications.create().owner(getScene().getWindow())
233 .text(Bundle.IntervalSelector_zoomToSelectedInterval_errorMessage())
234 .showError();
235 logger.log(Level.SEVERE, "Error zooming in to the selected interval.");
236 }
237 }
238
246 protected abstract Interval adjustInterval(Interval interval);
247
256 protected abstract String formatSpan(final X date);
257
265 protected abstract DateTime parseDateTime(X date);
266
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."})
270 private void updateStartAndEnd() {
271 String startString = formatSpan(getSpanStart());
272 String endString = formatSpan(getSpanEnd());
273 startLabel.setText(startString);
274 endLabel.setText(endString);
275
276 Tooltip.uninstall(this, tooltip);
277 tooltip = new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString));
278 Tooltip.install(this, tooltip);
279 }
280
285 public X getSpanEnd() {
286 return getValueForDisplay(getBoundsInParent().getMaxX());
287 }
288
293 public X getSpanStart() {
294 return getValueForDisplay(getBoundsInParent().getMinX());
295 }
296
297 private X getValueForDisplay(final double displayX) {
298 return chart.getXAxis().getValueForDisplay(chart.getXAxis().parentToLocal(displayX, 0).getX());
299 }
300
305 private enum DragPosition {
306
310 }
311
312 private class ZoomToSelectedIntervalAction extends Action {
313
314 @NbBundle.Messages("IntervalSelector.ZoomAction.name=Zoom")
315 ZoomToSelectedIntervalAction() {
316 super(Bundle.IntervalSelector_ZoomAction_name());
317 setGraphic(new ImageView(ZOOM_TO_INTERVAL_ICON));
318 setEventHandler(actionEvent -> zoomToSelectedInterval());
319 }
320 }
321
322 private class ClearSelectedIntervalAction extends Action {
323
324 @NbBundle.Messages("IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval")
325 ClearSelectedIntervalAction() {
326 super("");
327 setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText());
328 setGraphic(new ImageView(CLEAR_INTERVAL_ICON));
329 setEventHandler(ationEvent -> chart.clearIntervalSelector());
330 }
331 }
332
357}
synchronized static Logger getLogger(String name)
Definition Logger.java:124
static void construct(Node node, String fxmlFileName)
abstract Interval adjustInterval(Interval interval)
IntervalSelector(IntervalSelectorProvider< X > chart)
void setIntervalSelector(IntervalSelector<? extends X > newIntervalSelector)

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.