split AbstractVisualizationPane into AbstractTimeLineView and AbstractTimelineChart

CountsViewPane and DetailViewPane extend AbstractTimelineChart, ListViewPane extends AbstractTimeLineView
This commit is contained in:
jmillman 2016-05-17 12:15:41 -04:00
parent b3c6f0a40c
commit 4d14c32a94
13 changed files with 452 additions and 473 deletions

View File

@ -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);
});
}
}
}

View File

@ -18,27 +18,14 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui; 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.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; 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.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList; import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Cursor;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.chart.Axis; import javafx.scene.chart.Axis;
import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart;
@ -46,14 +33,12 @@ import javafx.scene.control.Label;
import javafx.scene.control.OverrunStyle; import javafx.scene.control.OverrunStyle;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.layout.Border; import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths; import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii; import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color; import javafx.scene.paint.Color;
import javafx.scene.text.Font; import javafx.scene.text.Font;
import javafx.scene.text.FontWeight; import javafx.scene.text.FontWeight;
@ -61,15 +46,11 @@ import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment; import javafx.scene.text.TextAlignment;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.MaskerPane;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
/** /**
* Abstract base class for TimeLineChart based visualizations. * 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 * 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.") @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()); 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. * @return The default Tooltip.
*/ */
public static Tooltip getDefaultTooltip() { static public Tooltip getDefaultTooltip() {
return DEFAULT_TOOLTIP; return DEFAULT_TOOLTIP;
} }
/** /**
* Boolean property that holds true if the visualization may not represent * The visualization nodes that are selected.
* the current state of the DB, because, for example, tags have been updated *
* but the vis. was not refreshed. * @return An ObservableList<NodeType> of the nodes that are selected in
* this visualization.
*/ */
private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false); protected ObservableList<NodeType> getSelectedNodes() {
return selectedNodes;
/** }
* 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);
/** /**
* Access to chart data via series * Access to chart data via series
@ -127,54 +105,11 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
//// replacement axis label componenets //// replacement axis label componenets
private final Pane specificLabelPane = new Pane(); // container for the specfic labels in the decluttered axis 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 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(); 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(); 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() { public Pane getSpecificLabelPane() {
return specificLabelPane; return specificLabelPane;
} }
@ -187,53 +122,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
return spacer; 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. * Get the CharType that implements this visualization.
* *
@ -243,15 +131,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
return chart; 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. * Set the ChartType that implements this visualization.
* *
@ -263,28 +142,6 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
setCenter(chart); 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. * 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); 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. * 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(); 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. * 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. * @param controller The TimelineController for this visualization.
*/ */
protected AbstractVisualizationPane(TimeLineController controller) { protected AbstractTimelineChart(TimeLineController controller) {
this.controller = controller; super(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);
// });
createSeries(); createSeries();
selectedNodes.addListener((ListChangeListener.Change<? extends NodeType> change) -> { 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 //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);
}
} }

View File

@ -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 {
}

View File

@ -121,7 +121,7 @@ final public class VisualizationPanel extends BorderPane {
private LoggedTask<Void> histogramTask; private LoggedTask<Void> histogramTask;
private final EventsTree eventsTree; private final EventsTree eventsTree;
private AbstractVisualizationPane<?, ?, ?, ?> visualization; private AbstractTimeLineView visualization;
/* /*
* HBox that contains the histogram bars. * HBox that contains the histogram bars.
@ -580,7 +580,7 @@ final public class VisualizationPanel extends BorderPane {
* AbstractVislualization for one of the correct type. * AbstractVislualization for one of the correct type.
*/ */
private void syncVisualizationMode() { private void syncVisualizationMode() {
AbstractVisualizationPane<?, ?, ?, ?> vizPane; AbstractTimeLineView vizPane;
VisualizationMode visMode = controller.visualizationModeProperty().get(); VisualizationMode visMode = controller.visualizationModeProperty().get();
//make new visualization. //make new visualization.

View File

@ -60,7 +60,7 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; 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; 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 * Platform.runLater(java.lang.Runnable). The FilteredEventsModel should
* encapsulate all need synchronization internally. * 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()); 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) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void clearChartData() { protected void clearData() {
for (XYChart.Series<String, Number> series : dataSeries) { for (XYChart.Series<String, Number> series : dataSeries) {
series.getData().clear(); series.getData().clear();
} }
@ -349,7 +349,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
@NbBundle.Messages({ @NbBundle.Messages({
"CountsViewPane.loggedTask.name=Updating Counts View", "CountsViewPane.loggedTask.name=Updating Counts View",
"CountsViewPane.loggedTask.updatingCounts=Populating visualization"}) "CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
private class CountsUpdateTask extends VisualizationRefreshTask<List<String>> { private class CountsUpdateTask extends ViewRefreshTask<List<String>> {
CountsUpdateTask() { CountsUpdateTask() {
super(Bundle.CountsViewPane_loggedTask_name(), true); super(Bundle.CountsViewPane_loggedTask_name(), true);
@ -374,7 +374,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
List<Interval> intervals = rangeInfo.getIntervals(); List<Interval> intervals = rangeInfo.getIntervals();
//clear old data, and reset ranges and series //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()); updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
int chartMax = 0; int chartMax = 0;
@ -432,7 +432,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
@Override @Override
protected void setDateAxisValues(List<String> categories) { protected void setDateValues(List<String> categories) {
dateAxis.getCategories().setAll(categories); dateAxis.getCategories().setAll(categories);
} }
} }

View File

@ -56,7 +56,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; 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.utils.MappedList;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; 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 * grouped EventStripes, etc, etc. The leaves of the trees are EventClusters or
* SingleEvents. * 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()); 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) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void clearChartData() { protected void clearData() {
getChart().reset(); getChart().reset();
} }
@ -365,9 +365,8 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
"DetailViewPane.loggedTask.continueButton=Continue", "DetailViewPane.loggedTask.continueButton=Continue",
"DetailViewPane.loggedTask.backButton=Back (Cancel)", "DetailViewPane.loggedTask.backButton=Back (Cancel)",
"# {0} - number of events", "# {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?"}) "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() { DetailsUpdateTask() {
super(Bundle.DetailViewPane_loggedTask_name(), true); super(Bundle.DetailViewPane_loggedTask_name(), true);
@ -423,7 +422,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
currentZoomParams = newZoomParams; currentZoomParams = newZoomParams;
//clear the chart and set the horixontal axis //clear the chart and set the horixontal axis
resetChart(eventsModel.getTimeRange()); resetView(eventsModel.getTimeRange());
updateMessage(Bundle.DetailViewPane_loggedTask_updateUI()); updateMessage(Bundle.DetailViewPane_loggedTask_updateUI());
@ -447,7 +446,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
} }
@Override @Override
protected void setDateAxisValues(Interval timeRange) { protected void setDateValues(Interval timeRange) {
detailsChartDateAxis.setRange(timeRange, true); detailsChartDateAxis.setRange(timeRange, true);
pinnedDateAxis.setRange(timeRange, true); pinnedDateAxis.setRange(timeRange, true);
} }

View File

@ -32,6 +32,7 @@ import javafx.scene.control.ContextMenu;
import javafx.scene.control.Control; import javafx.scene.control.Control;
import javafx.scene.control.Skin; import javafx.scene.control.Skin;
import javafx.scene.control.SkinBase; import javafx.scene.control.SkinBase;
import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent; import javafx.scene.input.MouseEvent;
@ -216,6 +217,10 @@ final class DetailsChart extends Control implements TimeLineChart<DateTime> {
return nestedEvents; 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 * Clear any time based UI elements (GuideLines, IntervalSelector,...) from
* this chart. * this chart.

View File

@ -58,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter;
import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; 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; 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 //add a dummy series or the chart is never rendered
setData(FXCollections.observableList(Arrays.asList(new Series<DateTime, Y>()))); setData(FXCollections.observableList(Arrays.asList(new Series<DateTime, Y>())));
Tooltip.install(this, AbstractVisualizationPane.getDefaultTooltip()); Tooltip.install(this, AbstractTimelineChart.getDefaultTooltip());
dateAxis.setAutoRanging(false); dateAxis.setAutoRanging(false);
setLegendVisible(false); setLegendVisible(false);

View File

@ -77,7 +77,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent;
import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; 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 org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider;
import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show; import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show;
import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3; 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 //set up mouse hover effect and tooltip
setOnMouseEntered(mouseEntered -> { setOnMouseEntered(mouseEntered -> {
Tooltip.uninstall(chartLane, AbstractVisualizationPane.getDefaultTooltip()); Tooltip.uninstall(chartLane, AbstractTimelineChart.getDefaultTooltip());
showHoverControls(true); showHoverControls(true);
toFront(); toFront();
}); });
@ -176,7 +176,7 @@ public abstract class EventNodeBase<Type extends TimeLineEvent> extends StackPan
if (parentNode != null) { if (parentNode != null) {
parentNode.showHoverControls(true); parentNode.showHoverControls(true);
} else { } else {
Tooltip.install(chartLane, AbstractVisualizationPane.getDefaultTooltip()); Tooltip.install(chartLane, AbstractTimelineChart.getDefaultTooltip());
} }
}); });
setOnMouseClicked(new ClickHandler()); setOnMouseClicked(new ClickHandler());

