drive ContentViewer from List view.

This commit is contained in:
jmillman 2016-05-17 16:34:59 -04:00
parent fcbf2c3e25
commit cb9c162ea4
6 changed files with 88 additions and 44 deletions

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-15 Basis Technology Corp. * Copyright 2013-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,10 +18,13 @@
*/ */
package org.sleuthkit.autopsy.timeline; package org.sleuthkit.autopsy.timeline;
import com.google.common.collect.Iterables;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.embed.swing.JFXPanel; import javafx.embed.swing.JFXPanel;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.scene.control.SplitPane; import javafx.scene.control.SplitPane;
@ -46,6 +49,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.timeline.actions.Back; import org.sleuthkit.autopsy.timeline.actions.Back;
import org.sleuthkit.autopsy.timeline.actions.Forward; import org.sleuthkit.autopsy.timeline.actions.Forward;
import org.sleuthkit.autopsy.timeline.explorernodes.EventNode;
import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar; import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar;
import org.sleuthkit.autopsy.timeline.ui.StatusBar; import org.sleuthkit.autopsy.timeline.ui.StatusBar;
import org.sleuthkit.autopsy.timeline.ui.TimeLineResultView; import org.sleuthkit.autopsy.timeline.ui.TimeLineResultView;
@ -56,7 +60,7 @@ import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel;
import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane; import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane;
/** /**
* TopComponent for the timeline feature. * TopComponent for the Timeline feature.
*/ */
@TopComponent.Description( @TopComponent.Description(
preferredID = "TimeLineTopComponent", preferredID = "TimeLineTopComponent",
@ -75,6 +79,21 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
private final TimeLineController controller; private final TimeLineController controller;
/**
* Listener that drives the ContentViewer when in List ViewMode.
*/
private final InvalidationListener selectionListener = new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
if (controller.getSelectedEventIDs().size() == 1) {
EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel());
SwingUtilities.invokeLater(() -> dataContentPanel.setNode(eventNode));
} else {
SwingUtilities.invokeLater(() -> dataContentPanel.setNode(null));
}
}
};
public TimeLineTopComponent(TimeLineController controller) { public TimeLineTopComponent(TimeLineController controller) {
initComponents(); initComponents();
this.controller = controller; this.controller = controller;
@ -108,6 +127,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
lowerSplitXPane.add(contentViewerContainerPanel); lowerSplitXPane.add(contentViewerContainerPanel);
} }
}); });
controller.getSelectedEventIDs().removeListener(selectionListener);
break; break;
case LIST: case LIST:
/* /*
@ -119,6 +139,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
splitYPane.add(contentViewerContainerPanel); splitYPane.add(contentViewerContainerPanel);
dataResultPanel.setNode(null); dataResultPanel.setNode(null);
}); });
controller.getSelectedEventIDs().addListener(selectionListener);
break; break;
default: default:
throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode());

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014 Basis Technology Corp. * Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -31,21 +31,25 @@ import org.openide.nodes.Children;
import org.openide.nodes.PropertySupport; import org.openide.nodes.PropertySupport;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.datamodel.NodeProperty;
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.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* * Explorer Node for {@link SingleEvent}s. * * Explorer Node for SingleEvents.
*/ */
class EventNode extends DisplayableItemNode { public class EventNode extends DisplayableItemNode {
private static final Logger LOGGER = Logger.getLogger(EventNode.class.getName()); private static final Logger LOGGER = Logger.getLogger(EventNode.class.getName());
@ -155,10 +159,48 @@ class EventNode extends DisplayableItemNode {
} }
@Override @Override
public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { final public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
String oldValue = getValue(); String oldValue = getValue();
value = t; value = t;
firePropertyChange("time", oldValue, t); // NON-NLS firePropertyChange("time", oldValue, t); // NON-NLS
} }
} }
/**
* Factory method to create an EventNode form the event ID and the events
* model.
*
* @param eventID The ID of the event this node is for.
* @param eventsModel The model that provides access to the events DB.
*
* @return An EventNode with the file (and artifact) backing this event in
* its lookup.
*/
public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) {
/*
* Look up the event by id and creata an EventNode with the appropriate
* data in the lookup.
*/
final SingleEvent eventById = eventsModel.getEventById(eventID);
try {
SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
AbstractFile file = sleuthkitCase.getAbstractFileById(eventById.getFileID());
if (file != null) {
if (eventById.getArtifactID().isPresent()) {
BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get());
return new EventNode(eventById, file, blackboardArtifact);
} else {
return new EventNode(eventById, file);
}
} else {
//This should never happen in normal operations
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent."); // NON-NLS
return null;
}
} catch (IllegalStateException | TskCoreException ex) {
//if some how the case was closed or ther is another unspecified exception, just bail out with a warning.
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent.", ex); // NON-NLS
return null;
}
}
} }

