allow user to drag interval selector with any mouse button

This commit is contained in:
jmillman 2015-10-02 17:11:15 -04:00
parent 3c235d9bac
commit 84a31f3738
4 changed files with 116 additions and 61 deletions

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013 Basis Technology Corp.
* Copyright 2013-15 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -35,7 +35,6 @@ Timeline.ui.ZoomRanges.threeyears.text=Three Years
Timeline.ui.ZoomRanges.fiveyears.text=Five Years
Timeline.ui.ZoomRanges.tenyears.text=Ten Years
Timeline.ui.ZoomRanges.all.text=All
Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range\:\n{0} to {1}\nRight-click to clear.
TimeLineResultView.startDateToEndDate.text={0} to {1}
VisualizationPanel.histogramTask.title=Rebuild Histogram
VisualizationPanel.histogramTask.preparing=preparing

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.timeline.ui;
import javafx.event.EventHandler;
import javafx.event.EventType;
import javafx.geometry.Point2D;
import javafx.scene.Cursor;
import javafx.scene.chart.Axis;
@ -69,7 +70,7 @@ public interface TimeLineChart<X> extends TimeLineView {
* @param <X> the type of values along the horizontal axis
* @param <Y> the type of chart this is a drag handler for
*/
class ChartDragHandler<X, Y extends Chart & TimeLineChart<X>> implements EventHandler<MouseEvent> {
static class ChartDragHandler<X, Y extends Chart & TimeLineChart<X>> implements EventHandler<MouseEvent> {
private final Y chart;
@ -77,46 +78,80 @@ public interface TimeLineChart<X> extends TimeLineView {
private double startX; //hanlder mainstains position of drag start
private boolean requireDrag = true;
public boolean isRequireDrag() {
return requireDrag;
}
public void setRequireDrag(boolean requireDrag) {
this.requireDrag = requireDrag;
}
public ChartDragHandler(Y chart, Axis<X> dateAxis) {
this.chart = chart;
this.dateAxis = dateAxis;
}
@Override
public void handle(MouseEvent t) {
if (t.getButton() == MouseButton.SECONDARY) {
if (t.getEventType() == MouseEvent.MOUSE_PRESSED) {
public void handle(MouseEvent mouseEvent) {
EventType<? extends MouseEvent> mouseEventType = mouseEvent.getEventType();
if (mouseEventType == MouseEvent.MOUSE_PRESSED) {
//caputure x-position, incase we are repositioning existing selector
startX = t.getX();
chart.setCursor(Cursor.E_RESIZE);
} else if (t.getEventType() == MouseEvent.MOUSE_DRAGGED) {
startX = mouseEvent.getX();
// chart.setCursor(Cursor.H_RESIZE);
} else if ((mouseEventType == MouseEvent.MOUSE_DRAGGED)
|| mouseEventType == MouseEvent.MOUSE_MOVED && (requireDrag == false)) {
if (chart.getIntervalSelector() == null) {
//make new interval selector
chart.setIntervalSelector(chart.newIntervalSelector(t.getX(), dateAxis));
chart.setIntervalSelector(chart.newIntervalSelector(mouseEvent.getX(), dateAxis));
chart.getIntervalSelector().heightProperty().bind(chart.heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty())));
chart.getIntervalSelector().addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
IntervalSelector<? extends X> intervalSelector = chart.getIntervalSelector();
if (intervalSelector != null) {
intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
chart.clearIntervalSelector();
event.consume();
}
});
startX = t.getX();
}
startX = mouseEvent.getX();
} else {
//resize/position existing selector
if (t.getX() > startX) {
if (mouseEvent.getX() > startX) {
chart.getIntervalSelector().setX(startX);
chart.getIntervalSelector().setWidth(t.getX() - startX);
chart.getIntervalSelector().setWidth(mouseEvent.getX() - startX);
} else {
chart.getIntervalSelector().setX(t.getX());
chart.getIntervalSelector().setWidth(startX - t.getX());
chart.getIntervalSelector().setX(mouseEvent.getX());
chart.getIntervalSelector().setWidth(startX - mouseEvent.getX());
}
}
} else if (t.getEventType() == MouseEvent.MOUSE_RELEASED) {
chart.setCursor(Cursor.DEFAULT);
} else if (mouseEventType == MouseEvent.MOUSE_RELEASED) {
// chart.setCursor(Cursor.DEFAULT);
requireDrag = true;
IntervalSelector<? extends X> intervalSelector = chart.getIntervalSelector();
if (intervalSelector != null) {
intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
chart.clearIntervalSelector();
event.consume();
}
t.consume();
});
}
} else if (mouseEventType == MouseEvent.MOUSE_CLICKED) {
// chart.setCursor(Cursor.DEFAULT);
requireDrag = true;
IntervalSelector<? extends X> intervalSelector = chart.getIntervalSelector();
if (intervalSelector != null) {
intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> {
if (event.getButton() == MouseButton.SECONDARY) {
chart.clearIntervalSelector();
event.consume();
}
});
}
}
// mouseEvent.consume();
}
}
@ -140,15 +175,12 @@ public interface TimeLineChart<X> extends TimeLineView {
*/
private final Axis<X> dateAxis;
protected Tooltip tooltip;
private Tooltip tooltip;
/////////drag state
private DragPosition dragPosition;
private double startLeft;
private double startX;
private double startWidth;
/////////end drag state
@ -179,7 +211,8 @@ public interface TimeLineChart<X> extends TimeLineView {
Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY()));
final double diffX = getX() - localMouse.getX();
if (Math.abs(diffX) <= HALF_STROKE || Math.abs(diffX + getWidth()) <= HALF_STROKE) {
setCursor(Cursor.E_RESIZE);
//if the mouse is over the stroke, show the resize cursor
setCursor(Cursor.H_RESIZE);
} else {
setCursor(Cursor.HAND);
}
@ -221,9 +254,7 @@ public interface TimeLineChart<X> extends TimeLineView {
//convert to DateTimes, using max/min if null(off axis)
DateTime start = parseDateTime(getSpanStart());
DateTime end = parseDateTime(getSpanEnd());
Interval i = adjustInterval(start.isBefore(end) ? new Interval(start, end) : new Interval(end, start));
controller.pushTimeRange(i);
}
}
@ -258,13 +289,15 @@ public interface TimeLineChart<X> extends TimeLineView {
*/
protected abstract DateTime parseDateTime(X date);
@NbBundle.Messages({"# {0} - start timestamp",
"# {1} - end timestamp",
"Timeline.ui.TimeLineChart.tooltip.text=Double-click to zoom into range:\n{0} to {1}\nRight-click to clear."})
private void setTooltip() {
final X start = getSpanStart();
final X end = getSpanEnd();
Tooltip.uninstall(this, tooltip);
tooltip = new Tooltip(
NbBundle.getMessage(TimeLineChart.class, "Timeline.ui.TimeLineChart.tooltip.text", formatSpan(start),
formatSpan(end)));
Bundle.Timeline_ui_TimeLineChart_tooltip_text(formatSpan(start), formatSpan(end)));
Tooltip.install(this, tooltip);
}
@ -290,7 +323,9 @@ public interface TimeLineChart<X> extends TimeLineView {
*/
private enum DragPosition {
LEFT, CENTER, RIGHT
LEFT,
CENTER,
RIGHT
}
}
}