View File

@ -25,7 +25,6 @@ import javafx.scene.shape.Line;
import org.joda.time.DateTime; import org.joda.time.DateTime;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
/** /**
* Subclass of {@link Line} with appropriate behavior (mouse listeners) to act * 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."}) "GuideLine.tooltip.text={0}\nRight-click to remove.\nDrag to reposition."})
class GuideLine extends Line { 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 Tooltip tooltip = new Tooltip();
private final DetailsChart chart; private final DetailsChart chart;
@ -49,6 +48,7 @@ class GuideLine extends Line {
*/ */
GuideLine(DetailsChart chart) { GuideLine(DetailsChart chart) {
super(0, 0, 0, 0); super(0, 0, 0, 0);
CHART_DEFAULT_TOOLTIP = chart.getDefaultTooltip();
this.chart = chart; this.chart = chart;
Axis<DateTime> xAxis = chart.getXAxis(); Axis<DateTime> xAxis = chart.getXAxis();
endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty()))); endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty())));

View File

@ -5,7 +5,7 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.listvew; package org.sleuthkit.autopsy.timeline.ui.listvew;
import java.util.List; import java.util.Collection;
import javafx.beans.binding.Bindings; import javafx.beans.binding.Bindings;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@ -43,16 +43,30 @@ class ListChart extends BorderPane implements TimeLineChart<Long> {
@FXML @FXML
private TableView<Long> table; 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 TimeLineController controller;
private final TableColumn<Long, Long> idColumn = new TableColumn<>("Event ID");
private final TableColumn<Long, Long> millisColumn = new TableColumn<>("Date/Time"); @FXML
private final TableColumn<Long, Long> iconColumn = new TableColumn<>("Icon"); private TableColumn<Long, Long> idColumn;
private final TableColumn<Long, Long> descriptionColumn = new TableColumn<>("Description");
private final TableColumn<Long, Long> baseTypeColumn = new TableColumn<>("Base Type"); @FXML
private final TableColumn<Long, Long> subTypeColumn = new TableColumn<>("Sub Type"); private TableColumn<Long, Long> millisColumn;
private final TableColumn<Long, Long> knownColumn = new TableColumn<>("Known");
@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) { ListChart(TimeLineController controller) {
this.controller = 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 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'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'.";
// setRowFactory(tableView -> new EventRow()); table.setRowFactory(tableView -> new EventRow());
idColumn.setCellValueFactory(cellValueFactory); idColumn.setCellValueFactory(CELL_VALUE_FACTORY);
millisColumn.setCellValueFactory(cellValueFactory); millisColumn.setCellValueFactory(CELL_VALUE_FACTORY);
millisColumn.setCellFactory(col -> new EpochMillisCell()); millisColumn.setCellFactory(col -> new EpochMillisCell());
iconColumn.setCellValueFactory(cellValueFactory); iconColumn.setCellValueFactory(CELL_VALUE_FACTORY);
iconColumn.setCellFactory(col -> new ImageCell()); iconColumn.setCellFactory(col -> new ImageCell());
descriptionColumn.setCellValueFactory(cellValueFactory); descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY);
descriptionColumn.setCellFactory(col -> new DescriptionCell()); descriptionColumn.setCellFactory(col -> new DescriptionCell());
baseTypeColumn.setCellValueFactory(cellValueFactory); baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); baseTypeColumn.setCellFactory(col -> new BaseTypeCell());
subTypeColumn.setCellValueFactory(cellValueFactory); subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY);
subTypeColumn.setCellFactory(col -> new EventTypeCell()); subTypeColumn.setCellFactory(col -> new EventTypeCell());
knownColumn.setCellValueFactory(cellValueFactory); knownColumn.setCellValueFactory(CELL_VALUE_FACTORY);
knownColumn.setCellFactory(col -> new KnownCell()); knownColumn.setCellFactory(col -> new KnownCell());
eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); 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) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
void setEventIDs(List<Long> eventIDs) { void setEventIDs(Collection<Long> eventIDs) {
table.getItems().setAll(eventIDs); table.getItems().setAll(eventIDs);
} }

