From 4d14c32a94e3fce033f8b52b0423047191fd0659 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 12:15:41 -0400 Subject: [PATCH] split AbstractVisualizationPane into AbstractTimeLineView and AbstractTimelineChart CountsViewPane and DetailViewPane extend AbstractTimelineChart, ListViewPane extends AbstractTimeLineView --- .../timeline/ui/AbstractTimeLineView.java | 360 +++++++++++++++++ ...onPane.java => AbstractTimelineChart.java} | 377 +----------------- .../autopsy/timeline/ui/TimeLineView.java | 14 - .../timeline/ui/VisualizationPanel.java | 4 +- .../ui/countsview/CountsViewPane.java | 12 +- .../ui/detailview/DetailViewPane.java | 13 +- .../timeline/ui/detailview/DetailsChart.java | 5 + .../ui/detailview/DetailsChartLane.java | 4 +- .../timeline/ui/detailview/EventNodeBase.java | 6 +- .../timeline/ui/detailview/GuideLine.java | 4 +- .../timeline/ui/listvew/ListChart.java | 51 ++- .../timeline/ui/listvew/ListViewChart.fxml | 12 +- .../timeline/ui/listvew/ListViewPane.java | 63 +-- 13 files changed, 452 insertions(+), 473 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java rename Core/src/org/sleuthkit/autopsy/timeline/ui/{AbstractVisualizationPane.java => AbstractTimelineChart.java} (57%) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java new file mode 100644 index 0000000000..e890a7387a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -0,0 +1,360 @@ +/* + * 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 com.google.common.eventbus.Subscribe; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.concurrent.Task; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; +import org.controlsfx.control.MaskerPane; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.coreutils.LoggedTask; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; + +public abstract class AbstractTimeLineView extends BorderPane { + + private static final Logger LOGGER = Logger.getLogger(AbstractTimeLineView.class.getName()); + + /** + * Boolean property that holds true if the visualization does not show any + * events with the current zoom and filter settings. + */ + private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true); + + /** + * Boolean property that holds true if the visualization may not represent + * the current state of the DB, because, for example, tags have been updated + * but the vis. was not refreshed. + */ + private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false); + + /** + * List of Nodes to insert into the toolbar. This should be set in an + * implementations constructor. + */ + private List settingsNodes; + + /** + * Listener that is attached to various properties that should trigger a vis + * update when they change. + */ + private InvalidationListener updateListener = (Observable any) -> refresh(); + + /** + * task used to reload the content of this visualization + */ + private Task updateTask; + + private final TimeLineController controller; + private final FilteredEventsModel filteredEvents; + + /** + * Constructor + * + * @param controller + */ + public AbstractTimeLineView(TimeLineController controller) { + this.controller = controller; + this.filteredEvents = controller.getEventsModel(); + this.filteredEvents.registerForEvents(this); + this.filteredEvents.zoomParametersProperty().addListener(updateListener); + TimeLineController.getTimeZone().addListener(updateListener); + } + + /** + * Handle a RefreshRequestedEvent from the events model by updating the + * visualization. + * + * @param event The RefreshRequestedEvent to handle. + */ + @Subscribe + public void handleRefreshRequested(RefreshRequestedEvent event) { + refresh(); + } + + /** + * Does the visualization represent an out-of-date state of the DB. It might + * if, for example, tags have been updated but the vis. was not refreshed. + * + * @return True if the visualization does not represent the curent state of + * the DB. + */ + public boolean isOutOfDate() { + return outOfDate.get(); + } + + /** + * Get a ReadOnlyBooleanProperty that holds true if this visualization does + * not represent the current state of the DB> + * + * @return A ReadOnlyBooleanProperty that holds the out-of-date state for + * this visualization. + */ + public ReadOnlyBooleanProperty outOfDateProperty() { + return outOfDate.getReadOnlyProperty(); + } + + /** + * Get the TimelineController for this visualization. + * + * @return The TimelineController for this visualization. + */ + protected TimeLineController getController() { + return controller; + } + + /** + * Refresh this visualization based on current state of zoom / filters. + * Primarily this invokes the background VisualizationUpdateTask returned by + * getUpdateTask(), which derived classes must implement. + * + * TODO: replace this logic with a javafx Service ? -jm + */ + protected final synchronized void refresh() { + if (updateTask != null) { + updateTask.cancel(true); + updateTask = null; + } + updateTask = getNewUpdateTask(); + updateTask.stateProperty().addListener((Observable observable) -> { + switch (updateTask.getState()) { + case CANCELLED: + case FAILED: + case READY: + case RUNNING: + case SCHEDULED: + break; + case SUCCEEDED: + try { + this.hasVisibleEvents.set(updateTask.get()); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS + } + break; + } + }); + getController().monitorTask(updateTask); + } + + /** + * Get the FilteredEventsModel for this visualization. + * + * @return The FilteredEventsModel for this visualization. + */ + protected FilteredEventsModel getEventsModel() { + return filteredEvents; + } + + /** + * Get a new background Task that fetches the appropriate data and loads it + * into this visualization. + * + * @return A new task to execute on a background thread to reload this + * visualization with different data. + */ + protected abstract Task getNewUpdateTask(); + + /** + * Get a List of nodes containing settings widgets to insert into this + * visualization's header. + * + * @return The List of settings Nodes. + */ + protected List getSettingsNodes() { + return Collections.unmodifiableList(settingsNodes); + } + + /** + * Set the List of nodes containing settings widgets to insert into this + * visualization's header. + * + * + * @param settingsNodes The List of nodes containing settings widgets to + * insert into this visualization's header. + */ + final protected void setSettingsNodes(List settingsNodes) { + this.settingsNodes = new ArrayList<>(settingsNodes); + } + + /** + * Dispose of this visualization and any resources it holds onto. + */ + final synchronized void dispose() { + //cancel and gc updateTask + if (updateTask != null) { + updateTask.cancel(true); + updateTask = null; + } + //remvoe and gc updateListener + this.filteredEvents.zoomParametersProperty().removeListener(updateListener); + TimeLineController.getTimeZone().removeListener(updateListener); + updateListener = null; + filteredEvents.unRegisterForEvents(this); + } + + /** + * Are there are any events visible in this visualization with the current + * view parameters? + * + * @return True if there are events visible in this visualization with the + * current view parameters. + */ + boolean hasVisibleEvents() { + return hasVisibleEventsProperty().get(); + } + + /** + * A property that indicates whether there are any events visible in this + * visualization with the current view parameters. + * + * @return A property that indicates whether there are any events visible in + * this visualization with the current view parameters. + */ + ReadOnlyBooleanProperty hasVisibleEventsProperty() { + return hasVisibleEvents.getReadOnlyProperty(); + } + + /** + * Set this visualization out of date because, for example, tags have been + * updated but the vis. was not refreshed. + */ + void setOutOfDate() { + outOfDate.set(true); + } + + /** + * Clear all data items from this chart. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + abstract protected void clearData(); + + /** + * Base class for Tasks that refreshes a view when the view settings change. + * + * @param The type of a single object that can represent + * the range of data displayed along the X-Axis. + */ + protected abstract class ViewRefreshTask extends LoggedTask { + + private final Node center; + + /** + * Constructor + * + * @param taskName The name of this task. + * @param logStateChanges Whether or not task state changes should be + * logged. + */ + protected ViewRefreshTask(String taskName, boolean logStateChanges) { + super(taskName, logStateChanges); + this.center = getCenter(); + } + + /** + * Sets initial progress value and message and shows blocking progress + * indicator over the visualization. Derived Tasks should be sure to + * call this as part of their call() implementation. + * + * @return True + * + * @throws Exception If there is an unhandled exception during the + * background operation + */ + @NbBundle.Messages(value = {"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) + @Override + protected Boolean call() throws Exception { + updateProgress(-1, 1); + updateMessage(Bundle.VisualizationUpdateTask_preparing()); + Platform.runLater(() -> { + MaskerPane maskerPane = new MaskerPane(); + maskerPane.textProperty().bind(messageProperty()); + maskerPane.progressProperty().bind(progressProperty()); + setCenter(new StackPane(center, maskerPane)); + setCursor(Cursor.WAIT); + }); + return true; + } + + /** + * Updates the horizontal axis and removes the blocking progress + * indicator. Derived Tasks should be sure to call this as part of their + * succeeded() implementation. + */ + @Override + protected void succeeded() { + super.succeeded(); + outOfDate.set(false); + cleanup(); + } + + /** + * Removes the blocking progress indicator. Derived Tasks should be sure + * to call this as part of their cancelled() implementation. + */ + @Override + protected void cancelled() { + super.cancelled(); + cleanup(); + } + + /** + * Removes the blocking progress indicator. Derived Tasks should be sure + * to call this as part of their failed() implementation. + */ + @Override + protected void failed() { + super.failed(); + cleanup(); + } + + /** + * Removes the blocking progress indicator and reset the cursor to the + * default. + */ + private void cleanup() { + setCenter(center); //clear masker pane installed in call() + setCursor(Cursor.DEFAULT); + } + + /** + * Set the horizontal range that this chart will show. + * + * @param values A single object representing the range that this chart + * will show. + */ + protected abstract void setDateValues(AxisValuesType values); + + /** + * Clears the chart data and sets the horizontal axis range. For use + * within the derived implementation of the call() method. + * + * @param axisValues + */ + @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) + protected void resetView(AxisValuesType axisValues) { + Platform.runLater(() -> { + clearData(); + setDateValues(axisValues); + }); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java similarity index 57% rename from Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java rename to Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java index ac58f65ca5..17209b9a4b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java @@ -18,27 +18,14 @@ */ package org.sleuthkit.autopsy.timeline.ui; -import com.google.common.eventbus.Subscribe; -import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; -import javafx.concurrent.Task; import javafx.geometry.Pos; -import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.chart.Axis; import javafx.scene.chart.XYChart; @@ -46,14 +33,12 @@ import javafx.scene.control.Label; import javafx.scene.control.OverrunStyle; import javafx.scene.control.Tooltip; 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.layout.Pane; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; @@ -61,15 +46,11 @@ import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import javax.annotation.concurrent.Immutable; import org.apache.commons.lang3.StringUtils; -import org.controlsfx.control.MaskerPane; import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; /** * Abstract base class for TimeLineChart based visualizations. @@ -85,9 +66,9 @@ import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; * * TODO: pull up common history context menu items out of derived classes? -jm */ -public abstract class AbstractVisualizationPane> extends BorderPane implements TimeLineView { +public abstract class AbstractTimelineChart> extends AbstractTimeLineView { - private static final Logger LOGGER = Logger.getLogger(AbstractVisualizationPane.class.getName()); + private static final Logger LOGGER = Logger.getLogger(AbstractTimelineChart.class.getName()); @NbBundle.Messages("AbstractVisualization.Default_Tooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.") private static final Tooltip DEFAULT_TOOLTIP = new Tooltip(Bundle.AbstractVisualization_Default_Tooltip_text()); @@ -99,22 +80,19 @@ public abstract class AbstractVisualizationPane of the nodes that are selected in + * this visualization. */ - private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false); - - /** - * Boolean property that holds true if the visualization does not show any - * events with the current zoom and filter settings. - */ - private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true); + protected ObservableList getSelectedNodes() { + return selectedNodes; + } /** * Access to chart data via series @@ -127,54 +105,11 @@ public abstract class AbstractVisualizationPane updateTask; - - final private TimeLineController controller; - final private FilteredEventsModel filteredEvents; - final private ObservableList selectedNodes = FXCollections.observableArrayList(); - /** - * Listener that is attached to various properties that should trigger a vis - * update when they change. - */ - private InvalidationListener updateListener = any -> refresh(); - - /** - * Does the visualization represent an out-of-date state of the DB. It might - * if, for example, tags have been updated but the vis. was not refreshed. - * - * @return True if the visualization does not represent the curent state of - * the DB. - */ - public boolean isOutOfDate() { - return outOfDate.get(); - } - - /** - * Set this visualization out of date because, for example, tags have been - * updated but the vis. was not refreshed. - */ - void setOutOfDate() { - outOfDate.set(true); - } - - /** - * Get a ReadOnlyBooleanProperty that holds true if this visualization does - * not represent the current state of the DB> - * - * @return A ReadOnlyBooleanProperty that holds the out-of-date state for - * this visualization. - */ - public ReadOnlyBooleanProperty outOfDateProperty() { - return outOfDate.getReadOnlyProperty(); - } - public Pane getSpecificLabelPane() { return specificLabelPane; } @@ -187,53 +122,6 @@ public abstract class AbstractVisualizationPane of the nodes that are selected in - * this visualization. - */ - protected ObservableList getSelectedNodes() { - return selectedNodes; - } - - /** - * List of Nodes to insert into the toolbar. This should be set in an - * implementations constructor. - */ - private List settingsNodes; - - /** - * Get a List of nodes containing settings widgets to insert into this - * visualization's header. - * - * @return The List of settings Nodes. - */ - protected List getSettingsNodes() { - return Collections.unmodifiableList(settingsNodes); - } - - /** - * Set the List of nodes containing settings widgets to insert into this - * visualization's header. - * - * - * @param settingsNodes The List of nodes containing settings widgets to - * insert into this visualization's header. - */ - protected void setSettingsNodes(List settingsNodes) { - this.settingsNodes = new ArrayList<>(settingsNodes); - } - - /** - * Get the TimelineController for this visualization. - * - * @return The TimelineController for this visualization. - */ - protected TimeLineController getController() { - return controller; - } - /** * Get the CharType that implements this visualization. * @@ -243,15 +131,6 @@ public abstract class AbstractVisualizationPane getNewUpdateTask(); - /** * Get the label that should be used for a tick mark at the given value. * @@ -373,74 +221,6 @@ public abstract class AbstractVisualizationPane { - switch (updateTask.getState()) { - case CANCELLED: - case FAILED: - case READY: - case RUNNING: - case SCHEDULED: - break; - case SUCCEEDED: - try { - this.hasVisibleEvents.set(updateTask.get()); - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS - } - break; - } - }); - controller.monitorTask(updateTask); - } - - /** - * Handle a RefreshRequestedEvent from the events model by updating the - * visualization. - * - * @param event The RefreshRequestedEvent to handle. - */ - @Subscribe - public void handleRefreshRequested(RefreshRequestedEvent event) { - refresh(); - } - - /** - * Dispose of this visualization and any resources it holds onto. - */ - final synchronized void dispose() { - - //cancel and gc updateTask - if (updateTask != null) { - updateTask.cancel(true); - updateTask = null; - } - //remvoe and gc updateListener - this.filteredEvents.zoomParametersProperty().removeListener(updateListener); - TimeLineController.getTimeZone().removeListener(updateListener); - updateListener = null; - - filteredEvents.unRegisterForEvents(this); - } - /** * Make a series for each event type in a consistent order. */ @@ -470,23 +250,8 @@ public abstract class AbstractVisualizationPane { -// VBox vBox = new VBox(specificLabelPane, contextLabelPane); -// vBox.setFillWidth(false); -// HBox hBox = new HBox(spacer, vBox); -// hBox.setFillHeight(false); -// setBottom(hBox); -// DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); -// spacer.minWidthProperty().bind(spacerSize); -// spacer.prefWidthProperty().bind(spacerSize); -// spacer.maxWidthProperty().bind(spacerSize); -// }); - + protected AbstractTimelineChart(TimeLineController controller) { + super(controller); createSeries(); selectedNodes.addListener((ListChangeListener.Change change) -> { @@ -496,10 +261,8 @@ public abstract class AbstractVisualizationPane controller.setStatusMessage(isHover() ? DEFAULT_TOOLTIP.getText() : "")); + hoverProperty().addListener(hoverProp -> controller.setStatusMessage(isHover() ? getDefaultTooltip().getText() : "")); } @@ -690,116 +453,4 @@ public abstract class AbstractVisualizationPane The type of a single object that can represent - * the range of data displayed along the X-Axis. - */ - abstract protected class VisualizationRefreshTask extends LoggedTask { - - private final Node center; - - /** - * Constructor - * - * @param taskName The name of this task. - * @param logStateChanges Whether or not task state changes should be - * logged. - */ - protected VisualizationRefreshTask(String taskName, boolean logStateChanges) { - super(taskName, logStateChanges); - this.center = getCenter(); - } - - /** - * Sets initial progress value and message and shows blocking progress - * indicator over the visualization. Derived Tasks should be sure to - * call this as part of their call() implementation. - * - * @return True - * - * @throws Exception If there is an unhandled exception during the - * background operation - */ - @NbBundle.Messages({"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) - @Override - protected Boolean call() throws Exception { - updateProgress(-1, 1); - updateMessage(Bundle.VisualizationUpdateTask_preparing()); - Platform.runLater(() -> { - MaskerPane maskerPane = new MaskerPane(); - maskerPane.textProperty().bind(messageProperty()); - maskerPane.progressProperty().bind(progressProperty()); - setCenter(new StackPane(center, maskerPane)); - setCursor(Cursor.WAIT); - }); - - return true; - } - - /** - * Updates the horizontal axis and removes the blocking progress - * indicator. Derived Tasks should be sure to call this as part of their - * succeeded() implementation. - */ - @Override - protected void succeeded() { - super.succeeded(); - outOfDate.set(false); - cleanup(); - } - - /** - * Removes the blocking progress indicator. Derived Tasks should be sure - * to call this as part of their cancelled() implementation. - */ - @Override - protected void cancelled() { - super.cancelled(); - cleanup(); - } - - /** - * Removes the blocking progress indicator. Derived Tasks should be sure - * to call this as part of their failed() implementation. - */ - @Override - protected void failed() { - super.failed(); - cleanup(); - } - - /** - * Removes the blocking progress indicator and reset the cursor to the - * default. - */ - private void cleanup() { - setCenter(center); //clear masker pane installed in call() - setCursor(Cursor.DEFAULT); - } - - /** - * Clears the chart data and sets the horizontal axis range. For use - * within the derived implementation of the call() method. - * - * @param axisValues - */ - @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) - protected void resetChart(AxisValuesType axisValues) { - Platform.runLater(() -> { - clearChartData(); - setDateAxisValues(axisValues); - }); - } - - /** - * Set the horizontal range that this chart will show. - * - * @param values A single object representing the range that this chart - * will show. - */ - abstract protected void setDateAxisValues(AxisValuesType values); - } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java deleted file mode 100644 index cfbd3c9f5a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * 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; - -/** - * - * @author jmillman - */ -public interface TimeLineView { - -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index f31b5e4f0d..14e92b2998 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -121,7 +121,7 @@ final public class VisualizationPanel extends BorderPane { private LoggedTask histogramTask; private final EventsTree eventsTree; - private AbstractVisualizationPane visualization; + private AbstractTimeLineView visualization; /* * HBox that contains the histogram bars. @@ -580,7 +580,7 @@ final public class VisualizationPanel extends BorderPane { * AbstractVislualization for one of the correct type. */ private void syncVisualizationMode() { - AbstractVisualizationPane vizPane; + AbstractTimeLineView vizPane; VisualizationMode visMode = controller.visualizationModeProperty().get(); //make new visualization. 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 575a6ca900..c5d9ddb6a4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -60,7 +60,7 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** @@ -79,7 +79,7 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; * Platform.runLater(java.lang.Runnable). The FilteredEventsModel should * encapsulate all need synchronization internally. */ -public class CountsViewPane extends AbstractVisualizationPane { +public class CountsViewPane extends AbstractTimelineChart { private static final Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName()); @@ -170,7 +170,7 @@ public class CountsViewPane extends AbstractVisualizationPane series : dataSeries) { series.getData().clear(); } @@ -349,7 +349,7 @@ public class CountsViewPane extends AbstractVisualizationPane> { + private class CountsUpdateTask extends ViewRefreshTask> { CountsUpdateTask() { super(Bundle.CountsViewPane_loggedTask_name(), true); @@ -374,7 +374,7 @@ public class CountsViewPane extends AbstractVisualizationPane intervals = rangeInfo.getIntervals(); //clear old data, and reset ranges and series - resetChart(Lists.transform(intervals, rangeInfo::formatForTick)); + resetView(Lists.transform(intervals, rangeInfo::formatForTick)); updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts()); int chartMax = 0; @@ -432,7 +432,7 @@ public class CountsViewPane extends AbstractVisualizationPane categories) { + protected void setDateValues(List categories) { dateAxis.getCategories().setAll(categories); } } 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 13d76c6977..c3998e7cda 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -56,7 +56,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.utils.MappedList; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; @@ -75,7 +75,7 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; * grouped EventStripes, etc, etc. The leaves of the trees are EventClusters or * SingleEvents. */ -public class DetailViewPane extends AbstractVisualizationPane, DetailsChart> { +public class DetailViewPane extends AbstractTimelineChart, DetailsChart> { private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName()); @@ -218,7 +218,7 @@ public class DetailViewPane extends AbstractVisualizationPane { + private class DetailsUpdateTask extends ViewRefreshTask { DetailsUpdateTask() { super(Bundle.DetailViewPane_loggedTask_name(), true); @@ -423,7 +422,7 @@ public class DetailViewPane extends AbstractVisualizationPane { return nestedEvents; } + Tooltip getDefaultTooltip() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + /** * Clear any time based UI elements (GuideLines, IntervalSelector,...) from * this chart. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java index b1e67e7eca..8dc24adff2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java @@ -58,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider; /** @@ -178,7 +178,7 @@ abstract class DetailsChartLane extends XYChart()))); - Tooltip.install(this, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.install(this, AbstractTimelineChart.getDefaultTooltip()); dateAxis.setAutoRanging(false); setLegendVisible(false); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java index 66bdb7fdf3..4af8dd8c06 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java @@ -77,7 +77,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider; import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show; import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3; @@ -167,7 +167,7 @@ public abstract class EventNodeBase extends StackPan //set up mouse hover effect and tooltip setOnMouseEntered(mouseEntered -> { - Tooltip.uninstall(chartLane, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.uninstall(chartLane, AbstractTimelineChart.getDefaultTooltip()); showHoverControls(true); toFront(); }); @@ -176,7 +176,7 @@ public abstract class EventNodeBase extends StackPan if (parentNode != null) { parentNode.showHoverControls(true); } else { - Tooltip.install(chartLane, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.install(chartLane, AbstractTimelineChart.getDefaultTooltip()); } }); setOnMouseClicked(new ClickHandler()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java index bbdd039a11..67e4de1c05 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java @@ -25,7 +25,6 @@ import javafx.scene.shape.Line; import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; /** * Subclass of {@link Line} with appropriate behavior (mouse listeners) to act @@ -35,7 +34,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; "GuideLine.tooltip.text={0}\nRight-click to remove.\nDrag to reposition."}) class GuideLine extends Line { - private static final Tooltip CHART_DEFAULT_TOOLTIP = AbstractVisualizationPane.getDefaultTooltip(); + private final Tooltip CHART_DEFAULT_TOOLTIP ; private final Tooltip tooltip = new Tooltip(); private final DetailsChart chart; @@ -49,6 +48,7 @@ class GuideLine extends Line { */ GuideLine(DetailsChart chart) { super(0, 0, 0, 0); + CHART_DEFAULT_TOOLTIP = chart.getDefaultTooltip(); this.chart = chart; Axis xAxis = chart.getXAxis(); endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty()))); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index 9235b763da..99aac7e3f0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -5,7 +5,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; -import java.util.List; +import java.util.Collection; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -43,16 +43,30 @@ class ListChart extends BorderPane implements TimeLineChart { @FXML private TableView table; - Callback, ObservableValue> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue()); + private static final Callback, ObservableValue> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue()); private final TimeLineController controller; - private final TableColumn idColumn = new TableColumn<>("Event ID"); - private final TableColumn millisColumn = new TableColumn<>("Date/Time"); - private final TableColumn iconColumn = new TableColumn<>("Icon"); - private final TableColumn descriptionColumn = new TableColumn<>("Description"); - private final TableColumn baseTypeColumn = new TableColumn<>("Base Type"); - private final TableColumn subTypeColumn = new TableColumn<>("Sub Type"); - private final TableColumn knownColumn = new TableColumn<>("Known"); + + @FXML + private TableColumn idColumn; + + @FXML + private TableColumn millisColumn; + + @FXML + private TableColumn iconColumn; + + @FXML + private TableColumn descriptionColumn; + + @FXML + private TableColumn baseTypeColumn; + + @FXML + private TableColumn subTypeColumn; + + @FXML + private TableColumn knownColumn; ListChart(TimeLineController controller) { this.controller = controller; @@ -71,26 +85,27 @@ class ListChart extends BorderPane implements TimeLineChart { assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; -// setRowFactory(tableView -> new EventRow()); - idColumn.setCellValueFactory(cellValueFactory); + table.setRowFactory(tableView -> new EventRow()); + idColumn.setCellValueFactory(CELL_VALUE_FACTORY); - millisColumn.setCellValueFactory(cellValueFactory); + millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); millisColumn.setCellFactory(col -> new EpochMillisCell()); - iconColumn.setCellValueFactory(cellValueFactory); + iconColumn.setCellValueFactory(CELL_VALUE_FACTORY); iconColumn.setCellFactory(col -> new ImageCell()); - descriptionColumn.setCellValueFactory(cellValueFactory); + descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY); descriptionColumn.setCellFactory(col -> new DescriptionCell()); - baseTypeColumn.setCellValueFactory(cellValueFactory); + baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); - subTypeColumn.setCellValueFactory(cellValueFactory); + subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); subTypeColumn.setCellFactory(col -> new EventTypeCell()); - knownColumn.setCellValueFactory(cellValueFactory); + knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); knownColumn.setCellFactory(col -> new KnownCell()); + eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } @@ -146,7 +161,7 @@ class ListChart extends BorderPane implements TimeLineChart { } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void setEventIDs(List eventIDs) { + void setEventIDs(Collection eventIDs) { table.getItems().setAll(eventIDs); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml index f3b45572c2..87dfee3f19 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml @@ -8,7 +8,7 @@ - + @@ -23,13 +23,13 @@
- - + - + - - + + + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index aa6468298d..363f734d4a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -8,15 +8,11 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.List; import javafx.application.Platform; import javafx.concurrent.Task; -import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.chart.Axis; import org.joda.time.Interval; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; -import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; -import org.sleuthkit.autopsy.timeline.ui.TimeLineView; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView; /** * @param The type of data plotted along the x axis @@ -33,7 +29,9 @@ import org.sleuthkit.autopsy.timeline.ui.TimeLineView; * public abstract class AbstractVisualizationPane> extends BorderPane { */ -public class ListViewPane extends AbstractVisualizationPane implements TimeLineView { +public class ListViewPane extends AbstractTimeLineView { + + private final ListChart listChart; /** * Constructor @@ -42,55 +40,21 @@ public class ListViewPane extends AbstractVisualizationPane getNewUpdateTask() { return new ListUpdateTask(); } @Override - protected String getTickMarkLabel(Long tickValue) { - return ""; - } - - @Override - protected double getTickSpacing() { - return 0; - } - - @Override - protected Axis getXAxis() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - protected Axis getYAxis() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - protected double getAxisMargin() { - return 0; - } - - @Override - protected void clearChartData() { - getChart().clear(); + protected void clearData() { + listChart.clear(); } private static class ListViewSettingsPane extends Parent { @@ -99,7 +63,7 @@ public class ListViewPane extends AbstractVisualizationPane { + private class ListUpdateTask extends ViewRefreshTask { ListUpdateTask() { super("List update task", true); @@ -114,12 +78,12 @@ public class ListViewPane extends AbstractVisualizationPane eventIDs = eventsModel.getEventIDs(); - Platform.runLater(() -> getChart().setEventIDs(eventIDs)); + Platform.runLater(() -> listChart.setEventIDs(eventIDs)); updateMessage("updating ui"); return eventIDs.isEmpty() == false; @@ -132,9 +96,8 @@ public class ListViewPane extends AbstractVisualizationPane