cleanup in VisualizationPanel

This commit is contained in:
jmillman 2016-05-06 13:23:06 -04:00
parent 060c912336
commit 9c0d549c51

View File

@ -70,6 +70,7 @@ import org.joda.time.Interval;
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.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.VisualizationMode;
@ -88,10 +89,10 @@ import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree;
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
/**
* A container for an {@link AbstractVisualizationPane}, has a toolbar on top to
* hold settings widgets supplied by contained {@link AbstAbstractVisualization}
* and, the histogram / time selection on bottom. Also supplies containers for
* replacement axis to contained {@link AbstractAbstractVisualization}
* A container for an AbstractVisualizationPane, has a Toolbar on top to hold
* settings widgets supplied by contained AbstractVisualizationPane and, the
* histogram / time selection on bottom. Also supplies containers for
* replacement axis to contained AbstractVisualizationPane
*
* TODO: refactor common code out of histogram and CountsView? -jm
*/
@ -101,22 +102,24 @@ final public class VisualizationPanel extends BorderPane {
private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); // NON-NLS
private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); // NON-NLS
private static final Background background = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY));
private static final Background GRAY_BACKGROUND = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY));
@GuardedBy("this")
private LoggedTask<Void> histogramTask;
private final EventsTree eventsTree;
private AbstractVisualizationPane<?, ?, ?, ?> visualization;
//// range slider and histogram componenets
/**
* hbox that contains the histogram bars. //TODO: abstract this into a
* seperate class, and/or use a real bar chart?
/*
* HBox that contains the histogram bars.
*
* //TODO: Abstract this into a seperate class, and/or use a real bar
* chart? -jm
*/
@FXML
private HBox histogramBox;
/**
* stack pane that superimposes rangeslider over histogram
/*
* Stack pane that superimposes rangeslider over histogram
*/
@FXML
private StackPane rangeHistogramStack;
@ -126,7 +129,6 @@ final public class VisualizationPanel extends BorderPane {
//// time range selection components
@FXML
private MenuButton zoomMenuButton;
@FXML
private Button zoomOutButton;
@FXML
@ -144,6 +146,8 @@ final public class VisualizationPanel extends BorderPane {
@FXML
private ToolBar toolBar;
@FXML
private Label visualizationModeLabel;
@FXML
private ToggleButton countsToggle;
@FXML
private ToggleButton detailsToggle;
@ -151,20 +155,25 @@ final public class VisualizationPanel extends BorderPane {
private Button snapShotButton;
@FXML
private Button refreshButton;
@FXML
private Label visualizationModeLabel;
/**
* wraps contained visualization so that we can show notifications over it.
/*
* Wraps contained visualization so that we can show notifications over it.
*/
private final NotificationPane notificationPane = new NotificationPane();
/*
* Boolean property that holds true if the visualziation 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 needsRefresh = new ReadOnlyBooleanWrapper(false);
private final TimeLineController controller;
private final FilteredEventsModel filteredEvents;
/**
* listen to change in range slider selected time and push to controller.
* waits until the user releases thumb to send controller.
* Listen to changes in the range slider selection and forward to the
* controller. Waits until the user releases thumb to send to controller.
*/
private final InvalidationListener rangeSliderListener = new InvalidationListener() {
@Override
@ -197,20 +206,21 @@ final public class VisualizationPanel extends BorderPane {
private final InvalidationListener startListener = new PickerListener(() -> startPicker, Interval::withStartMillis);
/**
* convert the given LocalDateTime to epoch millis USING THE CURERNT
* TIMEZONE FROM TIMELINECONTROLLER
* Convert the given LocalDateTime to epoch millis USING THE CURRENT
* TIMEZONE FROM THE TIMELINECONTROLLER
*
* @param localDateTime
* @param localDateTime The LocalDateTime to convert to millis since the
* Unix epoch.
*
* @return the given localdatetime as epoch millis
* @return the given LocalDateTime as epoch millis
*/
private static long localDateTimeToEpochMilli(LocalDateTime localDateTime) {
return localDateTime.atZone(TimeLineController.getTimeZoneID()).toInstant().toEpochMilli();
}
/**
* Convert the given epoch millis to a LocalDateTime USING THE CURERNT
* TIMEZONE FROM TIMELINECONTROLLER
* Convert the given "millis from the Unix Epoch" to a LocalDateTime USING
* THE CURRENT TIMEZONE FROM THE TIMELINECONTROLLER
*
* @param millis The milliseconds to convert.
*
@ -220,6 +230,12 @@ final public class VisualizationPanel extends BorderPane {
return LocalDateTime.ofInstant(Instant.ofEpochMilli(millis), TimeLineController.getTimeZoneID());
}
/**
* Constructor
*
* @param controller The TimeLineController for this VisualizationPanel
* @param eventsTree The EventsTree this VisualizationPanel hosts.
*/
public VisualizationPanel(@Nonnull TimeLineController controller, @Nonnull EventsTree eventsTree) {
this.controller = controller;
this.filteredEvents = controller.getEventsModel();
@ -227,14 +243,15 @@ final public class VisualizationPanel extends BorderPane {
FXMLConstructor.construct(this, "VisualizationPanel.fxml"); // NON-NLS
}
@FXML // This method is called by the FXMLLoader when initialization is complete
@FXML
@NbBundle.Messages({
"VisualizationPanel.visualizationModeLabel.text=Visualization Mode:",
"VisualizationPanel.startLabel.text=Start:",
"VisualizationPanel.endLabel.text=End:",
"VisualizationPanel.countsToggle.text=Counts",
"VisualizationPanel.detailsToggle.text=Details",
"VisualizationPanel.zoomMenuButton.text=Zoom in/out to"})
"VisualizationPanel.zoomMenuButton.text=Zoom in/out to",
"VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date."})
void initialize() {
assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
assert histogramBox != null : "fx:id=\"histogramBox\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
@ -245,7 +262,15 @@ final public class VisualizationPanel extends BorderPane {
//configure notification pane
notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
notificationPane.getActions().setAll(new Refresh());
setCenter(notificationPane);
needsRefresh.addListener(observable -> {
if (needsRefresh.get()) {
notificationPane.show(Bundle.VisualizationPanel_tagsAddedOrDeleted(), new ImageView(INFORMATION));
} else {
notificationPane.hide();
}
});
//configure visualization mode toggle
visualizationModeLabel.setText(Bundle.VisualizationPanel_visualizationModeLabel_text());
@ -354,13 +379,22 @@ final public class VisualizationPanel extends BorderPane {
}
/**
* Set the given AbstractVisualizationPane as the one hosted by this
* VisualizationPanel.
*
* @param newViz The AbstractVisualizationPane to host.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI)
private synchronized void setVisualization(final AbstractVisualizationPane<?, ?, ?, ?> newViz) {
Platform.runLater(() -> {
//clear out old vis.
if (visualization != null) {
toolBar.getItems().removeAll(visualization.getSettingsNodes());
visualization.dispose();
}
//setup new vis.
visualization = newViz;
visualization.update();
toolBar.getItems().addAll(newViz.getSettingsNodes());
@ -374,7 +408,6 @@ final public class VisualizationPanel extends BorderPane {
}
visualization.hasEvents.addListener((observable, oldValue, newValue) -> {
if (newValue == false) {
notificationPane.setContent(
new StackPane(visualization,
new Region() {
@ -388,36 +421,55 @@ final public class VisualizationPanel extends BorderPane {
notificationPane.setContent(visualization);
}
});
});
setNeedsRefresh(false);
}
/**
* Handle TagsUpdatedEvents.
*
* Mark that the visualization needs to be refreshed.
*
* NOTE: This VisualizationPanel must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The TagsUpdatedEvent to handle.
*/
@Subscribe
public void handleTimeLineTagEvent(TagsUpdatedEvent event) {
setNeedsRefresh(true);
}
/**
* Handle RefreshRequestedEvent.
*
* Mark that the visualization has been refreshed.
*
* NOTE: This VisualizationPanel must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The TagsUpdatedEvent to handle.
*/
@Subscribe
public void handleRefreshRequestedEvent(RefreshRequestedEvent event) {
setNeedsRefresh(false);
}
@NbBundle.Messages("VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date.")
/**
* Set if the visualziation may not represent the current state of the DB,
* because, for example, tags have been updated.
*
* @param needsRefresh True if the visualization may not represent the
* current state of the DB.
*/
private void setNeedsRefresh(Boolean needsRefresh) {
Platform.runLater(() -> {
VisualizationPanel.this.needsRefresh.set(needsRefresh);
if (needsRefresh) {
notificationPane.getActions().setAll(new Refresh());
notificationPane.show(Bundle.VisualizationPanel_tagsAddedOrDeleted(), new ImageView(INFORMATION));
} else {
notificationPane.hide();
}
});
Platform.runLater(() -> VisualizationPanel.this.needsRefresh.set(needsRefresh));
}
/**
* Refresh the Histogram to represent the current state of the DB.
*/
synchronized private void refreshHistorgram() {
if (histogramTask != null) {
histogramTask.cancel(true);
}
@ -482,7 +534,7 @@ final public class VisualizationPanel extends BorderPane {
bar.prefHeightProperty().bind(histogramBox.heightProperty().multiply(Math.log(bin)).divide(fMax));
bar.setMaxHeight(USE_PREF_SIZE);
bar.setMinHeight(USE_PREF_SIZE);
bar.setBackground(background);
bar.setBackground(GRAY_BACKGROUND);
bar.setOnMouseEntered((MouseEvent event) -> {
Tooltip.install(bar, new Tooltip(bin.toString()));
});
@ -664,12 +716,14 @@ final public class VisualizationPanel extends BorderPane {
}
}
/**
* Action that refreshes the Visualization.
*/
private class Refresh extends Action {
@NbBundle.Messages({
"VisualizationPanel.refresh.text=Refresh",
"VisualizationPanel.refresh.longText=Refresh the visualization to include information that is in the database but not visualized, such as newly updated tags."})
Refresh() {
super(Bundle.VisualizationPanel_refresh_text());
setLongText(Bundle.VisualizationPanel_refresh_longText());