View File

@ -33,7 +33,5 @@ CountsViewPane.loggedTask.resetUI=resetting ui
CountsViewPane.tooltip.text={0} {1} events\nbetween {2}\nand {3}
CountsViewPane.loggedTask.updatingCounts=updating counts
CountsViewPane.loggedTask.wrappingUp=wrapping up
EventCountsChart.contextMenu.zoomHistory.name=Zoom History
CountsViewPane.scaleLabel.text=Scale\:
CountsViewPane.logRadio.text=Logarithmic
CountsViewPane.linearRadio.text=Linear

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline.ui.countsview;
import java.util.Arrays;
import java.util.Collections;
import java.util.MissingResourceException;
import javafx.scene.chart.Axis;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
@ -44,9 +45,9 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
* Customized {@link StackedBarChart<String, Number>} used to display the event
* counts in {@link CountsViewPane}
*/
class EventCountsChart extends StackedBarChart<String, Number> implements TimeLineChart<String> {
final class EventCountsChart extends StackedBarChart<String, Number> implements TimeLineChart<String> {
private ContextMenu contextMenu;
private ContextMenu chartContextMenu;
private TimeLineController controller;
@ -58,6 +59,7 @@ class EventCountsChart extends StackedBarChart<String, Number> implements TimeLi
* by padding the end with one 'period'
*/
private RangeDivisionInfo rangeInfo;
private final ChartDragHandler<String, EventCountsChart> dragHandler;
EventCountsChart(CategoryAxis dateAxis, NumberAxis countAxis) {
super(dateAxis, countAxis);
@ -81,15 +83,19 @@ class EventCountsChart extends StackedBarChart<String, Number> implements TimeLi
setTitle(null);
//use one handler with an if chain because it maintains state
ChartDragHandler<String, EventCountsChart> dragHandler = new ChartDragHandler<>(this, getXAxis());
dragHandler = new ChartDragHandler<>(this, getXAxis());
setOnMousePressed(dragHandler);
setOnMouseReleased(dragHandler);
setOnMouseDragged(dragHandler);
setOnMouseMoved(dragHandler);
setOnMouseClicked((MouseEvent clickEvent) -> {
contextMenu.hide();
if (chartContextMenu != null) {
chartContextMenu.hide();
}
if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) {
contextMenu.show(EventCountsChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
getChartContextMenu(clickEvent);
chartContextMenu.show(EventCountsChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
clickEvent.consume();
}
});
@ -101,17 +107,34 @@ class EventCountsChart extends StackedBarChart<String, Number> implements TimeLi
intervalSelector = null;
}
@Override
public final synchronized void setController(TimeLineController controller) {
this.controller = controller;
setModel(this.controller.getEventsModel());
//we have defered creating context menu until control is available
contextMenu = ActionUtils.createContextMenu(
Arrays.asList(new ActionGroup(
NbBundle.getMessage(this.getClass(), "EventCountsChart.contextMenu.zoomHistory.name"),
@NbBundle.Messages({"EventCountsChart.contextMenu.zoomHistory.name=Zoom History"})
ContextMenu getChartContextMenu(MouseEvent clickEvent) throws MissingResourceException {
if (chartContextMenu != null) {
chartContextMenu.hide();
}
chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(
// new Action("Select Interval") {
// {
// setEventHandler((ActionEvent t) -> {
// dragHandler.setRequireDrag(false);
// dragHandler.handle(clickEvent.copyFor(clickEvent.getSource(), clickEvent.getTarget(), MouseEvent.MOUSE_DRAGGED));
// });
// }
//
// },
new ActionGroup(
Bundle.EventCountsChart_contextMenu_zoomHistory_name(),
new Back(controller),
new Forward(controller))));
contextMenu.setAutoHide(true);
chartContextMenu.setAutoHide(true);
return chartContextMenu;
}
@Override
public synchronized void setController(TimeLineController controller) {
this.controller = controller;
setModel(this.controller.getEventsModel());
}
@Override
@ -145,7 +168,7 @@ class EventCountsChart extends StackedBarChart<String, Number> implements TimeLi
* @return the context menu for this chart
*/
ContextMenu getContextMenu() {
return contextMenu;
return chartContextMenu;
}
void setRangeInfo(RangeDivisionInfo rangeInfo) {
@ -177,7 +200,7 @@ class EventCountsChart extends StackedBarChart<String, Number> implements TimeLi
*/
private class CountsIntervalSelector extends IntervalSelector<String> {
public CountsIntervalSelector(double x, double height, Axis<String> axis, TimeLineController controller) {
CountsIntervalSelector(double x, double height, Axis<String> axis, TimeLineController controller) {
super(x, height, axis, controller);
}