mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 11:07:43 +00:00
split AbstractVisualizationPane into AbstractTimeLineView and AbstractTimelineChart
CountsViewPane and DetailViewPane extend AbstractTimelineChart, ListViewPane extends AbstractTimeLineView
This commit is contained in:
parent
b3c6f0a40c
commit
4d14c32a94
@ -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<Node> 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<Boolean> 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<Boolean> getNewUpdateTask();
|
||||
|
||||
/**
|
||||
* Get a List of nodes containing settings widgets to insert into this
|
||||
* visualization's header.
|
||||
*
|
||||
* @return The List of settings Nodes.
|
||||
*/
|
||||
protected List<Node> 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<Node> 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 <AxisValuesType> The type of a single object that can represent
|
||||
* the range of data displayed along the X-Axis.
|
||||
*/
|
||||
protected abstract class ViewRefreshTask<AxisValuesType> extends LoggedTask<Boolean> {
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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<X, Y, NodeType extends Node, ChartType extends Region & TimeLineChart<X>> extends BorderPane implements TimeLineView {
|
||||
public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartType extends Region & TimeLineChart<X>> 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<X, Y, NodeType extends Node, Cha
|
||||
*
|
||||
* @return The default Tooltip.
|
||||
*/
|
||||
public static Tooltip getDefaultTooltip() {
|
||||
static public Tooltip getDefaultTooltip() {
|
||||
return DEFAULT_TOOLTIP;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* The visualization nodes that are selected.
|
||||
*
|
||||
* @return An ObservableList<NodeType> 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<NodeType> getSelectedNodes() {
|
||||
return selectedNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to chart data via series
|
||||
@ -127,54 +105,11 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
//// replacement axis label componenets
|
||||
private final Pane specificLabelPane = new Pane(); // container for the specfic labels in the decluttered axis
|
||||
private final Pane contextLabelPane = new Pane();// container for the contextual labels in the decluttered axis
|
||||
// container for the contextual labels in the decluttered axis
|
||||
private final Region spacer = new Region();
|
||||
|
||||
/**
|
||||
* task used to reload the content of this visualization
|
||||
*/
|
||||
private Task<Boolean> updateTask;
|
||||
|
||||
final private TimeLineController controller;
|
||||
final private FilteredEventsModel filteredEvents;
|
||||
|
||||
final private ObservableList<NodeType> 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<X, Y, NodeType extends Node, Cha
|
||||
return spacer;
|
||||
}
|
||||
|
||||
/**
|
||||
* The visualization nodes that are selected.
|
||||
*
|
||||
* @return An ObservableList<NodeType> of the nodes that are selected in
|
||||
* this visualization.
|
||||
*/
|
||||
protected ObservableList<NodeType> getSelectedNodes() {
|
||||
return selectedNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* List of Nodes to insert into the toolbar. This should be set in an
|
||||
* implementations constructor.
|
||||
*/
|
||||
private List<Node> settingsNodes;
|
||||
|
||||
/**
|
||||
* Get a List of nodes containing settings widgets to insert into this
|
||||
* visualization's header.
|
||||
*
|
||||
* @return The List of settings Nodes.
|
||||
*/
|
||||
protected List<Node> 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<Node> 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<X, Y, NodeType extends Node, Cha
|
||||
return chart;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the FilteredEventsModel for this visualization.
|
||||
*
|
||||
* @return The FilteredEventsModel for this visualization.
|
||||
*/
|
||||
protected FilteredEventsModel getEventsModel() {
|
||||
return filteredEvents;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the ChartType that implements this visualization.
|
||||
*
|
||||
@ -263,28 +142,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
setCenter(chart);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply this visualization's 'selection effect' to the given node.
|
||||
*
|
||||
@ -324,15 +181,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
*/
|
||||
abstract protected void applySelectionEffect(NodeType node, Boolean applied);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
abstract protected Task<Boolean> getNewUpdateTask();
|
||||
|
||||
/**
|
||||
* Get the label that should be used for a tick mark at the given value.
|
||||
*
|
||||
@ -373,74 +221,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
*/
|
||||
abstract protected double getAxisMargin();
|
||||
|
||||
/**
|
||||
* Clear all data items from this chart.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
abstract protected void clearChartData();
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
});
|
||||
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<X, Y, NodeType extends Node, Cha
|
||||
*
|
||||
* @param controller The TimelineController for this visualization.
|
||||
*/
|
||||
protected AbstractVisualizationPane(TimeLineController controller) {
|
||||
this.controller = controller;
|
||||
this.filteredEvents = controller.getEventsModel();
|
||||
this.filteredEvents.registerForEvents(this);
|
||||
this.filteredEvents.zoomParametersProperty().addListener(updateListener);
|
||||
// Platform.runLater(() -> {
|
||||
// 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<? extends NodeType> change) -> {
|
||||
@ -496,10 +261,8 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
}
|
||||
});
|
||||
|
||||
TimeLineController.getTimeZone().addListener(updateListener);
|
||||
|
||||
//show tooltip text in status bar
|
||||
hoverProperty().addListener(hoverProp -> controller.setStatusMessage(isHover() ? DEFAULT_TOOLTIP.getText() : ""));
|
||||
hoverProperty().addListener(hoverProp -> controller.setStatusMessage(isHover() ? getDefaultTooltip().getText() : ""));
|
||||
|
||||
}
|
||||
|
||||
@ -690,116 +453,4 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for Tasks that refresh a visualization when the view settings
|
||||
* change.
|
||||
*
|
||||
* @param <AxisValuesType> The type of a single object that can represent
|
||||
* the range of data displayed along the X-Axis.
|
||||
*/
|
||||
abstract protected class VisualizationRefreshTask<AxisValuesType> extends LoggedTask<Boolean> {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
||||
}
|
@ -121,7 +121,7 @@ final public class VisualizationPanel extends BorderPane {
|
||||
private LoggedTask<Void> 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.
|
||||
|
@ -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<String, Number, Node, EventCountsChart> {
|
||||
public class CountsViewPane extends AbstractTimelineChart<String, Number, Node, EventCountsChart> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName());
|
||||
|
||||
@ -170,7 +170,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@Override
|
||||
protected void clearChartData() {
|
||||
protected void clearData() {
|
||||
for (XYChart.Series<String, Number> series : dataSeries) {
|
||||
series.getData().clear();
|
||||
}
|
||||
@ -349,7 +349,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
|
||||
@NbBundle.Messages({
|
||||
"CountsViewPane.loggedTask.name=Updating Counts View",
|
||||
"CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
|
||||
private class CountsUpdateTask extends VisualizationRefreshTask<List<String>> {
|
||||
private class CountsUpdateTask extends ViewRefreshTask<List<String>> {
|
||||
|
||||
CountsUpdateTask() {
|
||||
super(Bundle.CountsViewPane_loggedTask_name(), true);
|
||||
@ -374,7 +374,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
|
||||
List<Interval> 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<String, Number, No
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDateAxisValues(List<String> categories) {
|
||||
protected void setDateValues(List<String> categories) {
|
||||
dateAxis.getCategories().setAll(categories);
|
||||
}
|
||||
}
|
||||
|
@ -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<DateTime, EventStripe, EventNodeBase<?>, DetailsChart> {
|
||||
public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe, EventNodeBase<?>, DetailsChart> {
|
||||
|
||||
private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName());
|
||||
|
||||
@ -218,7 +218,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
@Override
|
||||
protected void clearChartData() {
|
||||
protected void clearData() {
|
||||
getChart().reset();
|
||||
}
|
||||
|
||||
@ -365,9 +365,8 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
"DetailViewPane.loggedTask.continueButton=Continue",
|
||||
"DetailViewPane.loggedTask.backButton=Back (Cancel)",
|
||||
"# {0} - number of events",
|
||||
|
||||
"DetailViewPane.loggedTask.prompt=You are about to show details for {0} events. This might be very slow and could exhaust available memory.\n\nDo you want to continue?"})
|
||||
private class DetailsUpdateTask extends VisualizationRefreshTask<Interval> {
|
||||
private class DetailsUpdateTask extends ViewRefreshTask<Interval> {
|
||||
|
||||
DetailsUpdateTask() {
|
||||
super(Bundle.DetailViewPane_loggedTask_name(), true);
|
||||
@ -423,7 +422,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
currentZoomParams = newZoomParams;
|
||||
|
||||
//clear the chart and set the horixontal axis
|
||||
resetChart(eventsModel.getTimeRange());
|
||||
resetView(eventsModel.getTimeRange());
|
||||
|
||||
updateMessage(Bundle.DetailViewPane_loggedTask_updateUI());
|
||||
|
||||
@ -447,7 +446,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDateAxisValues(Interval timeRange) {
|
||||
protected void setDateValues(Interval timeRange) {
|
||||
detailsChartDateAxis.setRange(timeRange, true);
|
||||
pinnedDateAxis.setRange(timeRange, true);
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Control;
|
||||
import javafx.scene.control.Skin;
|
||||
import javafx.scene.control.SkinBase;
|
||||
import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.input.MouseEvent;
|
||||
@ -216,6 +217,10 @@ final class DetailsChart extends Control implements TimeLineChart<DateTime> {
|
||||
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.
|
||||
|
@ -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<Y extends TimeLineEvent> extends XYChart<DateTim
|
||||
//add a dummy series or the chart is never rendered
|
||||
setData(FXCollections.observableList(Arrays.asList(new Series<DateTime, Y>())));
|
||||
|
||||
Tooltip.install(this, AbstractVisualizationPane.getDefaultTooltip());
|
||||
Tooltip.install(this, AbstractTimelineChart.getDefaultTooltip());
|
||||
|
||||
dateAxis.setAutoRanging(false);
|
||||
setLegendVisible(false);
|
||||
|
@ -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<Type extends TimeLineEvent> 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<Type extends TimeLineEvent> extends StackPan
|
||||
if (parentNode != null) {
|
||||
parentNode.showHoverControls(true);
|
||||
} else {
|
||||
Tooltip.install(chartLane, AbstractVisualizationPane.getDefaultTooltip());
|
||||
Tooltip.install(chartLane, AbstractTimelineChart.getDefaultTooltip());
|
||||
}
|
||||
});
|
||||
setOnMouseClicked(new ClickHandler());
|
||||
|
@ -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<DateTime> xAxis = chart.getXAxis();
|
||||
endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty())));
|
||||
|
@ -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<Long> {
|
||||
@FXML
|
||||
private TableView<Long> table;
|
||||
|
||||
Callback<TableColumn.CellDataFeatures<Long, Long>, ObservableValue<Long>> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue());
|
||||
private static final Callback<TableColumn.CellDataFeatures<Long, Long>, ObservableValue<Long>> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue());
|
||||
|
||||
private final TimeLineController controller;
|
||||
private final TableColumn<Long, Long> idColumn = new TableColumn<>("Event ID");
|
||||
private final TableColumn<Long, Long> millisColumn = new TableColumn<>("Date/Time");
|
||||
private final TableColumn<Long, Long> iconColumn = new TableColumn<>("Icon");
|
||||
private final TableColumn<Long, Long> descriptionColumn = new TableColumn<>("Description");
|
||||
private final TableColumn<Long, Long> baseTypeColumn = new TableColumn<>("Base Type");
|
||||
private final TableColumn<Long, Long> subTypeColumn = new TableColumn<>("Sub Type");
|
||||
private final TableColumn<Long, Long> knownColumn = new TableColumn<>("Known");
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> idColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> millisColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> iconColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> descriptionColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> baseTypeColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> subTypeColumn;
|
||||
|
||||
@FXML
|
||||
private TableColumn<Long, Long> knownColumn;
|
||||
|
||||
ListChart(TimeLineController controller) {
|
||||
this.controller = controller;
|
||||
@ -71,26 +85,27 @@ class ListChart extends BorderPane implements TimeLineChart<Long> {
|
||||
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<Long> {
|
||||
}
|
||||
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
void setEventIDs(List<Long> eventIDs) {
|
||||
void setEventIDs(Collection<Long> eventIDs) {
|
||||
table.getItems().setAll(eventIDs);
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
|
||||
<fx:root maxHeight="-Infinity" minHeight="-Infinity" minWidth="-Infinity" type="BorderPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<HBox alignment="CENTER" BorderPane.alignment="CENTER">
|
||||
<children>
|
||||
@ -23,13 +23,13 @@
|
||||
<center>
|
||||
<TableView fx:id="table" tableMenuButtonVisible="true" BorderPane.alignment="CENTER">
|
||||
<columns>
|
||||
<TableColumn fx:id="idColumn" editable="false" maxWidth="200.0" prefWidth="75.0" sortable="false" text="ID" />
|
||||
<TableColumn fx:id="millisColumn" editable="false" maxWidth="300.0" minWidth="150.0" prefWidth="300.0" resizable="false" sortable="false" text="Date/Time" />
|
||||
<TableColumn fx:id="millisColumn" editable="false" maxWidth="200.0" minWidth="150.0" prefWidth="150.0" resizable="false" sortable="false" text="Date/Time" />
|
||||
<TableColumn fx:id="iconColumn" editable="false" maxWidth="36.0" minWidth="36.0" prefWidth="36.0" resizable="false" sortable="false" text="Icon" />
|
||||
<TableColumn fx:id="descriptionColumn" editable="false" maxWidth="1000.0" minWidth="100.0" prefWidth="200.0" sortable="false" text="Description" />
|
||||
<TableColumn fx:id="descriptionColumn" editable="false" maxWidth="1000.0" minWidth="100.0" prefWidth="300.0" sortable="false" text="Description" />
|
||||
<TableColumn fx:id="baseTypeColumn" editable="false" maxWidth="75.0" minWidth="75.0" prefWidth="75.0" sortable="false" text="BaseType" />
|
||||
<TableColumn fx:id="subTypeColumn" editable="false" maxWidth="75.0" minWidth="75.0" prefWidth="75.0" sortable="false" text="SubType" />
|
||||
<TableColumn fx:id="knownColumn" editable="false" maxWidth="100.0" minWidth="75.0" prefWidth="75.0" sortable="false" text="Known" />
|
||||
<TableColumn fx:id="subTypeColumn" editable="false" maxWidth="100.0" minWidth="100.0" prefWidth="100.0" sortable="false" text="SubType" />
|
||||
<TableColumn fx:id="knownColumn" editable="false" maxWidth="75.0" minWidth="75.0" prefWidth="75.0" sortable="false" text="Known" />
|
||||
<TableColumn fx:id="idColumn" editable="false" maxWidth="150.0" minWidth="50.0" prefWidth="75.0" sortable="false" text="ID" />
|
||||
</columns>
|
||||
<columnResizePolicy>
|
||||
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
|
||||
|
@ -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 <X> The type of data plotted along the x axis
|
||||
@ -33,7 +29,9 @@ import org.sleuthkit.autopsy.timeline.ui.TimeLineView;
|
||||
* public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node,
|
||||
* ChartType extends Region & TimeLineChart<X>> extends BorderPane {
|
||||
*/
|
||||
public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, Node, ListChart> implements TimeLineView {
|
||||
public class ListViewPane extends AbstractTimeLineView {
|
||||
|
||||
private final ListChart listChart;
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
@ -42,55 +40,21 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
|
||||
*/
|
||||
public ListViewPane(TimeLineController controller) {
|
||||
super(controller);
|
||||
listChart = new ListChart(controller);
|
||||
|
||||
//initialize chart;
|
||||
setChart(new ListChart(controller));
|
||||
setCenter(listChart);
|
||||
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean isTickBold(Long value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void applySelectionEffect(Node node, Boolean applied) {
|
||||
// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Task<Boolean> getNewUpdateTask() {
|
||||
return new ListUpdateTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTickMarkLabel(Long tickValue) {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected double getTickSpacing() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Axis<Long> getXAxis() {
|
||||
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Axis<SingleEvent> 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<Long, SingleEvent, N
|
||||
}
|
||||
}
|
||||
|
||||
private class ListUpdateTask extends VisualizationRefreshTask<Interval> {
|
||||
private class ListUpdateTask extends ViewRefreshTask<Interval> {
|
||||
|
||||
ListUpdateTask() {
|
||||
super("List update task", true);
|
||||
@ -114,12 +78,12 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
|
||||
FilteredEventsModel eventsModel = getEventsModel();
|
||||
|
||||
//clear the chart and set the horixontal axis
|
||||
resetChart(eventsModel.getTimeRange());
|
||||
resetView(eventsModel.getTimeRange());
|
||||
|
||||
updateMessage("Querying db for events");
|
||||
//get the event stripes to be displayed
|
||||
List<Long> 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<Long, SingleEvent, N
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setDateAxisValues(Interval timeRange) {
|
||||
// detailsChartDateAxis.setRange(timeRange, true);
|
||||
// pinnedDateAxis.setRange(timeRange, true);
|
||||
protected void setDateValues(Interval timeRange) {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user