From 84a31f37386b3e34d23ec2cb7285e439c0cf0870 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 2 Oct 2015 17:11:15 -0400 Subject: [PATCH] allow user to drag interval selector with any mouse button --- .../autopsy/timeline/ui/Bundle.properties | 3 +- .../autopsy/timeline/ui/TimeLineChart.java | 119 +++++++++++------- .../timeline/ui/countsview/Bundle.properties | 2 - .../ui/countsview/EventCountsChart.java | 53 +++++--- 4 files changed, 116 insertions(+), 61 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties index 1c31801521..9283be2f95 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013 Basis Technology Corp. + * Copyright 2013-15 Basis Technology Corp. * Contact: carrier sleuthkit 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 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java index 55efda74e1..d05b1149b0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java @@ -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 extends TimeLineView { * @param the type of values along the horizontal axis * @param the type of chart this is a drag handler for */ - class ChartDragHandler> implements EventHandler { + static class ChartDragHandler> implements EventHandler { private final Y chart; @@ -77,46 +78,80 @@ public interface TimeLineChart 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 dateAxis) { this.chart = chart; this.dateAxis = dateAxis; } @Override - public void handle(MouseEvent t) { - if (t.getButton() == MouseButton.SECONDARY) { - - if (t.getEventType() == 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) { - if (chart.getIntervalSelector() == null) { - //make new interval selector - chart.setIntervalSelector(chart.newIntervalSelector(t.getX(), dateAxis)); - chart.getIntervalSelector().heightProperty().bind(chart.heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty()))); - chart.getIntervalSelector().addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { - if (event.getButton() == MouseButton.SECONDARY) { - chart.clearIntervalSelector(); - event.consume(); - } - }); - startX = t.getX(); - } else { - //resize/position existing selector - if (t.getX() > startX) { - chart.getIntervalSelector().setX(startX); - chart.getIntervalSelector().setWidth(t.getX() - startX); - } else { - chart.getIntervalSelector().setX(t.getX()); - chart.getIntervalSelector().setWidth(startX - t.getX()); + public void handle(MouseEvent mouseEvent) { + EventType mouseEventType = mouseEvent.getEventType(); + if (mouseEventType == MouseEvent.MOUSE_PRESSED) { + //caputure x-position, incase we are repositioning existing selector + 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(mouseEvent.getX(), dateAxis)); + chart.getIntervalSelector().heightProperty().bind(chart.heightProperty().subtract(dateAxis.heightProperty().subtract(dateAxis.tickLengthProperty()))); + IntervalSelector intervalSelector = chart.getIntervalSelector(); + if (intervalSelector != null) { + intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); } - } - } else if (t.getEventType() == MouseEvent.MOUSE_RELEASED) { - chart.setCursor(Cursor.DEFAULT); + }); + } + startX = mouseEvent.getX(); + } else { + //resize/position existing selector + if (mouseEvent.getX() > startX) { + chart.getIntervalSelector().setX(startX); + chart.getIntervalSelector().setWidth(mouseEvent.getX() - startX); + } else { + chart.getIntervalSelector().setX(mouseEvent.getX()); + chart.getIntervalSelector().setWidth(startX - mouseEvent.getX()); + } + } + } else if (mouseEventType == MouseEvent.MOUSE_RELEASED) { +// chart.setCursor(Cursor.DEFAULT); + requireDrag = true; + IntervalSelector intervalSelector = chart.getIntervalSelector(); + if (intervalSelector != null) { + intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); + } + }); + } + } else if (mouseEventType == MouseEvent.MOUSE_CLICKED) { +// chart.setCursor(Cursor.DEFAULT); + requireDrag = true; + IntervalSelector intervalSelector = chart.getIntervalSelector(); + if (intervalSelector != null) { + intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); + } + }); } - t.consume(); } +// mouseEvent.consume(); } } @@ -140,15 +175,12 @@ public interface TimeLineChart extends TimeLineView { */ private final Axis 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 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 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 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 extends TimeLineView { */ private enum DragPosition { - LEFT, CENTER, RIGHT + LEFT, + CENTER, + RIGHT } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties index 1248574772..640949cfcd 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties @@ -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 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java index 78eeb702dc..3998872668 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java @@ -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} used to display the event * counts in {@link CountsViewPane} */ -class EventCountsChart extends StackedBarChart implements TimeLineChart { +final class EventCountsChart extends StackedBarChart implements TimeLineChart { - private ContextMenu contextMenu; + private ContextMenu chartContextMenu; private TimeLineController controller; @@ -58,6 +59,7 @@ class EventCountsChart extends StackedBarChart implements TimeLi * by padding the end with one 'period' */ private RangeDivisionInfo rangeInfo; + private final ChartDragHandler dragHandler; EventCountsChart(CategoryAxis dateAxis, NumberAxis countAxis) { super(dateAxis, countAxis); @@ -81,15 +83,19 @@ class EventCountsChart extends StackedBarChart implements TimeLi setTitle(null); //use one handler with an if chain because it maintains state - ChartDragHandler 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 implements TimeLi intervalSelector = null; } + @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)))); + chartContextMenu.setAutoHide(true); + return chartContextMenu; + } + @Override - public final synchronized void setController(TimeLineController controller) { + public 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"), - new Back(controller), - new Forward(controller)))); - contextMenu.setAutoHide(true); } @Override @@ -145,7 +168,7 @@ class EventCountsChart extends StackedBarChart 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 implements TimeLi */ private class CountsIntervalSelector extends IntervalSelector { - public CountsIntervalSelector(double x, double height, Axis axis, TimeLineController controller) { + CountsIntervalSelector(double x, double height, Axis axis, TimeLineController controller) { super(x, height, axis, controller); }