From 795c40d169e4b16ceaae51a95eaf92a61dfe33ac Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 23 May 2016 12:54:39 -0400 Subject: [PATCH 1/2] maintain selection when the table contents change --- .../autopsy/timeline/ui/listvew/ListTimeline.java | 11 +++++++++++ .../autopsy/timeline/ui/listvew/ListViewPane.java | 9 +++++++-- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index 77fc831b29..93ca0914a8 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -149,6 +149,10 @@ class ListTimeline extends BorderPane { table.getItems().clear(); } + Long getSelectedEventID() { + return table.getSelectionModel().getSelectedItem(); + } + /** * Set the Collection of events (by ID) to show in the table. * @@ -169,6 +173,13 @@ class ListTimeline extends BorderPane { return table.getSelectionModel().getSelectedItems(); } + void selectEventID(Long selectedEventID) { + //restore selection. + table.scrollTo(selectedEventID); + table.getSelectionModel().select(selectedEventID); + table.requestFocus(); + } + /** * TableCell to show the icon for the type of an event. */ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index 23278f0d86..09b129ed78 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -73,7 +73,7 @@ public class ListViewPane extends AbstractTimeLineView { @Override protected void clearData() { - listChart.clear(); + } private static class ListViewSettingsPane extends Parent { @@ -96,13 +96,18 @@ public class ListViewPane extends AbstractTimeLineView { } FilteredEventsModel eventsModel = getEventsModel(); + Long selectedEventID = listChart.getSelectedEventID(); + //clear the chart and set the horixontal axis resetView(eventsModel.getTimeRange()); updateMessage("Querying db for events"); //get the event stripes to be displayed List eventIDs = eventsModel.getEventIDs(); - Platform.runLater(() -> listChart.setEventIDs(eventIDs)); + Platform.runLater(() -> { + listChart.setEventIDs(eventIDs); + listChart.selectEventID(selectedEventID); + }); updateMessage("updating ui"); return eventIDs.isEmpty() == false; From 1db35dc915f9e7ebcbf80a58cb5463040c8178ed Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 23 May 2016 13:46:14 -0400 Subject: [PATCH 2/2] cleanup ListViewPane.java and ListTimeline.java --- .../timeline/ui/listvew/ListTimeline.java | 128 +++++++----------- .../timeline/ui/listvew/ListViewPane.java | 50 +++---- 2 files changed, 65 insertions(+), 113 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index 93ca0914a8..efab1852c0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -23,9 +23,10 @@ import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Objects; +import java.util.function.Function; import java.util.logging.Level; import javafx.application.Platform; -import javafx.beans.binding.Bindings; +import javafx.beans.binding.StringBinding; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; @@ -57,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.datamodel.TskCoreException; /** - * The inner component that makes up the Lsit view. Manages the table. + * The inner component that makes up the List view. Manages the TableView. */ class ListTimeline extends BorderPane { @@ -100,6 +101,9 @@ class ListTimeline extends BorderPane { } @FXML + @NbBundle.Messages({ + "# {0} - the number of events", + "ListTimeline.evetnCountLabel.text={0} events"}) void initialize() { assert eventCountLabel != null : "fx:id=\"eventCountLabel\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert table != null : "fx:id=\"table\" was not injected: check your FXML file 'ListViewPane.fxml'."; @@ -114,31 +118,44 @@ class ListTimeline extends BorderPane { //override default row with one that provides context menu.S table.setRowFactory(tableView -> new EventRow()); - //remove idColumn (can be used for debugging). + //remove idColumn (can be restored for debugging). table.getColumns().remove(idColumn); - // set up cell and cell-value factories for columns - // + ///// set up cell and cell-value factories for columns millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); - millisColumn.setCellFactory(col -> new EpochMillisCell()); + millisColumn.setCellFactory(col -> new TextEventTableCell(singleEvent -> + TimeLineController.getZonedFormatter().print(singleEvent.getStartMillis()))); iconColumn.setCellValueFactory(CELL_VALUE_FACTORY); iconColumn.setCellFactory(col -> new ImageCell()); descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY); - descriptionColumn.setCellFactory(col -> new DescriptionCell()); + descriptionColumn.setCellFactory(col -> new TextEventTableCell(singleEvent -> + singleEvent.getDescription(DescriptionLoD.FULL))); baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); - baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); + baseTypeColumn.setCellFactory(col -> new TextEventTableCell(singleEvent -> + singleEvent.getEventType().getBaseType().getDisplayName())); subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); - subTypeColumn.setCellFactory(col -> new EventTypeCell()); + subTypeColumn.setCellFactory(col -> new TextEventTableCell(singleEvent -> + singleEvent.getEventType().getDisplayName())); knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); - knownColumn.setCellFactory(col -> new KnownCell()); + knownColumn.setCellFactory(col -> new TextEventTableCell(singleEvent -> + singleEvent.getKnown().getName())); - //bind event count lable no number of items in table - eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); + //bind event count label to number of items in the table + eventCountLabel.textProperty().bind(new StringBinding() { + { + bind(table.getItems()); + } + + @Override + protected String computeValue() { + return Bundle.ListTimeline_evetnCountLabel_text(table.getItems().size()); + } + }); } /** @@ -149,6 +166,11 @@ class ListTimeline extends BorderPane { table.getItems().clear(); } + /** + * Get the selected event ID. + * + * @return The selected event ID. + */ Long getSelectedEventID() { return table.getSelectionModel().getSelectedItem(); } @@ -173,6 +195,11 @@ class ListTimeline extends BorderPane { return table.getSelectionModel().getSelectedItems(); } + /** + * Set the ID of the event that is selected. + * + * @param selectedEventID The ID of the event that should be selected. + */ void selectEventID(Long selectedEventID) { //restore selection. table.scrollTo(selectedEventID); @@ -198,86 +225,23 @@ class ListTimeline extends BorderPane { } /** - * TableCell to show the full description for an event. + * TableCell to show text derived from a SingleEvent by the given Funtion. */ - private class DescriptionCell extends EventTableCell { + private class TextEventTableCell extends EventTableCell { - @Override - protected void updateItem(Long item, boolean empty) { - super.updateItem(item, empty); + private final Function textSupplier; - if (empty || item == null) { - setText(""); - } else { - setText(getEvent().getDescription(DescriptionLoD.FULL)); - } + TextEventTableCell(Function textSupplier) { + this.textSupplier = textSupplier; } - } - - /** - * TableCell to show the base type of an event. - */ - private class BaseTypeCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - if (empty || item == null) { - setText(""); + setText(null); } else { - setText(getEvent().getEventType().getBaseType().getDisplayName()); - } - } - } - - /** - * TableCell to show the sub type of an event. - */ - private class EventTypeCell extends EventTableCell { - - @Override - protected void updateItem(Long item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - setText(""); - } else { - setText(getEvent().getEventType().getDisplayName()); - } - } - } - - /** - * TableCell to show the known state of the file backing an event. - */ - private class KnownCell extends EventTableCell { - - @Override - protected void updateItem(Long item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - setText(""); - } else { - setText(getEvent().getKnown().getName()); - } - } - } - - /** - * TableCell to show the (start) time of an event. - */ - private class EpochMillisCell extends EventTableCell { - - @Override - protected void updateItem(Long item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - setText(""); - } else { - setText(TimeLineController.getZonedFormatter().print(getEvent().getStartMillis())); + setText(textSupplier.apply(getEvent())); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java index 09b129ed78..91762abd7c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -29,23 +29,11 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView; /** - * @param The type of data plotted along the x axis - * @param The type of data plotted along the y axis - * @param The type of nodes used to represent data items - * @param The type of the TimeLineChart this class uses to plot - * the data. Must extend Region. - * - * TODO: this is becoming (too?) closely tied to the notion that there is a - * XYChart doing the rendering. Is this a good idea? -jm - * - * TODO: pull up common history context menu items out of derived classes? -jm - * - * public abstract class AbstractVisualizationPane> extends BorderPane { + * An AbstractTimeLineView that uses a TableView to represent the events. */ public class ListViewPane extends AbstractTimeLineView { - private final ListTimeline listChart; + private final ListTimeline listTimeline; /** * Constructor @@ -54,15 +42,15 @@ public class ListViewPane extends AbstractTimeLineView { */ public ListViewPane(TimeLineController controller) { super(controller); - listChart = new ListTimeline(controller); + listTimeline = new ListTimeline(controller); //initialize chart; - setCenter(listChart); + setCenter(listTimeline); setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable()); //keep controller's list of selected event IDs in sync with this list's - listChart.getSelectedEventIDs().addListener((Observable selectedIDs) -> { - controller.selectEventIDs(listChart.getSelectedEventIDs()); + listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> { + controller.selectEventIDs(listTimeline.getSelectedEventIDs()); }); } @@ -73,13 +61,10 @@ public class ListViewPane extends AbstractTimeLineView { @Override protected void clearData() { - + listTimeline.clear(); } private static class ListViewSettingsPane extends Parent { - - ListViewSettingsPane() { - } } private class ListUpdateTask extends ViewRefreshTask { @@ -90,26 +75,30 @@ public class ListViewPane extends AbstractTimeLineView { @Override protected Boolean call() throws Exception { - super.call(); //To change body of generated methods, choose Tools | Templates. + super.call(); if (isCancelled()) { return null; } + FilteredEventsModel eventsModel = getEventsModel(); - Long selectedEventID = listChart.getSelectedEventID(); + //grab the currently selected event + Long selectedEventID = listTimeline.getSelectedEventID(); - //clear the chart and set the horixontal axis + //clear the chart and set the time range. resetView(eventsModel.getTimeRange()); - updateMessage("Querying db for events"); - //get the event stripes to be displayed + updateMessage("Querying DB for events"); + //get the IDs of th events to be displayed List eventIDs = eventsModel.getEventIDs(); + updateMessage("Updating UI"); Platform.runLater(() -> { - listChart.setEventIDs(eventIDs); - listChart.selectEventID(selectedEventID); + //put the event IDs into the table. + listTimeline.setEventIDs(eventIDs); + //restore the selected event + listTimeline.selectEventID(selectedEventID); }); - updateMessage("updating ui"); return eventIDs.isEmpty() == false; } @@ -122,6 +111,5 @@ public class ListViewPane extends AbstractTimeLineView { @Override protected void setDateValues(Interval timeRange) { } - } }