flesh out ListViewPane, keep events sorted by date

This commit is contained in:
jmillman 2016-05-13 16:57:33 -04:00
parent 695c5c69c3
commit 2858421d46
6 changed files with 163 additions and 57 deletions

View File

@ -279,7 +279,7 @@ public final class FilteredEventsModel {
return repo.getTagCountsByTagName(eventIDsWithTags);
}
public Set<Long> getEventIDs(Interval timeRange, Filter filter) {
public List<Long> getEventIDs(Interval timeRange, Filter filter) {
final Interval overlap;
final RootFilter intersect;
synchronized (this) {
@ -290,7 +290,7 @@ public final class FilteredEventsModel {
return repo.getEventIDs(overlap, intersect);
}
public Set<Long> getEventIDs() {
public List<Long> getEventIDs() {
return getEventIDs(requestedTimeRange.get(), requestedFilter.get());
}

View File

@ -343,18 +343,19 @@ public class EventDB {
return result;
}
Set<Long> getEventIDs(Interval timeRange, RootFilter filter) {
List<Long> getEventIDs(Interval timeRange, RootFilter filter) {
return getEventIDs(timeRange.getStartMillis() / 1000, timeRange.getEndMillis() / 1000, filter);
}
Set<Long> getEventIDs(Long startTime, Long endTime, RootFilter filter) {
List<Long> getEventIDs(Long startTime, Long endTime, RootFilter filter) {
if (Objects.equals(startTime, endTime)) {
endTime++;
}
Set<Long> resultIDs = new HashSet<>();
ArrayList<Long> resultIDs = new ArrayList<>();
DBLock.lock();
final String query = "SELECT events.event_id AS event_id FROM events" + useHashHitTablesHelper(filter) + useTagTablesHelper(filter) + " WHERE time >= " + startTime + " AND time <" + endTime + " AND " + SQLHelper.getSQLWhere(filter); // NON-NLS
final String query = "SELECT events.event_id AS event_id FROM events" + useHashHitTablesHelper(filter) + useTagTablesHelper(filter)
+ " WHERE time >= " + startTime + " AND time <" + endTime + " AND " + SQLHelper.getSQLWhere(filter) + " ORDER BY time ASC"; // NON-NLS
try (Statement stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query)) {
while (rs.next()) {
@ -1168,7 +1169,6 @@ public class EventDB {
return useSubTypes ? "sub_type" : "base_type"; //NON-NLS
}
private PreparedStatement prepareStatement(String queryString) throws SQLException {
PreparedStatement prepareStatement = con.prepareStatement(queryString);
preparedStatements.add(prepareStatement);

View File

@ -214,7 +214,7 @@ public class EventsRepository {
idToEventCache.invalidateAll();
}
public Set<Long> getEventIDs(Interval timeRange, RootFilter filter) {
public List<Long> getEventIDs(Interval timeRange, RootFilter filter) {
return eventDB.getEventIDs(timeRange, filter);
}

View File

@ -29,15 +29,12 @@ import java.util.function.Supplier;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuButton;
import javafx.scene.control.TitledPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToolBar;
import javafx.scene.control.Tooltip;
@ -60,8 +57,10 @@ import javax.annotation.Nonnull;
import javax.annotation.concurrent.GuardedBy;
import jfxtras.scene.control.LocalDateTimePicker;
import jfxtras.scene.control.LocalDateTimeTextField;
import jfxtras.scene.control.ToggleGroupValue;
import org.controlsfx.control.NotificationPane;
import org.controlsfx.control.RangeSlider;
import org.controlsfx.control.SegmentedButton;
import org.controlsfx.control.action.Action;
import org.controlsfx.control.action.ActionUtils;
import org.joda.time.DateTime;
@ -162,6 +161,8 @@ final public class VisualizationPanel extends BorderPane {
@FXML
private Label visualizationModeLabel;
@FXML
private SegmentedButton modeSegButton;
@FXML
private ToggleButton countsToggle;
@FXML
private ToggleButton detailsToggle;
@ -261,6 +262,7 @@ final public class VisualizationPanel extends BorderPane {
"VisualizationPanel.endLabel.text=End:",
"VisualizationPanel.countsToggle.text=Counts",
"VisualizationPanel.detailsToggle.text=Details",
"VisualizationPanel.listToggle.text=List",
"VisualizationPanel.zoomMenuButton.text=Zoom in/out to",
"VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date."
})
@ -280,25 +282,18 @@ final public class VisualizationPanel extends BorderPane {
visualizationModeLabel.setText(Bundle.VisualizationPanel_visualizationModeLabel_text());
countsToggle.setText(Bundle.VisualizationPanel_countsToggle_text());
detailsToggle.setText(Bundle.VisualizationPanel_detailsToggle_text());
ChangeListener<Toggle> toggleListener = (ObservableValue<? extends Toggle> observable, Toggle oldValue, Toggle newValue) -> {
if (newValue == null) {
countsToggle.getToggleGroup().selectToggle(oldValue != null ? oldValue : countsToggle);
} else if (newValue == countsToggle && oldValue != null) {
controller.setVisualizationMode(VisualizationMode.COUNTS);
} else if (newValue == detailsToggle && oldValue != null) {
controller.setVisualizationMode(VisualizationMode.DETAIL);
} else if (newValue == listToggle && oldValue != null) {
controller.setVisualizationMode(VisualizationMode.LIST);
}
};
listToggle.setText(Bundle.VisualizationPanel_listToggle_text());
if (countsToggle.getToggleGroup() != null) {
countsToggle.getToggleGroup().selectedToggleProperty().addListener(toggleListener);
} else {
countsToggle.toggleGroupProperty().addListener((Observable toggleGroup) -> {
countsToggle.getToggleGroup().selectedToggleProperty().addListener(toggleListener);
});
}
ToggleGroupValue<VisualizationMode> visModeToggleGroup = new ToggleGroupValue<>();
visModeToggleGroup.add(listToggle, VisualizationMode.LIST);
visModeToggleGroup.add(detailsToggle, VisualizationMode.DETAIL);
visModeToggleGroup.add(countsToggle, VisualizationMode.COUNTS);
modeSegButton.setToggleGroup(visModeToggleGroup);
visModeToggleGroup.valueProperty().addListener((observable, oldVisMode, newValue) -> {
controller.setVisualizationMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : VisualizationMode.COUNTS));
});
controller.visualizationModeProperty().addListener(visualizationMode -> syncVisualizationMode());
syncVisualizationMode();

View File

@ -7,6 +7,7 @@ package org.sleuthkit.autopsy.timeline.ui.listvew;
import java.util.Arrays;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node;
@ -14,50 +15,66 @@ import javafx.scene.chart.Axis;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
import org.sleuthkit.autopsy.timeline.ui.IntervalSelector;
import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD;
/**
*
*/
class ListChart extends TableView<Long> implements TimeLineChart<Long> {
Callback<TableColumn.CellDataFeatures<Long, Long>, ObservableValue<Long>> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue());
private final TimeLineController controller;
private final TableColumn<Long, Long> idColumn = new TableColumn<>();
private final TableColumn<Long, Long> millisColumn = new TableColumn<>();
private final TableColumn<Long, Image> iconColumn = new TableColumn<>();
private final TableColumn<Long, String> descriptionColumn = new TableColumn<>();
private final TableColumn<Long, EventType> baseTypeColumn = new TableColumn<>();
private final TableColumn<Long, EventType> subTypeColumn = new TableColumn<>();
private final TableColumn<Long, TskData.FileKnown> knownColumn = new TableColumn<>();
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");
ListChart(TimeLineController controller) {
this.controller = controller;
getColumns().addAll(Arrays.asList(idColumn, iconColumn, millisColumn));
setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
getColumns().addAll(Arrays.asList(idColumn, millisColumn, iconColumn, descriptionColumn, baseTypeColumn, subTypeColumn, knownColumn));
idColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue()));
setRowFactory(tableView -> new EventRow());
millisColumn.setCellValueFactory(param -> {
return new SimpleObjectProperty<>(controller.getEventsModel().getEventById(param.getValue()).getStartMillis());
});
idColumn.setCellValueFactory(cellValueFactory);
idColumn.setSortable(false);
millisColumn.setCellValueFactory(cellValueFactory);
millisColumn.setCellFactory(col -> new EpochMillisCell());
iconColumn.setCellValueFactory(param -> {
return new SimpleObjectProperty<>(controller.getEventsModel().getEventById(param.getValue()).getEventType().getFXImage());
});
millisColumn.setSortable(false);
iconColumn.setCellValueFactory(cellValueFactory);
iconColumn.setCellFactory(col -> new ImageCell());
iconColumn.setSortable(false);
millisColumn.setSortType(TableColumn.SortType.DESCENDING);
millisColumn.setSortable(true);
millisColumn.setComparator(Long::compare);
getSortOrder().setAll(Arrays.asList(millisColumn));
descriptionColumn.setCellValueFactory(cellValueFactory);
descriptionColumn.setCellFactory(col -> new DescriptionCell());
descriptionColumn.setSortable(false);
baseTypeColumn.setCellValueFactory(cellValueFactory);
baseTypeColumn.setCellFactory(col -> new BaseTypeCell());
baseTypeColumn.setSortable(false);
subTypeColumn.setCellValueFactory(cellValueFactory);
subTypeColumn.setCellFactory(col -> new EventTypeCell());
subTypeColumn.setSortable(false);
knownColumn.setCellValueFactory(cellValueFactory);
knownColumn.setCellFactory(col -> new KnownCell());
knownColumn.setSortable(false);
}
@Override
@ -105,15 +122,86 @@ class ListChart extends TableView<Long> implements TimeLineChart<Long> {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
private static class ImageCell extends TableCell<Long, Image> {
private static class ImageCell extends TableCell<Long, Long> {
@Override
protected void updateItem(Image item, boolean empty) {
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(new ImageView(item));
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setGraphic(new ImageView(tableRow.getEvent().getEventType().getFXImage()));
}
}
}
}
private static class DescriptionCell extends TableCell<Long, Long> {
@Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
} else {
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setText(tableRow.getEvent().getDescription(DescriptionLoD.FULL));
}
}
}
}
private static class BaseTypeCell extends TableCell<Long, Long> {
@Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
} else {
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setText(tableRow.getEvent().getEventType().getBaseType().getDisplayName());
}
}
}
}
private static class EventTypeCell extends TableCell<Long, Long> {
@Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
} else {
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setText(tableRow.getEvent().getEventType().getDisplayName());
}
}
}
}
private static class KnownCell extends TableCell<Long, Long> {
@Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
setText("");
} else {
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setText(tableRow.getEvent().getKnown().getName());
}
}
}
}
@ -127,9 +215,32 @@ class ListChart extends TableView<Long> implements TimeLineChart<Long> {
if (empty || item == null) {
setText("");
} else {
setText(TimeLineController.getZonedFormatter().print(item));
EventRow tableRow = (EventRow) getTableRow();
if (tableRow != null) {
setText(TimeLineController.getZonedFormatter().print(tableRow.getEvent().getStartMillis()));
}
}
}
}
private class EventRow extends TableRow<Long> {
private SingleEvent event;
SingleEvent getEvent() {
return event;
}
@Override
protected void updateItem(Long item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null) {
event = null;
} else {
event = controller.getEventsModel().getEventById(item);
}
}
}
}

View File

@ -5,7 +5,7 @@
*/
package org.sleuthkit.autopsy.timeline.ui.listvew;
import java.util.Set;
import java.util.List;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.Parent;
@ -116,7 +116,7 @@ public class ListViewPane extends AbstractVisualizationPane<Long, SingleEvent, N
// updateMessage(Bundle.DetailViewPane_loggedTask_queryDb());
//get the event stripes to be displayed
Set<Long> eventIDs = eventsModel.getEventIDs();
List<Long> eventIDs = eventsModel.getEventIDs();
getChart().getItems().setAll(eventIDs);
// updateMessage(Bundle.DetailViewPane_loggedTask_updateUI());