From 59e90553974525363e5b34feb1405101d3829b05 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 4 Sep 2015 13:58:50 -0400 Subject: [PATCH 01/48] make IG PrePopulateDataSourceFiles cancellable --- .../imagegallery/ImageGalleryController.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java index 61a1a459e2..5b874cfbc0 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/ImageGalleryController.java @@ -53,6 +53,7 @@ import javax.swing.SwingUtilities; import org.apache.commons.lang3.StringUtils; import org.netbeans.api.progress.ProgressHandle; import org.netbeans.api.progress.ProgressHandleFactory; +import org.openide.util.Cancellable; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.History; @@ -584,7 +585,7 @@ public final class ImageGalleryController { try { InnerTask it = workQueue.take(); - if (it.cancelled == false) { + if (it.isCancelled() == false) { it.run(); } @@ -606,7 +607,7 @@ public final class ImageGalleryController { /** * Abstract base class for task to be done on {@link DBWorkerThread} */ - static public abstract class InnerTask implements Runnable { + static public abstract class InnerTask implements Runnable, Cancellable { public double getProgress() { return progress.get(); @@ -650,13 +651,13 @@ public final class ImageGalleryController { protected InnerTask() { } - protected volatile boolean cancelled = false; - - public void cancel() { + @Override + synchronized public boolean cancel() { updateState(Worker.State.CANCELLED); + return true; } - protected boolean isCancelled() { + synchronized protected boolean isCancelled() { return getState() == Worker.State.CANCELLED; } } @@ -798,7 +799,7 @@ public final class ImageGalleryController { DrawableDB.DrawableTransaction tr = taskDB.beginTransaction(); int units = 0; for (final AbstractFile f : files) { - if (cancelled) { + if (isCancelled()) { LOGGER.log(Level.WARNING, "task cancelled: not all contents may be transfered to database"); progressHandle.finish(); break; @@ -876,7 +877,7 @@ public final class ImageGalleryController { // (name like '.jpg' or name like '.png' ...) private final String DRAWABLE_QUERY = "(name LIKE '%." + StringUtils.join(FileTypeUtils.getAllSupportedExtensions(), "' or name LIKE '%.") + "') "; - private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database"); + private ProgressHandle progressHandle = ProgressHandleFactory.createHandle("prepopulating image/video database", this); /** * @@ -929,7 +930,7 @@ public final class ImageGalleryController { DrawableDB.DrawableTransaction tr = db.beginTransaction(); int units = 0; for (final AbstractFile f : files) { - if (cancelled) { + if (isCancelled()) { LOGGER.log(Level.WARNING, "task cancelled: not all contents may be transfered to database"); progressHandle.finish(); break; From 6ea119cacf8e494ab3d60c3d04f6a2fd9d730524 Mon Sep 17 00:00:00 2001 From: momo Date: Thu, 8 Oct 2015 16:52:28 -0400 Subject: [PATCH 02/48] change structure of creating mime types --- .../UserDefinedFileTypesManager.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java index 2fe4efea3c..7daf09e346 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java @@ -180,17 +180,17 @@ final class UserDefinedFileTypesManager { * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException */ private void loadPredefinedFileTypes() throws UserDefinedFileTypesException { - try { - FileType fileType = new FileType("text/xml", new Signature(" Date: Fri, 9 Oct 2015 09:52:54 -0400 Subject: [PATCH 03/48] adding all missed mime types that can be added in current state --- .../UserDefinedFileTypesManager.java | 37 ++++++++++++++++--- 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java index 7daf09e346..1dd1ee989d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java @@ -180,16 +180,41 @@ final class UserDefinedFileTypesManager { * org.sleuthkit.autopsy.modules.filetypeid.UserDefinedFileTypesManager.UserDefinedFileTypesException */ private void loadPredefinedFileTypes() throws UserDefinedFileTypesException { - byte[] byteArray = null; - FileType fileType = null; + byte[] byteArray; + FileType fileType; // Add rule for xml byteArray = DatatypeConverter.parseHexBinary("3C3F786D6C"); fileType = new FileType("text/xml", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); - - // Add rule for aiff - byteArray = DatatypeConverter.parseHexBinary("464F524D00"); - fileType = new FileType("audio/x-aiff", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + + // Add rule for .wk1 + byteArray = DatatypeConverter.parseHexBinary("0000020006040600080000000000"); + fileType = new FileType("application/x-123", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .cur + byteArray = DatatypeConverter.parseHexBinary("00000200"); + fileType = new FileType("image/x-icon", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for Radiance image + byteArray = DatatypeConverter.parseHexBinary("233F52414449414E43450A"); + fileType = new FileType("image/vnd.radiance", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .dcx image + byteArray = DatatypeConverter.parseHexBinary("B168DE3A"); + fileType = new FileType("image/x-dcx", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .ics image + byteArray = DatatypeConverter.parseHexBinary("69636E73"); + fileType = new FileType("image/x-icns", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .pict image + byteArray = DatatypeConverter.parseHexBinary("001102FF"); + fileType = new FileType("image/x-pict", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); } From bc945c03c31c33defe6361fbee60effa5d3cc614 Mon Sep 17 00:00:00 2001 From: momo Date: Wed, 14 Oct 2015 10:34:53 -0400 Subject: [PATCH 04/48] add pfm and pam --- .../filetypeid/UserDefinedFileTypesManager.java | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java index 1dd1ee989d..e1dbee08ec 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java @@ -182,11 +182,12 @@ final class UserDefinedFileTypesManager { private void loadPredefinedFileTypes() throws UserDefinedFileTypesException { byte[] byteArray; FileType fileType; + // Add rule for xml byteArray = DatatypeConverter.parseHexBinary("3C3F786D6C"); fileType = new FileType("text/xml", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); - + // Add rule for .wk1 byteArray = DatatypeConverter.parseHexBinary("0000020006040600080000000000"); fileType = new FileType("application/x-123", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS @@ -204,7 +205,7 @@ final class UserDefinedFileTypesManager { // Add rule for .dcx image byteArray = DatatypeConverter.parseHexBinary("B168DE3A"); - fileType = new FileType("image/x-dcx", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileType = new FileType("image/x-dcx", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); // Add rule for .ics image @@ -214,7 +215,17 @@ final class UserDefinedFileTypesManager { // Add rule for .pict image byteArray = DatatypeConverter.parseHexBinary("001102FF"); - fileType = new FileType("image/x-pict", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileType = new FileType("image/x-pict", new Signature(byteArray, 522L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .pam + byteArray = DatatypeConverter.parseHexBinary("P7"); + fileType = new FileType("image/x-portable-arbitrarymap", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS + fileTypes.put(fileType.getMimeType(), fileType); + + // Add rule for .pfm + byteArray = DatatypeConverter.parseHexBinary("PF"); + fileType = new FileType("image/x-portable-floatmap", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); } From 89a3f6b5fa14ba7f74f6e581ebe3284eb5eff9e6 Mon Sep 17 00:00:00 2001 From: momo Date: Fri, 16 Oct 2015 11:11:33 -0400 Subject: [PATCH 05/48] add more details for intillij --- docs/doxygen/modDevPython.dox | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/docs/doxygen/modDevPython.dox b/docs/doxygen/modDevPython.dox index 272df945de..45c3fc30a2 100755 --- a/docs/doxygen/modDevPython.dox +++ b/docs/doxygen/modDevPython.dox @@ -26,17 +26,25 @@ There are also a set of tutorials that Basis Technology published on their blog: -You don't really need anything to develop a python Autopsy module except for the standard Autopsy and your favorite text editor. We recommend IntelliJ IDEA or the Jython plug-in to NetBeans. To install NetBeans' plug-in: --# Download and install the Jython 2.7 installer (http://www.jython.org/downloads.html). +-# Download and install the Jython 2.7 installer to desired location (http://www.jython.org/downloads.html). -# Download NetBeans Python plug-in zip file (http://plugins.netbeans.org/plugin/56795/python4netbeans802). -# Unpack the content (.nbm files) of the zip file to the desired location. --# In NetBeans go to Tools->Plugins. In Download tab, click on Add Plugins, then choose extracted .nbm files. --# Setup Jython path from Tools->Python Platform, click on new, then choose Jython.exe (usually in C:/Program files/Jython2.7/bin/) +-# In NetBeans go to Tools->Plugins. In Downloaded tab, click on Add Plugins, then choose extracted .nbm files. +-# Setup Jython path from Tools->Python Platforms, click on new, then choose Jython.exe (usually in C:/Program files/Jython2.7/bin/) To install IntelliJ IDEA + Python plug-in: --# Download and install IDEA https://www.jetbrains.com/idea/download/ --# In File->Settings->Plugins-> install Python Community Edition --# In File->Project Structure->Project-> Project SDK-> choose IntelliJ IDEA Community Edition --# In Libraries->add new libraries->choose desired autopsy modules (usually in C:\Program Files\Autopsy-3.1.3\autopsy\modules) +-# Download java JDK depending on platform. Install to desired location (http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html). +-# Download and install IDEA Community Edition to desired location (https://www.jetbrains.com/idea/download/). +-# Open IDEA and choose desired UI theme. Continue with default settings. +-# Choose to either create a new empty project or open an existing one. +-# It will ask you to modify Project Structure. Leave that for now and click OK. +-# In File->Settings. Go to Plugins tab and click on Install JetBrains Plugin. +-# Look for and install Python Community Edition. After the installation, it will ask you restart. Restart IDEA. +-# In File->Project Structure. In Project tab, Project SDK, click on New and choose IntelliJ Platform Plugin SKD. +-# It will ask you to configure the JKD first, click OK and navigate to the JDK folder location and click OK. +-# After that it will ask you to choose the IntelliJ Platform Plugin SKD. It will most likely take you to it's locaation automatically. (Usually in C:\Program Files (x86)\JetBrains\IntelliJ IDEA Community Edition 14.1.5) +-# In the drop down menu next to New button, choose IntelliJ IDEA Community Edition. +-# Still in Project STructure, In Libraries tab, click on '+' to add new libraries. Choose desired autopsy modules (usually in C:\Program Files\Autopsy-3.1.3\autopsy\modules if you have executable version). \section mod_dev_py_create Creating a Basic Python Module From 5aab86ae29689b778b1f71ef95ecafde5b85eb14 Mon Sep 17 00:00:00 2001 From: momo Date: Fri, 16 Oct 2015 12:52:57 -0400 Subject: [PATCH 06/48] remove 08, for deflate compression, from gzip sig --- .../autopsy/modules/filetypeid/UserDefinedFileTypesManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java index 4ec5797d5f..275c9ec0bd 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java +++ b/Core/src/org/sleuthkit/autopsy/modules/filetypeid/UserDefinedFileTypesManager.java @@ -189,7 +189,7 @@ final class UserDefinedFileTypesManager { fileTypes.put(fileType.getMimeType(), fileType); // Add rule for gzip - byteArray = DatatypeConverter.parseHexBinary("1F8B08"); + byteArray = DatatypeConverter.parseHexBinary("1F8B"); fileType = new FileType("application/x-gzip", new Signature(byteArray, 0L, FileType.Signature.Type.ASCII), "", false); //NON-NLS fileTypes.put(fileType.getMimeType(), fileType); From 84a31f37386b3e34d23ec2cb7285e439c0cf0870 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 2 Oct 2015 17:11:15 -0400 Subject: [PATCH 07/48] 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); } From 49965d73a21ea3ff134027c3dd6a4f5050b14a8a Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 14 Oct 2015 16:36:12 -0400 Subject: [PATCH 08/48] refacto select interval action to separate inner class --- .../autopsy/timeline/images/select.png | Bin 0 -> 800 bytes .../autopsy/timeline/ui/TimeLineChart.java | 116 +++++++++++------- .../timeline/ui/countsview/Bundle.properties | 2 +- .../ui/countsview/CountsViewPane.java | 2 +- .../ui/countsview/EventCountsChart.java | 24 +--- .../ui/detailview/EventDetailChart.java | 25 ++-- 6 files changed, 88 insertions(+), 81 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/images/select.png diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/select.png b/Core/src/org/sleuthkit/autopsy/timeline/images/select.png new file mode 100644 index 0000000000000000000000000000000000000000..a8c5583a7f2ab624208bbb34d0167372e2f7ca51 GIT binary patch literal 800 zcmV+*1K<3KP)ch;KjopjQ@c#-aLz~Bt~AfG*z3J7$5N_i6ja_I9Q)}QHTlUAgI_D zOiL>bD#e!F@i)za8XpMVCDHKPd^0nPWGnwBM;9H0-EiD8~bCIP8tBj?At*CMx zWL~WSv)0k7)r*mJEfkg!Lh)f<3kCC9(djjr4FVek{x1SvulL_2U@bNmcsw3*(qcZBho;5`IS{!YPDY{1;Q;4Or`+N5|I&p11XO|s2nd1zf+#@rT@2hV z7dRbNa$AS*Clk6GptGmq1->9o;9zQc8fx4wa5yUE*7nXHOc-K7Q4|n(0eFsIRyzFw zYTdP9u~_7Dt?kPu3?iTa&vOL{NI=A;kR%auxi4_2sS(P`wo(&4_;a2&gaGaVNs=I+ z&lmQ>+}ua#?d>bnS(XKkqsGL;^vE{E0E5r`{5;$bhoQQ<8tisER95bQ?(SRO-n(~( zv?DrASAaF*ILu^bq3>QlOpH$q4iAqknoTCCsIZ}#JxB5LD^vs(LuGY0z%UF93=Bdl zHS;hOy3iJn#~-~ the type of values along the horizontal axis */ public interface TimeLineChart extends TimeLineView { - + IntervalSelector getIntervalSelector(); - + void setIntervalSelector(IntervalSelector newIntervalSelector); /** @@ -71,50 +78,51 @@ public interface TimeLineChart extends TimeLineView { * @param the type of chart this is a drag handler for */ static class ChartDragHandler> implements EventHandler { - + private final Y chart; - + private final Axis dateAxis; - + 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 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); + chart.setCursor(Cursor.H_RESIZE); + mouseEvent.consume(); } 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(); - } - }); - } + IntervalSelector intervalSelector = chart.getIntervalSelector(); + if (intervalSelector != null) { + intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); + } + }); + } startX = mouseEvent.getX(); } else { //resize/position existing selector @@ -126,34 +134,46 @@ public interface TimeLineChart extends TimeLineView { chart.getIntervalSelector().setWidth(startX - mouseEvent.getX()); } } + mouseEvent.consume(); } else if (mouseEventType == MouseEvent.MOUSE_RELEASED) { -// chart.setCursor(Cursor.DEFAULT); + 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(); - } - }); - } + chart.setOnMouseMoved(null); + mouseEvent.consume(); } else if (mouseEventType == MouseEvent.MOUSE_CLICKED) { -// chart.setCursor(Cursor.DEFAULT); + 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(); - } - }); - } + chart.setOnMouseMoved(null); + mouseEvent.consume(); } -// mouseEvent.consume(); + } } + + public static class StartIntervalSelectionAction extends Action { + + private static final Image SELECT_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/select.png", 16, 16, true, true, true); + + /** + * @param clickEvent the value of clickEvent + * @param dragHandler the value of dragHandler + */ + public StartIntervalSelectionAction(MouseEvent clickEvent, final ChartDragHandler dragHandler) { + super("Select Interval"); + setGraphic(new ImageView(SELECT_ICON)); + setEventHandler((ActionEvent t) -> { + dragHandler.setRequireDrag(false); + dragHandler.handle(clickEvent.copyFor(clickEvent.getSource(), clickEvent.getTarget(), MouseEvent.MOUSE_DRAGGED)); + }); + } + } + + @NbBundle.Messages({"TimeLineChart.zoomHistoryActionGroup.name=Zoom History"}) + static ActionGroup newZoomHistoyActionGroup(TimeLineController controller) { + return new ActionGroup(Bundle.TimeLineChart_zoomHistoryActionGroup_name(), + new Back(controller), + new Forward(controller)); + } /** * Visually represents a 'selected' time range, and allows mouse @@ -165,16 +185,16 @@ public interface TimeLineChart extends TimeLineView { * methods to handle formating and date 'lookup' of the generic x-axis type */ static abstract class IntervalSelector extends Rectangle { - + private static final double STROKE_WIDTH = 3; - + private static final double HALF_STROKE = STROKE_WIDTH / 2; /** * the Axis this is a selector over */ private final Axis dateAxis; - + private Tooltip tooltip; /////////drag state @@ -206,7 +226,7 @@ public interface TimeLineChart extends TimeLineView { setTooltip(); }); setTooltip(); - + setOnMouseMoved((MouseEvent event) -> { Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); final double diffX = getX() - localMouse.getX(); @@ -245,10 +265,11 @@ public interface TimeLineChart extends TimeLineView { setWidth(startWidth + dX); break; } + event.consume(); }); //have to add handler rather than use convenience methods so that charts can listen for dismisal click addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { - + public void handle(MouseEvent event) { if (event.getClickCount() >= 2) { //convert to DateTimes, using max/min if null(off axis) @@ -256,6 +277,7 @@ public interface TimeLineChart extends TimeLineView { DateTime end = parseDateTime(getSpanEnd()); Interval i = adjustInterval(start.isBefore(end) ? new Interval(start, end) : new Interval(end, start)); controller.pushTimeRange(i); + event.consume(); } } }); @@ -288,7 +310,7 @@ public interface TimeLineChart extends TimeLineView { * @return a {@link DateTime} corresponding to the given x-axis value */ 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."}) @@ -322,7 +344,7 @@ public interface TimeLineChart extends TimeLineView { * or a horizontal slide triggered by dragging the center */ private enum DragPosition { - + 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 640949cfcd..7048132d41 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/Bundle.properties @@ -26,12 +26,12 @@ Timeline.ui.countsview.menuItem.selectTimeRange=Select Time Range Timeline.ui.countsview.menuItem.selectEventType=Select Event Type Timeline.ui.countsview.menuItem.selectTimeandType=Select Time and Type Timeline.ui.countsview.menuItem.zoomIntoTimeRange=Zoom into Time Range -Timeline.ui.countsview.contextMenu.ActionGroup.zoomHistory.title=Zoom History CountsViewPane.loggedTask.name=Updating Counts Graph CountsViewPane.loggedTask.prepUpdate=preparing update 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 +CountsViewPane.scaleLabel.text=Scale\: CountsViewPane.logRadio.text=Logarithmic CountsViewPane.linearRadio.text=Linear diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 76f89462fc..7127f3b6b6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -534,7 +534,7 @@ public class CountsViewPane extends AbstractVisualization implements setOnMousePressed(dragHandler); setOnMouseReleased(dragHandler); setOnMouseDragged(dragHandler); - setOnMouseMoved(dragHandler); setOnMouseClicked((MouseEvent clickEvent) -> { if (chartContextMenu != null) { @@ -95,6 +91,7 @@ final class EventCountsChart extends StackedBarChart implements } if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) { getChartContextMenu(clickEvent); + setOnMouseMoved(dragHandler); chartContextMenu.show(EventCountsChart.this, clickEvent.getScreenX(), clickEvent.getScreenY()); clickEvent.consume(); } @@ -113,20 +110,10 @@ final class EventCountsChart extends StackedBarChart implements 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 = ActionUtils.createContextMenu( + Arrays.asList( + new StartIntervalSelectionAction(clickEvent, dragHandler), + TimeLineChart.newZoomHistoyActionGroup(controller))); chartContextMenu.setAutoHide(true); return chartContextMenu; } @@ -226,4 +213,5 @@ final class EventCountsChart extends StackedBarChart implements return date == null ? new DateTime(rangeInfo.getLowerBound()) : rangeInfo.getTickFormatter().parseDateTime(date); } } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java index 1114cf6a72..e8845bfae0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java @@ -62,14 +62,11 @@ import javafx.scene.shape.StrokeLineCap; import javafx.util.Duration; import org.apache.commons.lang3.tuple.ImmutablePair; import org.controlsfx.control.action.Action; -import org.controlsfx.control.action.ActionGroup; import org.controlsfx.control.action.ActionUtils; import org.joda.time.DateTime; import org.joda.time.Interval; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.actions.Back; -import org.sleuthkit.autopsy.timeline.actions.Forward; import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; @@ -182,6 +179,7 @@ public final class EventDetailChart extends XYChart impl * via slider if truncateAll is true */ final SimpleDoubleProperty truncateWidth = new SimpleDoubleProperty(200.0); + private final ChartDragHandler dragHandler; EventDetailChart(DateAxis dateAxis, final Axis verticalAxis, ObservableList> selectedNodes) { super(dateAxis, verticalAxis); @@ -211,22 +209,23 @@ public final class EventDetailChart extends XYChart impl setPrefHeight(boundsInLocalProperty().get().getHeight()); }); - ///////set up mouse listeners + //use one handler with an if chain because it maintains state + dragHandler = new ChartDragHandler<>(this, getXAxis()); + setOnMousePressed(dragHandler); + setOnMouseReleased(dragHandler); + setOnMouseDragged(dragHandler); + setOnMouseClicked((MouseEvent clickEvent) -> { if (chartContextMenu != null) { chartContextMenu.hide(); } if (clickEvent.getButton() == MouseButton.SECONDARY && clickEvent.isStillSincePress()) { getChartContextMenu(clickEvent); + setOnMouseMoved(dragHandler); chartContextMenu.show(EventDetailChart.this, clickEvent.getScreenX(), clickEvent.getScreenY()); clickEvent.consume(); } }); - //use one handler with an if chain because it maintains state - final ChartDragHandler dragHandler = new ChartDragHandler<>(this, getXAxis()); - setOnMousePressed(dragHandler); - setOnMouseReleased(dragHandler); - setOnMouseDragged(dragHandler); this.selectedNodes = selectedNodes; this.selectedNodes.addListener(new SelectionChangeHandler()); @@ -240,17 +239,14 @@ public final class EventDetailChart extends XYChart impl return controller; } - @NbBundle.Messages({"EventDetailChart.chartContextMenu.placeMarker.name=Place Marker", - "EventDetailChart.contextMenu.zoomHistory.name=Zoom History"}) ContextMenu getChartContextMenu(MouseEvent clickEvent) throws MissingResourceException { if (chartContextMenu != null) { chartContextMenu.hide(); } chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new PlaceMarkerAction(clickEvent), - new ActionGroup(Bundle.EventDetailChart_contextMenu_zoomHistory_name(), - new Back(controller), - new Forward(controller)))); +// new StartIntervalSelectionAction(clickEvent, dragHandler), + TimeLineChart.newZoomHistoyActionGroup(controller))); chartContextMenu.setAutoHide(true); return chartContextMenu; } @@ -608,6 +604,7 @@ public final class EventDetailChart extends XYChart impl private class PlaceMarkerAction extends Action { + @NbBundle.Messages({"EventDetailChart.chartContextMenu.placeMarker.name=Place Marker"}) PlaceMarkerAction(MouseEvent clickEvent) { super(Bundle.EventDetailChart_chartContextMenu_placeMarker_name()); From b31e172f13e7aece063e5f385e9cd68b338aabcd Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 14 Oct 2015 17:00:27 -0400 Subject: [PATCH 09/48] change interval selector to BorderPane --- .../autopsy/timeline/ui/IntervalSelector.css | 13 + .../autopsy/timeline/ui/IntervalSelector.fxml | 41 ++++ .../autopsy/timeline/ui/IntervalSelector.java | 211 ++++++++++++++++ .../autopsy/timeline/ui/TimeLineChart.java | 226 +++--------------- .../ui/countsview/EventCountsChart.java | 1 + .../ui/detailview/EventDetailChart.java | 1 + 6 files changed, 299 insertions(+), 194 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css new file mode 100644 index 0000000000..15da7a3f51 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css @@ -0,0 +1,13 @@ +.intervalSelector{ + -fx-background-color: rgba(0,0,255,.5); + -fx-border-color: rgba(0,0,255,.5); + -fx-border-width: 3; +} + +.closeButton:hover{ + -fx-opacity: 1; +} + +.zoomButton:hover{ + -fx-opacity: 1; +} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml new file mode 100644 index 0000000000..82ad81021c --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + +
+ +
+
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java new file mode 100644 index 0000000000..ef99f040aa --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java @@ -0,0 +1,211 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui; + +import javafx.event.EventHandler; +import javafx.geometry.Insets; +import javafx.geometry.Point2D; +import javafx.scene.Cursor; +import javafx.scene.chart.Axis; +import javafx.scene.control.Tooltip; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.Border; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.BorderStroke; +import javafx.scene.layout.BorderStrokeStyle; +import javafx.scene.layout.BorderWidths; +import javafx.scene.layout.CornerRadii; +import javafx.scene.paint.Color; +import org.joda.time.DateTime; +import org.joda.time.Interval; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.timeline.FXMLConstructor; +import org.sleuthkit.autopsy.timeline.TimeLineController; + +/** + * Visually represents a 'selected' time range, and allows mouse interactions + * with it. + * + * @param the type of values along the x axis this is a selector for + * + * This abstract class requires concrete implementations to implement hook + * methods to handle formating and date 'lookup' of the generic x-axis type + */ +public abstract class IntervalSelector extends BorderPane { + + private static final double STROKE_WIDTH = 3; + private static final double HALF_STROKE = STROKE_WIDTH / 2; + /** + * the Axis this is a selector over + */ + private final Axis dateAxis; + private Tooltip tooltip; + /////////drag state + private DragPosition dragPosition; + private double startLeft; + private double startX; + private double startWidth; + /////////end drag state + private TimeLineController controller; + + public IntervalSelector(Axis dateAxis, TimeLineController controller) { + this.dateAxis = dateAxis; + this.controller = controller; + FXMLConstructor.construct(this, "TimeZonePanel.fxml"); // NON-NLS + + } + + + + /** + * + * @param x the initial x position of this selector + * @param height the initial height of this selector + * @param axis the {@link Axis} this is a selector over + * @param controller the controller to invoke when this selector is double + * clicked + */ + public IntervalSelector(double x, double height, Axis axis, TimeLineController controller) { + + setMaxHeight(USE_PREF_SIZE); + setMinHeight(USE_PREF_SIZE); + setMaxWidth(USE_PREF_SIZE); + setMinWidth(USE_PREF_SIZE); + dateAxis = axis; + setBorder(new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(STROKE_WIDTH)))); + // setStroke(Color.BLUE); + // setStrokeWidth(STROKE_WIDTH); + setBackground(new Background(new BackgroundFill(Color.BLUE.deriveColor(0, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY))); + setOpacity(0.5); + widthProperty().addListener((o) -> { + setTooltip(); + }); + layoutXProperty().addListener((o) -> { + setTooltip(); + }); + setTooltip(); + setOnMouseMoved((MouseEvent event) -> { + Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + final double diffX = getLayoutX() - localMouse.getX(); + if (Math.abs(diffX) <= HALF_STROKE || Math.abs(diffX + getWidth()) <= HALF_STROKE) { + //if the mouse is over the stroke, show the resize cursor + setCursor(Cursor.H_RESIZE); + } else { + setCursor(Cursor.HAND); + } + }); + setOnMousePressed((MouseEvent event) -> { + Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + final double diffX = getLayoutX() - localMouse.getX(); + startX = event.getX(); + startWidth = getWidth(); + startLeft = getLayoutX(); + if (Math.abs(diffX) <= HALF_STROKE) { + dragPosition = IntervalSelector.DragPosition.LEFT; + } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) { + dragPosition = IntervalSelector.DragPosition.RIGHT; + } else { + dragPosition = IntervalSelector.DragPosition.CENTER; + } + }); + setOnMouseDragged((MouseEvent event) -> { + double dX = event.getX() - startX; + switch (dragPosition) { + case CENTER: + relocate(startLeft + dX, 0); + break; + case LEFT: + relocate(startLeft + dX, 0); + setPrefWidth(startWidth - dX); + break; + case RIGHT: + setPrefWidth(startWidth + dX); + break; + } + event.consume(); + }); + //have to add handler rather than use convenience methods so that charts can listen for dismisal click + addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { + public void handle(MouseEvent event) { + if (event.getClickCount() >= 2) { + //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); + event.consume(); + } + } + }); + } + + /** + * + * @param i the interval represented by this selector + * + * @return a modified version of {@code i} adjusted to suite the needs of + * the concrete implementation + */ + protected abstract Interval adjustInterval(Interval i); + + /** + * format a string representation of the given x-axis value to use in the + * tooltip + * + * @param date a x-axis value of type X + * + * @return a string representation of the given x-axis value + */ + protected abstract String formatSpan(final X date); + + /** + * parse an x-axis value to a {@link DateTime} + * + * @param date a x-axis value of type X + * + * @return a {@link DateTime} corresponding to the given x-axis value + */ + protected abstract DateTime parseDateTime(X date); + + @NbBundle.Messages(value = {"# {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(Bundle.Timeline_ui_TimeLineChart_tooltip_text(formatSpan(start), formatSpan(end))); + Tooltip.install(this, tooltip); + } + + /** + * @return the value along the x-axis corresponding to the left edge of the + * selector + */ + public X getSpanEnd() { + return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMaxX(), 0).getX()); + } + + /** + * @return the value along the x-axis corresponding to the right edge of the + * selector + */ + public X getSpanStart() { + return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMinX(), 0).getX()); + } + + /** + * enum to represent whether the drag is a left/right-edge modification or a + * horizontal slide triggered by dragging the center + */ + private enum DragPosition { + + LEFT, + CENTER, + RIGHT + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java index 185a97689e..02e8f4e006 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.timeline.ui; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.event.EventType; +import javafx.geometry.Insets; import javafx.geometry.Point2D; import javafx.scene.Cursor; import javafx.scene.chart.Axis; @@ -30,8 +31,15 @@ import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.Border; +import javafx.scene.layout.BorderStroke; +import javafx.scene.layout.BorderStrokeStyle; +import javafx.scene.layout.BorderWidths; +import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; -import javafx.scene.shape.Rectangle; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionGroup; import org.joda.time.DateTime; @@ -48,9 +56,9 @@ import org.sleuthkit.autopsy.timeline.actions.Forward; * @param the type of values along the horizontal axis */ public interface TimeLineChart extends TimeLineView { - + IntervalSelector getIntervalSelector(); - + void setIntervalSelector(IntervalSelector newIntervalSelector); /** @@ -78,28 +86,28 @@ public interface TimeLineChart extends TimeLineView { * @param the type of chart this is a drag handler for */ static class ChartDragHandler> implements EventHandler { - + private final Y chart; - + private final Axis dateAxis; - + 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 mouseEvent) { EventType mouseEventType = mouseEvent.getEventType(); @@ -113,7 +121,7 @@ public interface TimeLineChart extends TimeLineView { 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()))); + chart.getIntervalSelector().prefHeightProperty().bind(chart.heightProperty()); IntervalSelector intervalSelector = chart.getIntervalSelector(); if (intervalSelector != null) { intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { @@ -127,11 +135,11 @@ public interface TimeLineChart extends TimeLineView { } else { //resize/position existing selector if (mouseEvent.getX() > startX) { - chart.getIntervalSelector().setX(startX); - chart.getIntervalSelector().setWidth(mouseEvent.getX() - startX); +// chart.getIntervalSelector().relocate(startX, 0); + chart.getIntervalSelector().setPrefWidth(mouseEvent.getX() - startX); } else { - chart.getIntervalSelector().setX(mouseEvent.getX()); - chart.getIntervalSelector().setWidth(startX - mouseEvent.getX()); + chart.getIntervalSelector().relocate(mouseEvent.getX(), 0); + chart.getIntervalSelector().setPrefWidth(startX - mouseEvent.getX()); } } mouseEvent.consume(); @@ -146,12 +154,17 @@ public interface TimeLineChart extends TimeLineView { chart.setOnMouseMoved(null); mouseEvent.consume(); } - + + if (chart.getIntervalSelector() != null) { + chart.getIntervalSelector().autosize(); + System.out.println(chart.getIntervalSelector().getBoundsInLocal()); + + } } } - + public static class StartIntervalSelectionAction extends Action { - + private static final Image SELECT_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/select.png", 16, 16, true, true, true); /** @@ -167,7 +180,7 @@ public interface TimeLineChart extends TimeLineView { }); } } - + @NbBundle.Messages({"TimeLineChart.zoomHistoryActionGroup.name=Zoom History"}) static ActionGroup newZoomHistoyActionGroup(TimeLineController controller) { return new ActionGroup(Bundle.TimeLineChart_zoomHistoryActionGroup_name(), @@ -175,179 +188,4 @@ public interface TimeLineChart extends TimeLineView { new Forward(controller)); } - /** - * Visually represents a 'selected' time range, and allows mouse - * interactions with it. - * - * @param the type of values along the x axis this is a selector for - * - * This abstract class requires concrete implementations to implement hook - * methods to handle formating and date 'lookup' of the generic x-axis type - */ - static abstract class IntervalSelector extends Rectangle { - - private static final double STROKE_WIDTH = 3; - - private static final double HALF_STROKE = STROKE_WIDTH / 2; - - /** - * the Axis this is a selector over - */ - private final Axis dateAxis; - - private Tooltip tooltip; - - /////////drag state - private DragPosition dragPosition; - private double startLeft; - private double startX; - private double startWidth; - /////////end drag state - - /** - * - * @param x the initial x position of this selector - * @param height the initial height of this selector - * @param axis the {@link Axis} this is a selector over - * @param controller the controller to invoke when this selector is - * double clicked - */ - public IntervalSelector(double x, double height, Axis axis, TimeLineController controller) { - super(x, 0, x, height); - dateAxis = axis; - setStroke(Color.BLUE); - setStrokeWidth(STROKE_WIDTH); - setFill(Color.BLUE.deriveColor(0, 1, 1, 0.5)); - setOpacity(0.5); - widthProperty().addListener(o -> { - setTooltip(); - }); - xProperty().addListener(o -> { - setTooltip(); - }); - setTooltip(); - - setOnMouseMoved((MouseEvent event) -> { - 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) { - //if the mouse is over the stroke, show the resize cursor - setCursor(Cursor.H_RESIZE); - } else { - setCursor(Cursor.HAND); - } - }); - setOnMousePressed((MouseEvent event) -> { - Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); - final double diffX = getX() - localMouse.getX(); - startX = event.getX(); - startWidth = getWidth(); - startLeft = getX(); - if (Math.abs(diffX) <= HALF_STROKE) { - dragPosition = IntervalSelector.DragPosition.LEFT; - } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) { - dragPosition = IntervalSelector.DragPosition.RIGHT; - } else { - dragPosition = IntervalSelector.DragPosition.CENTER; - } - }); - setOnMouseDragged((MouseEvent event) -> { - double dX = event.getX() - startX; - switch (dragPosition) { - case CENTER: - setX(startLeft + dX); - break; - case LEFT: - setX(startLeft + dX); - setWidth(startWidth - dX); - break; - case RIGHT: - setWidth(startWidth + dX); - break; - } - event.consume(); - }); - //have to add handler rather than use convenience methods so that charts can listen for dismisal click - addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { - - public void handle(MouseEvent event) { - if (event.getClickCount() >= 2) { - //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); - event.consume(); - } - } - }); - } - - /** - * - * @param i the interval represented by this selector - * - * @return a modified version of {@code i} adjusted to suite the needs - * of the concrete implementation - */ - protected abstract Interval adjustInterval(Interval i); - - /** - * format a string representation of the given x-axis value to use in - * the tooltip - * - * @param date a x-axis value of type X - * - * @return a string representation of the given x-axis value - */ - protected abstract String formatSpan(final X date); - - /** - * parse an x-axis value to a {@link DateTime} - * - * @param date a x-axis value of type X - * - * @return a {@link DateTime} corresponding to the given x-axis value - */ - 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( - Bundle.Timeline_ui_TimeLineChart_tooltip_text(formatSpan(start), formatSpan(end))); - Tooltip.install(this, tooltip); - } - - /** - * @return the value along the x-axis corresponding to the left edge of - * the selector - */ - public X getSpanEnd() { - return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMaxX(), 0).getX()); - } - - /** - * @return the value along the x-axis corresponding to the right edge of - * the selector - */ - public X getSpanStart() { - return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMinX(), 0).getX()); - } - - /** - * enum to represent whether the drag is a left/right-edge modification - * or a horizontal slide triggered by dragging the center - */ - private enum DragPosition { - - LEFT, - CENTER, - RIGHT - } - } } 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 0073870a94..db23e22b3a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java @@ -35,6 +35,7 @@ import org.joda.time.Interval; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.ui.IntervalSelector; import org.sleuthkit.autopsy.timeline.ui.TimeLineChart; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java index e8845bfae0..cb948559ec 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java @@ -74,6 +74,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; +import org.sleuthkit.autopsy.timeline.ui.IntervalSelector; import org.sleuthkit.autopsy.timeline.ui.TimeLineChart; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; From 668a3353197a4da38de662c08aba6c833ee9a306 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 15 Oct 2015 15:39:53 -0400 Subject: [PATCH 10/48] make IntervalSelector more of a first class citizen, and enhance its UX --- .../autopsy/timeline/FXMLConstructor.java | 16 +- .../autopsy/timeline/images/cross-script.png | Bin 0 -> 623 bytes .../autopsy/timeline/ui/IntervalSelector.css | 10 +- .../autopsy/timeline/ui/IntervalSelector.fxml | 40 ++-- .../autopsy/timeline/ui/IntervalSelector.java | 212 +++++++++++------- .../autopsy/timeline/ui/TimeLineChart.java | 51 +---- .../ui/countsview/EventCountsChart.java | 23 +- .../ui/detailview/EventDetailChart.java | 13 +- 8 files changed, 212 insertions(+), 153 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/images/cross-script.png diff --git a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java index 0c90af7f27..fd9ab26951 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/FXMLConstructor.java @@ -60,7 +60,21 @@ public class FXMLConstructor { */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) static public void construct(Node node, String fxmlFileName) { - final String name = "nbres:/" + StringUtils.replace(node.getClass().getPackage().getName(), ".", "/") + "/" + fxmlFileName; // NON-NLS + construct(node, node.getClass(), fxmlFileName); + } + + /** + * Load an fxml file and initialize a node with it. Since this manipulates + * the node, it must be called on the JFX thread. + * + * + * @param node a node to initialize from a loaded FXML + * @param fxmlFileName the the file name of the FXML to load, relative to + * the package that the class of node is defined in. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + static public void construct(Node node, Class clazz, String fxmlFileName) { + final String name = "nbres:/" + StringUtils.replace(clazz.getPackage().getName(), ".", "/") + "/" + fxmlFileName; // NON-NLS try { FXMLLoader fxmlLoader = new FXMLLoader(new URL(name)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/cross-script.png b/Core/src/org/sleuthkit/autopsy/timeline/images/cross-script.png new file mode 100644 index 0000000000000000000000000000000000000000..f37cf4183046203c9d77c03ea46c672084ae3dbd GIT binary patch literal 623 zcmV-#0+9WQP)uz-;<4aiE#*F2T#| zt;WP8_2kp17jJ+5TnKdXT%g9FN8i3}lmYYqfHOcjj~~!Ph-`p^4LiH!rC-0^KL7i72TcG6TZ`7-bKTQXvCc;3)#dB$oaEf1Gi^b0a{20RY5Z;(@&ISGfQH002ov JPDHLkV1iqyA&vk5 literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css index 15da7a3f51..d1eb403eae 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css @@ -1,7 +1,11 @@ .intervalSelector{ - -fx-background-color: rgba(0,0,255,.5); - -fx-border-color: rgba(0,0,255,.5); - -fx-border-width: 3; + -fx-background-color: rgba(0,0,255,.25); + -fx-border-color: rgba(0,0,255,.25); + -fx-border-width: 0 3 0 3; +} + +.closeButton{ + -fx-background-color: transparent; } .closeButton:hover{ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml index 82ad81021c..ac279f7c81 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml @@ -7,18 +7,12 @@ - - - - - - + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java index ef99f040aa..025d8c4e04 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java @@ -1,26 +1,37 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2014-15 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.timeline.ui; -import javafx.event.EventHandler; -import javafx.geometry.Insets; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; import javafx.geometry.Point2D; import javafx.scene.Cursor; -import javafx.scene.chart.Axis; +import javafx.scene.control.Button; +import javafx.scene.control.Label; import javafx.scene.control.Tooltip; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.Border; import javafx.scene.layout.BorderPane; -import javafx.scene.layout.BorderStroke; -import javafx.scene.layout.BorderStrokeStyle; -import javafx.scene.layout.BorderWidths; -import javafx.scene.layout.CornerRadii; -import javafx.scene.paint.Color; +import org.controlsfx.control.action.Action; +import org.controlsfx.control.action.ActionUtils; import org.joda.time.DateTime; import org.joda.time.Interval; import org.openide.util.NbBundle; @@ -33,76 +44,84 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; * * @param the type of values along the x axis this is a selector for * - * This abstract class requires concrete implementations to implement hook + * This abstract class requires concrete implementations to implement template * methods to handle formating and date 'lookup' of the generic x-axis type */ public abstract class IntervalSelector extends BorderPane { + private static final Image ClEAR_INTERVAL_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/cross-script.png", 16, 16, true, true, true); + private static final Image ZOOM_TO_INTERVAL_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/magnifier-zoom-fit.png", 16, 16, true, true, true); private static final double STROKE_WIDTH = 3; private static final double HALF_STROKE = STROKE_WIDTH / 2; + /** * the Axis this is a selector over */ - private final Axis dateAxis; + public final TimeLineChart chart; + private Tooltip tooltip; /////////drag state private DragPosition dragPosition; private double startLeft; - private double startX; + private double startDragX; private double startWidth; /////////end drag state - private TimeLineController controller; + private final TimeLineController controller; - public IntervalSelector(Axis dateAxis, TimeLineController controller) { - this.dateAxis = dateAxis; - this.controller = controller; - FXMLConstructor.construct(this, "TimeZonePanel.fxml"); // NON-NLS + @FXML + private Label startLabel; + @FXML + private Label endLabel; + + @FXML + private Button closeButton; + + @FXML + private Button zoomButton; + + public IntervalSelector(TimeLineChart chart) { + this.chart = chart; + this.controller = chart.getController(); + FXMLConstructor.construct(this, IntervalSelector.class, "IntervalSelector.fxml"); // NON-NLS } - - - /** - * - * @param x the initial x position of this selector - * @param height the initial height of this selector - * @param axis the {@link Axis} this is a selector over - * @param controller the controller to invoke when this selector is double - * clicked - */ - public IntervalSelector(double x, double height, Axis axis, TimeLineController controller) { - + @FXML + void initialize() { + assert startLabel != null : "fx:id=\"startLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'."; + assert endLabel != null : "fx:id=\"endLabel\" was not injected: check your FXML file 'IntervalSelector.fxml'."; + assert closeButton != null : "fx:id=\"closeButton\" was not injected: check your FXML file 'IntervalSelector.fxml'."; + assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'IntervalSelector.fxml'."; + setMaxHeight(USE_PREF_SIZE); setMinHeight(USE_PREF_SIZE); setMaxWidth(USE_PREF_SIZE); setMinWidth(USE_PREF_SIZE); - dateAxis = axis; - setBorder(new Border(new BorderStroke(Color.BLUE, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(STROKE_WIDTH)))); - // setStroke(Color.BLUE); - // setStrokeWidth(STROKE_WIDTH); - setBackground(new Background(new BackgroundFill(Color.BLUE.deriveColor(0, 1, 1, 0.5), CornerRadii.EMPTY, Insets.EMPTY))); - setOpacity(0.5); - widthProperty().addListener((o) -> { - setTooltip(); - }); - layoutXProperty().addListener((o) -> { - setTooltip(); - }); - setTooltip(); + + closeButton.visibleProperty().bind(hoverProperty()); + zoomButton.visibleProperty().bind(hoverProperty()); + + widthProperty().addListener((o) -> this.updateStartAndEnd()); + layoutXProperty().addListener((o) -> this.updateStartAndEnd()); + updateStartAndEnd(); + setOnMouseMoved((MouseEvent event) -> { - Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); - final double diffX = getLayoutX() - localMouse.getX(); - if (Math.abs(diffX) <= HALF_STROKE || Math.abs(diffX + getWidth()) <= HALF_STROKE) { - //if the mouse is over the stroke, show the resize cursor - setCursor(Cursor.H_RESIZE); + Point2D parentMouse = getParent().sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + final double diffX = getLayoutX() - parentMouse.getX(); + if (Math.abs(diffX) <= HALF_STROKE) { + setCursor(Cursor.W_RESIZE); + } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) { + setCursor(Cursor.E_RESIZE); } else { setCursor(Cursor.HAND); } + event.consume(); }); + setOnMousePressed((MouseEvent event) -> { - Point2D localMouse = sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); - final double diffX = getLayoutX() - localMouse.getX(); - startX = event.getX(); + Point2D parentMouse = getParent().sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + final double diffX = getLayoutX() - parentMouse.getX(); + startDragX = event.getScreenX(); startWidth = getWidth(); startLeft = getLayoutX(); if (Math.abs(diffX) <= HALF_STROKE) { @@ -112,38 +131,52 @@ public abstract class IntervalSelector extends BorderPane { } else { dragPosition = IntervalSelector.DragPosition.CENTER; } + event.consume(); }); setOnMouseDragged((MouseEvent event) -> { - double dX = event.getX() - startX; + double dX = event.getScreenX() - startDragX; switch (dragPosition) { case CENTER: - relocate(startLeft + dX, 0); + setLayoutX(startLeft + dX); break; case LEFT: - relocate(startLeft + dX, 0); + setLayoutX(startLeft + dX); setPrefWidth(startWidth - dX); + autosize(); break; case RIGHT: setPrefWidth(startWidth + dX); + autosize(); break; } event.consume(); + System.out.println(dX); }); + + ActionUtils.configureButton(new ZoomToSelectedIntervalAction(), zoomButton); + ActionUtils.configureButton(new ClearSelectedIntervalAction(), closeButton); + //have to add handler rather than use convenience methods so that charts can listen for dismisal click - addEventHandler(MouseEvent.MOUSE_CLICKED, new EventHandler() { - public void handle(MouseEvent event) { - if (event.getClickCount() >= 2) { - //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); - event.consume(); - } + setOnMouseClicked((MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); + } + if (event.getClickCount() >= 2) { + zoomToSelectedInterval(); + event.consume(); } }); } + private void zoomToSelectedInterval() { + //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); + } + /** * * @param i the interval represented by this selector @@ -172,12 +205,17 @@ public abstract class IntervalSelector extends BorderPane { */ protected abstract DateTime parseDateTime(X date); - @NbBundle.Messages(value = {"# {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(); + @NbBundle.Messages(value = {"# {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 updateStartAndEnd() { + String startString = formatSpan(getSpanStart()); + String endString = formatSpan(getSpanEnd()); + startLabel.setText(startString); + endLabel.setText(endString); + Tooltip.uninstall(this, tooltip); - tooltip = new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(formatSpan(start), formatSpan(end))); + tooltip = new Tooltip(Bundle.Timeline_ui_TimeLineChart_tooltip_text(startString, endString)); Tooltip.install(this, tooltip); } @@ -186,7 +224,7 @@ public abstract class IntervalSelector extends BorderPane { * selector */ public X getSpanEnd() { - return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMaxX(), 0).getX()); + return chart.getXAxis().getValueForDisplay(chart.getXAxis().parentToLocal(getBoundsInParent().getMaxX(), 0).getX()); } /** @@ -194,7 +232,7 @@ public abstract class IntervalSelector extends BorderPane { * selector */ public X getSpanStart() { - return dateAxis.getValueForDisplay(dateAxis.parentToLocal(getBoundsInParent().getMinX(), 0).getX()); + return chart.getXAxis().getValueForDisplay(chart.getXAxis().parentToLocal(getBoundsInParent().getMinX(), 0).getX()); } /** @@ -208,4 +246,28 @@ public abstract class IntervalSelector extends BorderPane { RIGHT } + private class ZoomToSelectedIntervalAction extends Action { + + @NbBundle.Messages("IntervalSelector.ZoomAction.name=Zoom") + ZoomToSelectedIntervalAction() { + super(Bundle.IntervalSelector_ZoomAction_name()); + setGraphic(new ImageView(ZOOM_TO_INTERVAL_ICON)); + setEventHandler((ActionEvent t) -> { + zoomToSelectedInterval(); + }); + } + } + + private class ClearSelectedIntervalAction extends Action { + + @NbBundle.Messages("IntervalSelector.ClearSelectedIntervalAction.tooltTipText=Clear Selected Interval") + ClearSelectedIntervalAction() { + super(""); + setLongText(Bundle.IntervalSelector_ClearSelectedIntervalAction_tooltTipText()); + setGraphic(new ImageView(ClEAR_INTERVAL_ICON)); + setEventHandler((ActionEvent t) -> { + chart.clearIntervalSelector(); + }); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java index 02e8f4e006..d67876e0ee 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-15 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,29 +21,14 @@ package org.sleuthkit.autopsy.timeline.ui; import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.event.EventType; -import javafx.geometry.Insets; -import javafx.geometry.Point2D; import javafx.scene.Cursor; import javafx.scene.chart.Axis; import javafx.scene.chart.Chart; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; -import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; -import javafx.scene.layout.Background; -import javafx.scene.layout.BackgroundFill; -import javafx.scene.layout.Border; -import javafx.scene.layout.BorderStroke; -import javafx.scene.layout.BorderStrokeStyle; -import javafx.scene.layout.BorderWidths; -import javafx.scene.layout.CornerRadii; -import javafx.scene.layout.StackPane; -import javafx.scene.paint.Color; import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionGroup; -import org.joda.time.DateTime; -import org.joda.time.Interval; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineView; @@ -65,12 +50,9 @@ public interface TimeLineChart extends TimeLineView { * derived classes should implement this so as to supply an appropriate * subclass of {@link IntervalSelector} * - * @param x the initial x position of the new interval selector - * @param axis the axis the new interval selector will be over - * * @return a new interval selector */ - IntervalSelector newIntervalSelector(double x, Axis axis); + IntervalSelector newIntervalSelector(); /** * clear any references to previous interval selectors , including removing @@ -78,6 +60,10 @@ public interface TimeLineChart extends TimeLineView { */ void clearIntervalSelector(); + public Axis getXAxis(); + + public TimeLineController getController(); + /** * drag handler class used by {@link TimeLineChart}s to create * {@link IntervalSelector}s @@ -115,50 +101,33 @@ public interface TimeLineChart extends TimeLineView { //caputure x-position, incase we are repositioning existing selector startX = mouseEvent.getX(); chart.setCursor(Cursor.H_RESIZE); - mouseEvent.consume(); } 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.setIntervalSelector(chart.newIntervalSelector()); chart.getIntervalSelector().prefHeightProperty().bind(chart.heightProperty()); - IntervalSelector intervalSelector = chart.getIntervalSelector(); - if (intervalSelector != null) { - intervalSelector.addEventHandler(MouseEvent.MOUSE_CLICKED, (MouseEvent event) -> { - if (event.getButton() == MouseButton.SECONDARY) { - chart.clearIntervalSelector(); - event.consume(); - } - }); - } startX = mouseEvent.getX(); + chart.getIntervalSelector().relocate(startX, 0); } else { //resize/position existing selector if (mouseEvent.getX() > startX) { -// chart.getIntervalSelector().relocate(startX, 0); + chart.getIntervalSelector().relocate(startX, 0); chart.getIntervalSelector().setPrefWidth(mouseEvent.getX() - startX); } else { chart.getIntervalSelector().relocate(mouseEvent.getX(), 0); chart.getIntervalSelector().setPrefWidth(startX - mouseEvent.getX()); } } - mouseEvent.consume(); + chart.getIntervalSelector().autosize(); } else if (mouseEventType == MouseEvent.MOUSE_RELEASED) { chart.setCursor(Cursor.DEFAULT); requireDrag = true; chart.setOnMouseMoved(null); - mouseEvent.consume(); } else if (mouseEventType == MouseEvent.MOUSE_CLICKED) { chart.setCursor(Cursor.DEFAULT); requireDrag = true; chart.setOnMouseMoved(null); - mouseEvent.consume(); - } - - if (chart.getIntervalSelector() != null) { - chart.getIntervalSelector().autosize(); - System.out.println(chart.getIntervalSelector().getBoundsInLocal()); - } } } 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 db23e22b3a..892dac397a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java @@ -21,7 +21,6 @@ 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; import javafx.scene.chart.StackedBarChart; @@ -119,6 +118,11 @@ final class EventCountsChart extends StackedBarChart implements return chartContextMenu; } + @Override + public TimeLineController getController() { + return controller; + } + @Override public synchronized void setController(TimeLineController controller) { this.controller = controller; @@ -145,8 +149,8 @@ final class EventCountsChart extends StackedBarChart implements } @Override - public CountsIntervalSelector newIntervalSelector(double x, Axis dateAxis) { - return new CountsIntervalSelector(x, getHeight() - dateAxis.getHeight() - dateAxis.getTickLength(), dateAxis, controller); + public CountsIntervalSelector newIntervalSelector() { + return new CountsIntervalSelector(this); } /** @@ -186,10 +190,13 @@ final class EventCountsChart extends StackedBarChart implements * Interval Selector for the counts chart, adjusts interval based on * rangeInfo to include final period */ - private class CountsIntervalSelector extends IntervalSelector { + static private class CountsIntervalSelector extends IntervalSelector { - CountsIntervalSelector(double x, double height, Axis axis, TimeLineController controller) { - super(x, height, axis, controller); + private final EventCountsChart countsChart; + + CountsIntervalSelector(EventCountsChart chart) { + super(chart); + this.countsChart = chart; } @Override @@ -206,12 +213,12 @@ final class EventCountsChart extends StackedBarChart implements final DateTime lowerDate = new DateTime(lowerBound, TimeLineController.getJodaTimeZone()); final DateTime upperDate = new DateTime(upperBound, TimeLineController.getJodaTimeZone()); //add extra block to end that gets cut of by conversion from string/category. - return new Interval(lowerDate, upperDate.plus(rangeInfo.getPeriodSize().getPeriod())); + return new Interval(lowerDate, upperDate.plus(countsChart.rangeInfo.getPeriodSize().getPeriod())); } @Override protected DateTime parseDateTime(String date) { - return date == null ? new DateTime(rangeInfo.getLowerBound()) : rangeInfo.getTickFormatter().parseDateTime(date); + return date == null ? new DateTime(countsChart.rangeInfo.getLowerBound()) : countsChart.rangeInfo.getTickFormatter().parseDateTime(date); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java index cb948559ec..17a5220a90 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java @@ -236,7 +236,8 @@ public final class EventDetailChart extends XYChart impl return bundles; } - TimeLineController getController() { + @Override + public TimeLineController getController() { return controller; } @@ -246,7 +247,7 @@ public final class EventDetailChart extends XYChart impl } chartContextMenu = ActionUtils.createContextMenu(Arrays.asList(new PlaceMarkerAction(clickEvent), -// new StartIntervalSelectionAction(clickEvent, dragHandler), + // new StartIntervalSelectionAction(clickEvent, dragHandler), TimeLineChart.newZoomHistoyActionGroup(controller))); chartContextMenu.setAutoHide(true); return chartContextMenu; @@ -287,8 +288,8 @@ public final class EventDetailChart extends XYChart impl } @Override - public IntervalSelector newIntervalSelector(double x, Axis axis) { - return new DetailIntervalSelector(x, getHeight() - axis.getHeight() - axis.getTickLength(), axis, controller); + public IntervalSelector newIntervalSelector() { + return new DetailIntervalSelector(this); } synchronized void setBandByType(Boolean t1) { @@ -583,8 +584,8 @@ public final class EventDetailChart extends XYChart impl static private class DetailIntervalSelector extends IntervalSelector { - DetailIntervalSelector(double x, double height, Axis axis, TimeLineController controller) { - super(x, height, axis, controller); + DetailIntervalSelector(EventDetailChart chart) { + super(chart); } @Override From 645d6fba1d6bb9b0ca1fa4abb92a59315434336d Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 15 Oct 2015 17:42:11 -0400 Subject: [PATCH 11/48] remove right click menu to start IntervalSelector dragging. Improve Ux of IntervalSelector --- .../autopsy/timeline/ui/IntervalSelector.css | 1 - .../autopsy/timeline/ui/IntervalSelector.fxml | 32 ++-- .../autopsy/timeline/ui/IntervalSelector.java | 149 ++++++++++++------ .../autopsy/timeline/ui/TimeLineChart.java | 44 +----- .../ui/countsview/EventCountsChart.java | 1 - 5 files changed, 124 insertions(+), 103 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css index d1eb403eae..b349466632 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.css @@ -5,7 +5,6 @@ } .closeButton{ - -fx-background-color: transparent; } .closeButton:hover{ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml index ac279f7c81..a3885619d4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.fxml @@ -8,7 +8,7 @@ - - - diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java index 025d8c4e04..8ab9a92e06 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/IntervalSelector.java @@ -18,9 +18,14 @@ */ package org.sleuthkit.autopsy.timeline.ui; +import javafx.beans.Observable; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; import javafx.event.ActionEvent; import javafx.fxml.FXML; import javafx.geometry.Point2D; +import javafx.geometry.Pos; import javafx.scene.Cursor; import javafx.scene.control.Button; import javafx.scene.control.Label; @@ -65,6 +70,8 @@ public abstract class IntervalSelector extends BorderPane { private double startLeft; private double startDragX; private double startWidth; + + private BooleanProperty isDragging = new SimpleBooleanProperty(false); /////////end drag state private final TimeLineController controller; @@ -80,6 +87,9 @@ public abstract class IntervalSelector extends BorderPane { @FXML private Button zoomButton; + @FXML + private BorderPane bottomBorder; + public IntervalSelector(TimeLineChart chart) { this.chart = chart; this.controller = chart.getController(); @@ -98,10 +108,21 @@ public abstract class IntervalSelector extends BorderPane { setMaxWidth(USE_PREF_SIZE); setMinWidth(USE_PREF_SIZE); - closeButton.visibleProperty().bind(hoverProperty()); - zoomButton.visibleProperty().bind(hoverProperty()); + BooleanBinding showingControls = hoverProperty().and(isDragging.not()); + closeButton.visibleProperty().bind(showingControls); + closeButton.managedProperty().bind(showingControls); + zoomButton.visibleProperty().bind(showingControls); + zoomButton.managedProperty().bind(showingControls); - widthProperty().addListener((o) -> this.updateStartAndEnd()); + widthProperty().addListener((Observable o) -> { + IntervalSelector.this.updateStartAndEnd(); + if (startLabel.getWidth() + zoomButton.getWidth() + endLabel.getWidth() > getWidth()) { + this.setCenter(zoomButton); + } else { + bottomBorder.setCenter(zoomButton); + } + BorderPane.setAlignment(zoomButton, Pos.BOTTOM_CENTER); + }); layoutXProperty().addListener((o) -> this.updateStartAndEnd()); updateStartAndEnd(); @@ -116,57 +137,85 @@ public abstract class IntervalSelector extends BorderPane { setCursor(Cursor.HAND); } event.consume(); - }); + } + ); - setOnMousePressed((MouseEvent event) -> { - Point2D parentMouse = getParent().sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); - final double diffX = getLayoutX() - parentMouse.getX(); - startDragX = event.getScreenX(); - startWidth = getWidth(); - startLeft = getLayoutX(); - if (Math.abs(diffX) <= HALF_STROKE) { - dragPosition = IntervalSelector.DragPosition.LEFT; - } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) { - dragPosition = IntervalSelector.DragPosition.RIGHT; - } else { - dragPosition = IntervalSelector.DragPosition.CENTER; - } - event.consume(); - }); - setOnMouseDragged((MouseEvent event) -> { - double dX = event.getScreenX() - startDragX; - switch (dragPosition) { - case CENTER: - setLayoutX(startLeft + dX); - break; - case LEFT: - setLayoutX(startLeft + dX); - setPrefWidth(startWidth - dX); - autosize(); - break; - case RIGHT: - setPrefWidth(startWidth + dX); - autosize(); - break; - } - event.consume(); - System.out.println(dX); - }); + setOnMousePressed( + (MouseEvent event) -> { + Point2D parentMouse = getParent().sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + final double diffX = getLayoutX() - parentMouse.getX(); + startDragX = event.getScreenX(); + startWidth = getWidth(); + startLeft = getLayoutX(); + if (Math.abs(diffX) <= HALF_STROKE) { + dragPosition = IntervalSelector.DragPosition.LEFT; + } else if (Math.abs(diffX + getWidth()) <= HALF_STROKE) { + dragPosition = IntervalSelector.DragPosition.RIGHT; + } else { + dragPosition = IntervalSelector.DragPosition.CENTER; + } + event.consume(); + } + ); - ActionUtils.configureButton(new ZoomToSelectedIntervalAction(), zoomButton); - ActionUtils.configureButton(new ClearSelectedIntervalAction(), closeButton); + setOnMouseReleased( + (MouseEvent event) -> { + isDragging.set(false); + } + ); + setOnMouseDragged( + (MouseEvent event) -> { + isDragging.set(true); + double dX = event.getScreenX() - startDragX; + switch (dragPosition) { + case CENTER: + setLayoutX(startLeft + dX); + break; + case LEFT: + if (dX > startWidth) { + startDragX = event.getScreenX(); + startWidth = 0; + dragPosition = DragPosition.RIGHT; + } else { + setLayoutX(startLeft + dX); + setPrefWidth(startWidth - dX); + autosize(); + } + break; + case RIGHT: + Point2D parentMouse = getParent().sceneToLocal(new Point2D(event.getSceneX(), event.getSceneY())); + if (parentMouse.getX() < startLeft) { + dragPosition = DragPosition.LEFT; + startDragX = event.getScreenX(); + startWidth = 0; + } else { + setPrefWidth(startWidth + dX); + autosize(); + } + break; + } + event.consume(); + } + ); + + ActionUtils.configureButton( + new ZoomToSelectedIntervalAction(), zoomButton); + ActionUtils.configureButton( + new ClearSelectedIntervalAction(), closeButton); //have to add handler rather than use convenience methods so that charts can listen for dismisal click - setOnMouseClicked((MouseEvent event) -> { - if (event.getButton() == MouseButton.SECONDARY) { - chart.clearIntervalSelector(); - event.consume(); - } - if (event.getClickCount() >= 2) { - zoomToSelectedInterval(); - event.consume(); - } - }); + setOnMouseClicked( + (MouseEvent event) -> { + if (event.getButton() == MouseButton.SECONDARY) { + chart.clearIntervalSelector(); + event.consume(); + } + if (event.getClickCount() >= 2) { + zoomToSelectedInterval(); + event.consume(); + } + } + ); } private void zoomToSelectedInterval() { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java index d67876e0ee..d6f73eefc7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineChart.java @@ -18,16 +18,12 @@ */ package org.sleuthkit.autopsy.timeline.ui; -import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.event.EventType; import javafx.scene.Cursor; import javafx.scene.chart.Axis; import javafx.scene.chart.Chart; -import javafx.scene.image.Image; -import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; -import org.controlsfx.control.action.Action; import org.controlsfx.control.action.ActionGroup; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; @@ -75,23 +71,13 @@ public interface TimeLineChart extends TimeLineView { private final Y chart; - private final Axis dateAxis; + private final Axis xAxis; 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; + this.xAxis = dateAxis; } @Override @@ -101,8 +87,7 @@ public interface TimeLineChart extends TimeLineView { //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)) { + } else if (mouseEventType == MouseEvent.MOUSE_DRAGGED) { if (chart.getIntervalSelector() == null) { //make new interval selector chart.setIntervalSelector(chart.newIntervalSelector()); @@ -122,39 +107,16 @@ public interface TimeLineChart extends TimeLineView { chart.getIntervalSelector().autosize(); } else if (mouseEventType == MouseEvent.MOUSE_RELEASED) { chart.setCursor(Cursor.DEFAULT); - requireDrag = true; - chart.setOnMouseMoved(null); } else if (mouseEventType == MouseEvent.MOUSE_CLICKED) { chart.setCursor(Cursor.DEFAULT); - requireDrag = true; - chart.setOnMouseMoved(null); } } } - public static class StartIntervalSelectionAction extends Action { - - private static final Image SELECT_ICON = new Image("/org/sleuthkit/autopsy/timeline/images/select.png", 16, 16, true, true, true); - - /** - * @param clickEvent the value of clickEvent - * @param dragHandler the value of dragHandler - */ - public StartIntervalSelectionAction(MouseEvent clickEvent, final ChartDragHandler dragHandler) { - super("Select Interval"); - setGraphic(new ImageView(SELECT_ICON)); - setEventHandler((ActionEvent t) -> { - dragHandler.setRequireDrag(false); - dragHandler.handle(clickEvent.copyFor(clickEvent.getSource(), clickEvent.getTarget(), MouseEvent.MOUSE_DRAGGED)); - }); - } - } - @NbBundle.Messages({"TimeLineChart.zoomHistoryActionGroup.name=Zoom History"}) static ActionGroup newZoomHistoyActionGroup(TimeLineController controller) { return new ActionGroup(Bundle.TimeLineChart_zoomHistoryActionGroup_name(), new Back(controller), new Forward(controller)); } - } 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 892dac397a..5387c53df7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java @@ -112,7 +112,6 @@ final class EventCountsChart extends StackedBarChart implements chartContextMenu = ActionUtils.createContextMenu( Arrays.asList( - new StartIntervalSelectionAction(clickEvent, dragHandler), TimeLineChart.newZoomHistoyActionGroup(controller))); chartContextMenu.setAutoHide(true); return chartContextMenu; From 2690d1c4ed414ba51775e63e7ee480ae2a4f6f90 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 16 Oct 2015 14:11:12 -0400 Subject: [PATCH 12/48] fix tooltips for event bundles and add general tooltip instructing user to "drag to select interval..." --- ...on.java => AbstractVisualizationPane.java} | 21 +++++++----- .../autopsy/timeline/ui/Bundle.properties | 2 +- .../timeline/ui/VisualizationPanel.java | 10 +++--- .../ui/countsview/CountsViewPane.java | 28 +++++++-------- .../ui/countsview/EventCountsChart.java | 4 ++- .../ui/detailview/DetailViewPane.java | 7 ++-- .../ui/detailview/EventBundleNodeBase.java | 34 +++++++++++-------- 7 files changed, 58 insertions(+), 48 deletions(-) rename Core/src/org/sleuthkit/autopsy/timeline/ui/{AbstractVisualization.java => AbstractVisualizationPane.java} (94%) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java similarity index 94% rename from Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java rename to Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index 993f61501e..df861c997f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -40,6 +40,7 @@ import javafx.scene.chart.Chart; import javafx.scene.chart.XYChart; import javafx.scene.control.Label; import javafx.scene.control.OverrunStyle; +import javafx.scene.control.Tooltip; import javafx.scene.effect.Effect; import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; @@ -53,6 +54,7 @@ import javafx.scene.text.TextAlignment; import javax.annotation.Nonnull; import javax.annotation.concurrent.Immutable; import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineView; @@ -73,7 +75,10 @@ import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; * {@link XYChart} doing the rendering. Is this a good idea? -jm TODO: pull up * common history context menu items out of derived classes? -jm */ -public abstract class AbstractVisualization & TimeLineChart> extends BorderPane implements TimeLineView { +public abstract class AbstractVisualizationPane & TimeLineChart> extends BorderPane implements TimeLineView { + + @NbBundle.Messages("AbstractVisualizationPane.Drag_Tooltip.text=Drag the mouse to select a time interval to zoom into.") + protected static final Tooltip DRAG_TOOLTIP = new Tooltip(Bundle.AbstractVisualizationPane_Drag_Tooltip_text()); protected final SimpleBooleanProperty hasEvents = new SimpleBooleanProperty(true); @@ -195,7 +200,7 @@ public abstract class AbstractVisualization & T try { this.hasEvents.set(updateTask.get()); } catch (InterruptedException | ExecutionException ex) { - Logger.getLogger(AbstractVisualization.class.getName()).log(Level.SEVERE, "Unexpected exception updating visualization", ex); + Logger.getLogger(AbstractVisualizationPane.class.getName()).log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NOI18N } break; } @@ -211,7 +216,7 @@ public abstract class AbstractVisualization & T invalidationListener = null; } - protected AbstractVisualization(Pane partPane, Pane contextPane, Region spacer) { + protected AbstractVisualizationPane(Pane partPane, Pane contextPane, Region spacer) { this.leafPane = partPane; this.branchPane = contextPane; this.spacer = spacer; @@ -370,7 +375,7 @@ public abstract class AbstractVisualization & T */ private synchronized void assignLeafLabel(String labelText, double labelWidth, double labelX, boolean bold) { - Text label = new Text(" " + labelText + " "); + Text label = new Text(" " + labelText + " "); //NOI18N label.setTextAlignment(TextAlignment.CENTER); label.setFont(Font.font(null, bold ? FontWeight.BOLD : FontWeight.NORMAL, 10)); //position label accounting for width @@ -414,9 +419,9 @@ public abstract class AbstractVisualization & T label.relocate(labelX, 0); if (labelX == 0) { // first label has no border - label.setStyle("-fx-border-width: 0 0 0 0 ; -fx-border-color:black;"); // NON-NLS + label.setStyle("-fx-border-width: 0 0 0 0 ; -fx-border-color:black;"); // NON-NLS //NOI18N } else { // subsequent labels have border on left to create dividers - label.setStyle("-fx-border-width: 0 0 0 1; -fx-border-color:black;"); // NON-NLS + label.setStyle("-fx-border-width: 0 0 0 1; -fx-border-color:black;"); // NON-NLS //NOI18N } branchPane.getChildren().add(label); @@ -446,10 +451,10 @@ public abstract class AbstractVisualization & T TwoPartDateTime(String dateString) { //find index of separator to spit on - int splitIndex = StringUtils.lastIndexOfAny(dateString, " ", "-", ":"); + int splitIndex = StringUtils.lastIndexOfAny(dateString, " ", "-", ":"); //NOI18N if (splitIndex < 0) { // there is only one part leaf = dateString; - branch = ""; + branch = ""; //NOI18N } else { //split at index leaf = StringUtils.substring(dateString, splitIndex + 1); branch = StringUtils.substring(dateString, 0, splitIndex); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties index 9283be2f95..521cf76e4f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties @@ -55,4 +55,4 @@ VisualizationPanel.zoomMenuButton.text=Zoom in/out to VisualizationPanel.snapShotButton.text=Screenshot VisualizationPanel.detailsToggle.text=Details VisualizationPanel.countsToggle.text=Counts -VisualizationPanel.resetFiltersButton.text=Reset all filters +VisualizationPanel.resetFiltersButton.text=Reset all filters \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index 6aca0b6b6c..4195931c08 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -90,10 +90,10 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.tree.NavPanel; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** - * A Container for an {@link AbstractVisualization}, has a toolbar on top to - * hold settings widgets supplied by contained {@link AbstractVisualization}, + * A Container for an {@link AbstractVisualizationPane}, has a toolbar on top to + * hold settings widgets supplied by contained {@link AbstractVisualizationPane}, * and the histogram / timeselection on bottom. Also supplies containers for - * replacement axis to contained {@link AbstractVisualization} + * replacement axis to contained {@link AbstractVisualizationPane} * * TODO: refactor common code out of histogram and CountsView? -jm */ @@ -109,7 +109,7 @@ public class VisualizationPanel extends BorderPane implements TimeLineView { private final NavPanel navPanel; - private AbstractVisualization visualization; + private AbstractVisualizationPane visualization; //// range slider and histogram componenets @FXML @@ -361,7 +361,7 @@ public class VisualizationPanel extends BorderPane implements TimeLineView { } } - private synchronized void setVisualization(final AbstractVisualization newViz) { + private synchronized void setVisualization(final AbstractVisualizationPane newViz) { Platform.runLater(() -> { synchronized (VisualizationPanel.this) { if (visualization != null) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 7127f3b6b6..546a9e8da1 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -73,7 +73,7 @@ import org.sleuthkit.autopsy.timeline.actions.Forward; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.RootEventType; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualization; +import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** @@ -97,7 +97,7 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; * TODO: refactor common code out of this class and ClusterChartPane into * AbstractChartView */ -public class CountsViewPane extends AbstractVisualization { +public class CountsViewPane extends AbstractVisualizationPane { private static final Effect SELECTED_NODE_EFFECT = new Lighting(); @@ -213,17 +213,17 @@ public class CountsViewPane extends AbstractVisualization { - //defer tooltip creation till needed, this had a surprisingly large impact on speed of loading the chart - final Tooltip tooltip = new Tooltip( - NbBundle.getMessage(this.getClass(), "CountsViewPane.tooltip.text", - count, - et.getDisplayName(), - dateString, - interval.getEnd().toString( - rangeInfo.getTickFormatter()))); - tooltip.setGraphic(new ImageView(et.getFXImage())); - Tooltip.install(node, tooltip); node.setEffect(new DropShadow(10, et.getColor())); }); node.setOnMouseExited((MouseEvent event) -> { @@ -285,6 +285,7 @@ public class CountsViewPane extends AbstractVisualization(new CountsViewSettingsPane().getChildrenUnmodifiable()); @@ -310,9 +311,6 @@ public class CountsViewPane extends AbstractVisualization sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -96,6 +96,8 @@ final class EventCountsChart extends StackedBarChart implements clickEvent.consume(); } }); + + } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 40ea735938..fe9d776e2f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -73,7 +73,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.EventBundle; import org.sleuthkit.autopsy.timeline.datamodel.EventCluster; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualization; +import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; @@ -89,9 +89,8 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; * EventTypeMap, and dataSets is all linked directly to the ClusterChart which * must only be manipulated on the JavaFx thread. */ -public class DetailViewPane extends AbstractVisualization, EventDetailChart> { +public class DetailViewPane extends AbstractVisualizationPane, EventDetailChart> { - private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName()); private MultipleSelectionModel>> treeSelectionModel; @@ -121,6 +120,7 @@ public class DetailViewPane extends AbstractVisualization(new DetailViewSettingsPane().getChildrenUnmodifiable()); @@ -477,5 +477,4 @@ public class DetailViewPane extends AbstractVisualization { /* @@ -174,12 +175,7 @@ public abstract class EventBundleNodeBase { - showHoverControls(false); - if (parentNode != null) { - parentNode.showHoverControls(true); - } + }); setDescriptionVisibility(DescriptionVisibility.SHOWN); @@ -210,8 +206,11 @@ public abstract class EventBundleNodeBase tooltTipTask = new Task() { + { + updateTitle("loading tooltip"); + } @Override protected String call() throws Exception { @@ -252,13 +251,10 @@ public abstract class EventBundleNodeBase new DropShadow(-10, eventType.getColor())); setEffect(showControls ? dropShadow : null); + enableTooltip(showControls); if (parentNode != null) { + parentNode.enableTooltip(false); parentNode.showHoverControls(false); } } @@ -334,4 +332,12 @@ public abstract class EventBundleNodeBase Date: Fri, 16 Oct 2015 15:07:01 -0400 Subject: [PATCH 13/48] add status bar message about dragging IntervalSelector --- .../autopsy/timeline/TimeLineController.java | 16 +++++++++++++--- .../timeline/images/information-gray.png | Bin 0 -> 1266 bytes .../timeline/ui/AbstractVisualizationPane.java | 8 ++++++++ .../autopsy/timeline/ui/StatusBar.fxml | 11 ++++++++++- .../autopsy/timeline/ui/StatusBar.java | 10 +++++++++- 5 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/images/information-gray.png diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index e7dd27a635..975eda71dc 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -64,13 +64,13 @@ import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.casemodule.Case.Events.DATA_SOURCE_ADDED; -import org.sleuthkit.autopsy.coreutils.History; -import org.sleuthkit.autopsy.coreutils.LoggedTask; -import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.BlackBoardArtifactTagDeletedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; +import org.sleuthkit.autopsy.coreutils.History; +import org.sleuthkit.autopsy.coreutils.LoggedTask; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; @@ -135,6 +135,12 @@ public class TimeLineController { private final ReadOnlyStringWrapper taskTitle = new ReadOnlyStringWrapper(); + private final ReadOnlyStringWrapper status = new ReadOnlyStringWrapper(); + + public ReadOnlyStringProperty getStatusProperty() { + return status.getReadOnlyProperty(); + } + private final Case autoCase; @ThreadConfined(type = ThreadConfined.ThreadType.JFX) @@ -813,6 +819,10 @@ public class TimeLineController { JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION; } + public void setStatus(String string) { + status.set(string); + } + private class AutopsyIngestModuleListener implements PropertyChangeListener { @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/information-gray.png b/Core/src/org/sleuthkit/autopsy/timeline/images/information-gray.png new file mode 100644 index 0000000000000000000000000000000000000000..c0dd006c9fb7f781f09257f676735bea7e0ec89e GIT binary patch literal 1266 zcmVkdg00004XF*Lt006O% z3;baP0000WV@Og>004R>004l5008;`004mK004C`008P>0026e000+ooVrmw00002 zVoOIv0RM-N%)bBt1L#RaK~zY`eU;x&TtyVeKQs5vWp_cAU7$dJAVO1%fQd~SH8v(% ztq)CWeQ69)G>w|Ze?eb-&^KR=F)H=}V@#Tq5UQaMK4`_(#1x?=Y%QgQ0!v}ng=K%- z-FwG}z55gDoR^t7GvD()XU;hz*kzn<48AAc;b{$k0Qj2=iLXaDcb*ls>$BayOR9f> z{p?5K;^30u0n0>iW>RN^Meeencm{=$T8qm0;*&<1VKXJbL?n9j20o2gF zo{p0=;pZRbxJQw3Bb^+iiw%~zPIP7XadJBW)Y}Wk_a5aj*0{tr1`>e)G#VF&dQK2# zfmvpjFLV_lM9KR7m)noh#4r4X!YEy(1mlupfajQFmbum8&H{u{@cpeXdtN+FhzV{| zhlzni<6z?;kQhiLCQ*_+LymaE!M#)C=mLN^@nra$H)-aocU>ZYJSTad*YFX+L?V$G z#K>}(94klm{BUOjB%s2k=uj$mbHD>}|th6;)*)T(lPrQKgX>#A(8h z*^AG_1ce|)U6pm|~E)CcQMvQhuO{i)a6h$BivxH52jJzasePodsE%<8y$`PsE_u*%W4A(}; zNbI#}cK$98=&RyD6#!L_*hz#Wx%p@m+dyMvZq*Mqun)<0$%^KD3E)|7crPJp><83k&5{sX!1S%mgvA8zb+sLZ;Z#9Nej&I?uR)1YIyKjP_&`BLY(ya z@F^DA#4Q47Y;sdScfFDH$58a($H2u!AP`st2oWU0uO!LFhEKCVjC{GNf=d>AYJ6Wu z>d?O|lck<^S_u;(gr5WvuCa(seEj?KEU;eN)(h1WqP@HSg-`3c8n94k92~EqJLJhO z{PxlO46DR<`&bHm0yNVaY#NoiQ*S>2001R)MObuXVRU6WZEs|0W_bWI zFfchSFgGnSH&ie-IyE#pGcYSKGdeIZ8Kl>C0000PbVXQnQ*UN;cVTj606}DLVr3vn cZDD6+Qe|Oed2z{QJOBUy07*qoM6N<$g0)yGB>(^b literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index df861c997f..4fe6075686 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -242,6 +242,14 @@ public abstract class AbstractVisualizationPane TimeLineController.getTimeZone().addListener((Observable observable) -> { update(); }); + + hoverProperty().addListener((observable, oldActivated, newActivated) -> { + if (newActivated) { + controller.setStatus(DRAG_TOOLTIP.getText()); + } else { + controller.setStatus(""); + } + }); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/StatusBar.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/StatusBar.fxml index 781bb18d1a..a731fb13a6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/StatusBar.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/StatusBar.fxml @@ -5,7 +5,7 @@ - + +