View File

@ -8,7 +8,7 @@
<?import javafx.scene.layout.HBox?> <?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.Region?> <?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> <top>
<HBox alignment="CENTER" BorderPane.alignment="CENTER"> <HBox alignment="CENTER" BorderPane.alignment="CENTER">
<children> <children>
@ -23,13 +23,13 @@
<center> <center>
<TableView fx:id="table" tableMenuButtonVisible="true" BorderPane.alignment="CENTER"> <TableView fx:id="table" tableMenuButtonVisible="true" BorderPane.alignment="CENTER">
<columns> <columns>
<TableColumn fx:id="idColumn" editable="false" maxWidth="200.0" prefWidth="75.0" sortable="false" text="ID" /> <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="millisColumn" editable="false" maxWidth="300.0" minWidth="150.0" prefWidth="300.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="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="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="subTypeColumn" editable="false" maxWidth="100.0" minWidth="100.0" prefWidth="100.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="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> </columns>
<columnResizePolicy> <columnResizePolicy>
<TableView fx:constant="CONSTRAINED_RESIZE_POLICY" /> <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />

View File

@ -8,15 +8,11 @@ package org.sleuthkit.autopsy.timeline.ui.listvew;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.Parent; import javafx.scene.Parent;
import javafx.scene.chart.Axis;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
import org.sleuthkit.autopsy.timeline.ui.TimeLineView;
/** /**
* @param <X> The type of data plotted along the x axis * @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, * public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node,
* ChartType extends Region & TimeLineChart<X>> extends BorderPane { * 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 * Constructor
@ -42,55 +40,21 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
*/ */
public ListViewPane(TimeLineController controller) { public ListViewPane(TimeLineController controller) {
super(controller); super(controller);
listChart = new ListChart(controller);
//initialize chart; //initialize chart;
setChart(new ListChart(controller)); setCenter(listChart);
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable()); 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 @Override
protected Task<Boolean> getNewUpdateTask() { protected Task<Boolean> getNewUpdateTask() {
return new ListUpdateTask(); return new ListUpdateTask();
} }
@Override @Override
protected String getTickMarkLabel(Long tickValue) { protected void clearData() {
return ""; listChart.clear();
}
@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();
} }
private static class ListViewSettingsPane extends Parent { 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() { ListUpdateTask() {
super("List update task", true); super("List update task", true);
@ -114,12 +78,12 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
FilteredEventsModel eventsModel = getEventsModel(); FilteredEventsModel eventsModel = getEventsModel();
//clear the chart and set the horixontal axis //clear the chart and set the horixontal axis
resetChart(eventsModel.getTimeRange()); resetView(eventsModel.getTimeRange());
updateMessage("Querying db for events"); updateMessage("Querying db for events");
//get the event stripes to be displayed //get the event stripes to be displayed
List<Long> eventIDs = eventsModel.getEventIDs(); List<Long> eventIDs = eventsModel.getEventIDs();
Platform.runLater(() -> getChart().setEventIDs(eventIDs)); Platform.runLater(() -> listChart.setEventIDs(eventIDs));
updateMessage("updating ui"); updateMessage("updating ui");
return eventIDs.isEmpty() == false; return eventIDs.isEmpty() == false;
@ -132,9 +96,8 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
} }
@Override @Override
protected void setDateAxisValues(Interval timeRange) { protected void setDateValues(Interval timeRange) {
// detailsChartDateAxis.setRange(timeRange, true);
// pinnedDateAxis.setRange(timeRange, true);
} }
} }
} }