View File

@ -20,23 +20,16 @@ package org.sleuthkit.autopsy.timeline.explorernodes;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Level;
import org.openide.nodes.AbstractNode; import org.openide.nodes.AbstractNode;
import org.openide.nodes.ChildFactory; import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children; import org.openide.nodes.Children;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* Root Explorer node to represent events. * Root Explorer node to represent events.
@ -132,31 +125,7 @@ public class EventRootNode extends DisplayableItemNode {
*/ */
return new TooManyNode(eventIDs.size()); return new TooManyNode(eventIDs.size());
} else { } else {
/* return EventNode.createEventNode(eventID, filteredEvents);
* look up the event by id and creata an EventNode with the
* appropriate data in the lookup.
*/
final SingleEvent eventById = filteredEvents.getEventById(eventID);
try {
SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase();
AbstractFile file = sleuthkitCase.getAbstractFileById(eventById.getFileID());
if (file != null) {
if (eventById.getArtifactID().isPresent()) {
BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get());
return new EventNode(eventById, file, blackboardArtifact);
} else {
return new EventNode(eventById, file);
}
} else {
//This should never happen in normal operations
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent."); // NON-NLS
return null;
}
} catch (IllegalStateException | TskCoreException ex) {
//if some how the case was closed or ther is another unspecified exception, just bail out with a warning.
LOGGER.log(Level.WARNING, "Failed to lookup sleuthkit object backing TimeLineEvent.", ex); // NON-NLS
return null;
}
} }
} }
} }

View File

@ -62,13 +62,9 @@ public class TimeLineResultView {
dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, dataContent); dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, dataContent);
//set up listeners on relevant properties //set up listeners on relevant properties
TimeLineController.getTimeZone().addListener((Observable observable) -> { TimeLineController.getTimeZone().addListener(timeZone -> dataResultPanel.setPath(getSummaryString()));
dataResultPanel.setPath(getSummaryString());
});
controller.getSelectedEventIDs().addListener((Observable o) -> { controller.getSelectedEventIDs().addListener((Observable selectedIDs) -> refresh());
refresh();
});
refresh(); refresh();
} }

View File

@ -22,6 +22,7 @@ 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;
import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.TableCell; import javafx.scene.control.TableCell;
@ -128,6 +129,15 @@ class ListChart extends BorderPane {
table.getItems().setAll(eventIDs); table.getItems().setAll(eventIDs);
} }
/**
* Get the List of IDs of events that are selected in this list.
*
* @return The List of IDs of events that are selected in this list.
*/
ObservableList<Long> getSelectedEventIDs() {
return table.getSelectionModel().getSelectedItems();
}
private class ImageCell extends EventTableCell { private class ImageCell extends EventTableCell {
@Override @Override

View File

@ -20,6 +20,7 @@ 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.beans.Observable;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.scene.Parent; import javafx.scene.Parent;
import org.joda.time.Interval; import org.joda.time.Interval;
@ -58,6 +59,11 @@ public class ListViewPane extends AbstractTimeLineView {
//initialize chart; //initialize chart;
setCenter(listChart); setCenter(listChart);
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable()); 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());
});
} }
@Override @Override