diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index eb3bc57f46..f9d2f34e0e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -35,8 +35,8 @@ import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import static org.sleuthkit.autopsy.datamodel.Bundle.*; -import org.sleuthkit.autopsy.timeline.actions.ShowArtifactInTimelineAction; -import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.ArtifactEventType; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -131,14 +131,14 @@ public class BlackboardArtifactNode extends DisplayableItemNode { } if (hasTimeStamp) { //if this artifact has a time stamp add the action to view it in the timeline - actionsList.add(new ShowArtifactInTimelineAction(artifact)); + actionsList.add(new ViewArtifactInTimelineAction(artifact)); } AbstractFile file = getLookup().lookup(AbstractFile.class); if (null != file) { //if this artifact has associated content, add the action to view the content in the timeline - actionsList.add(new ShowFileInTimelineAction(file)); + actionsList.add(new ViewFileInTimelineAction(file, true)); } return actionsList.toArray(new Action[actionsList.size()]); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java index 1eee85ec0c..34f0006a4c 100755 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileNode.java @@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.HashSearchAction; import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; import org.sleuthkit.autopsy.directorytree.ViewContextAction; -import org.sleuthkit.autopsy.timeline.actions.ShowFileInTimelineAction; +import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskData.TSK_DB_FILES_TYPE_ENUM; import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM; @@ -86,7 +86,7 @@ public class FileNode extends AbstractFsContentNode { } actionsList.add(new NewWindowViewAction(Bundle.FileNode_getActions_viewInNewWin_text(), this)); actionsList.add(new ExternalViewerAction(Bundle.FileNode_getActions_openInExtViewer_text(), this)); - actionsList.add(new ShowFileInTimelineAction(getContent())); + actionsList.add(new ViewFileInTimelineAction(getContent(), false)); actionsList.add(null); // creates a menu separator actionsList.add(ExtractAction.getInstance()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java index 4f769030fe..2a1bd31eab 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ShowInTimelineDialog.java @@ -49,6 +49,7 @@ import javafx.scene.layout.VBox; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.text.WordUtils; import org.joda.time.Interval; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; @@ -57,14 +58,17 @@ import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; /** - * + * A Dialog that, given a AbstractFile OR BlackBoardArtifact, allows the user to + * choose a specific event and a time range around it to show in the Timeline + * List View. */ -public class ShowInTimelineDialog extends Dialog { - - private static final ButtonType SHOW = new ButtonType("Show Timeline", ButtonBar.ButtonData.OK_DONE); +final class ShowInTimelineDialog extends Dialog { private static final Logger LOGGER = Logger.getLogger(ShowInTimelineDialog.class.getName()); + @NbBundle.Messages({"ShowInTimelineDialog.showTimelineButtonType.text=Show Timeline"}) + private static final ButtonType SHOW = new ButtonType(Bundle.ShowInTimelineDialog_showTimelineButtonType_text(), ButtonBar.ButtonData.OK_DONE); + @FXML private TableView eventTable; @@ -79,12 +83,18 @@ public class ShowInTimelineDialog extends Dialog unitComboBox; + @FXML private Label chooseEventLabel; - private final VBox contentRoot; + private final VBox contentRoot = new VBox(); + private final TimeLineController controller; + /** + * List of ChronoUnits the user can select from when choosing a time range + * to show. + */ private static final List SCROLL_BY_UNITS = Arrays.asList( ChronoUnit.YEARS, ChronoUnit.MONTHS, @@ -93,24 +103,9 @@ public class ShowInTimelineDialog extends Dialog { - - @Override - protected void updateItem(ChronoUnit item, boolean empty) { - super.updateItem(item, empty); - - if (empty || item == null) { - setText(null); - } else { - setText(WordUtils.capitalizeFully(item.toString())); - } - } - } - - public ShowInTimelineDialog(TimeLineController controller, AbstractFile file, BlackboardArtifact artifact) { - super(); + private ShowInTimelineDialog(TimeLineController controller, List eventIDS) { this.controller = controller; - contentRoot = new VBox(); + final String name = "nbres:/" + StringUtils.replace(ShowInTimelineDialog.class.getPackage().getName(), ".", "/") + "/ShowInTimelineDialog.fxml"; // NON-NLS try { @@ -132,22 +127,6 @@ public class ShowInTimelineDialog extends Dialog { - if (buttonType == SHOW) { - SingleEvent selectedEvent = eventTable.getSelectionModel().getSelectedItem(); - - if (file == null) { - selectedEvent = eventTable.getItems().get(0); - } - Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem()); - - Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration); - return new EventInTimeRange(Collections.singleton(selectedEvent.getEventID()), range); - } else { - return null; - } - }); - amountSpinner.setValueFactory(new SpinnerValueFactory.IntegerSpinnerValueFactory(1, 1000)); unitComboBox.setButtonCell(new ChronoUnitListCell()); @@ -161,26 +140,64 @@ public class ShowInTimelineDialog extends Dialog new SimpleObjectProperty<>(param.getValue().getStartMillis())); dateTimeColumn.setCellFactory(param -> new DateTimeTableCell<>()); - List eventIDS; - if (file != null) { - eventIDS = controller.getEventsModel().getEventIDsForFile(file, false); - dialogPane.lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); - } else if (artifact != null) { - - eventIDS = controller.getEventsModel().getEventIDsForArtifact(artifact); - } else { - throw new IllegalArgumentException(); - } - setResizable(true); eventTable.getItems().setAll(eventIDS.stream().map(controller.getEventsModel()::getEventById).collect(Collectors.toSet())); - if (eventIDS.size() == 1) { - chooseEventLabel.setVisible(false); - chooseEventLabel.setManaged(false); - eventTable.getSelectionModel().select(0); - } eventTable.setPrefHeight(Math.min(200, 24 * eventTable.getItems().size() + 28)); } + ShowInTimelineDialog(TimeLineController controller, BlackboardArtifact artifact) { + this(controller, + controller.getEventsModel().getEventIDsForArtifact(artifact)); + chooseEventLabel.setVisible(false); + chooseEventLabel.setManaged(false); + eventTable.getSelectionModel().select(0); + + setResultConverter(buttonType -> { + if (buttonType == SHOW) { + SingleEvent selectedEvent = eventTable.getSelectionModel().getSelectedItem(); + if (selectedEvent == null) { + selectedEvent = eventTable.getItems().get(0); + } + return makeEventInTimeRange(selectedEvent); + } else { + return null; + } + }); + } + + ShowInTimelineDialog(TimeLineController controller, AbstractFile file) { + this(controller, + controller.getEventsModel().getEventIDsForFile(file, false)); + getDialogPane().lookupButton(SHOW).disableProperty().bind(eventTable.getSelectionModel().selectedItemProperty().isNull()); + + setResultConverter(buttonType -> { + if (buttonType == SHOW) { + return makeEventInTimeRange(eventTable.getSelectionModel().getSelectedItem()); + } else { + return null; + } + }); + } + + private EventInTimeRange makeEventInTimeRange(SingleEvent selectedEvent) { + Duration selectedDuration = Duration.of(amountSpinner.getValue(), unitComboBox.getSelectionModel().getSelectedItem()); + Interval range = IntervalUtils.getIntervalAround(Instant.ofEpochMilli(selectedEvent.getStartMillis()), selectedDuration); + return new EventInTimeRange(Collections.singleton(selectedEvent.getEventID()), range); + } + + static private class ChronoUnitListCell extends ListCell { + + @Override + protected void updateItem(ChronoUnit item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(null); + } else { + setText(WordUtils.capitalizeFully(item.toString())); + } + } + } + static private class DateTimeTableCell extends TableCell { @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index c715ed7a20..c29c803239 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -418,7 +418,8 @@ public class TimeLineController { } //get a task that rebuilds the repo with the bellow state listener attached - final CancellationProgressTask rebuildRepositoryTask = repoBuilder.apply(newSate -> { + final CancellationProgressTask rebuildRepositoryTask; + rebuildRepositoryTask = repoBuilder.apply(newSate -> { //this will be on JFX thread switch (newSate) { case SUCCEEDED: @@ -443,7 +444,9 @@ public class TimeLineController { SwingUtilities.invokeLater(this::showWindow); TimeLineController.this.showFullRange(); } else { - ShowInTimelineDialog d = new ShowInTimelineDialog(this, file, artifact); + ShowInTimelineDialog d = (file == null) + ? new ShowInTimelineDialog(this, artifact) + : new ShowInTimelineDialog(this, file); Optional result = d.showAndWait(); result.ifPresent(eventInTimeRange -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java similarity index 82% rename from Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java rename to Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java index cb069431b5..0f7504ab35 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowArtifactInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewArtifactInTimelineAction.java @@ -28,14 +28,14 @@ import org.sleuthkit.datamodel.BlackboardArtifact; /** * An action that shows the given artifact in the Timeline List View. */ -public final class ShowArtifactInTimelineAction extends AbstractAction { +public final class ViewArtifactInTimelineAction extends AbstractAction { private static final long serialVersionUID = 1L; private final BlackboardArtifact artifact; - @NbBundle.Messages({"ShowArtifactInTimelineAction.displayName=Show Result in Timeline... "}) - public ShowArtifactInTimelineAction(BlackboardArtifact artifact) { - super(Bundle.ShowArtifactInTimelineAction_displayName()); + @NbBundle.Messages({"ViewArtifactInTimelineAction.displayName=View Result in Timeline... "}) + public ViewArtifactInTimelineAction(BlackboardArtifact artifact) { + super(Bundle.ViewArtifactInTimelineAction_displayName()); this.artifact = artifact; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java similarity index 72% rename from Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java rename to Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java index 5ab02ce5df..376d260765 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/ShowFileInTimelineAction.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/ViewFileInTimelineAction.java @@ -29,14 +29,17 @@ import org.sleuthkit.datamodel.AbstractFile; * An action to prompt the user to pick an timestamp/event associated with the * given file and show it in the Timeline List View */ -public final class ShowFileInTimelineAction extends AbstractAction { +public final class ViewFileInTimelineAction extends AbstractAction { private static final long serialVersionUID = 1L; private final AbstractFile file; - @NbBundle.Messages({"ShowFileInTimelineAction.displayName=Show File in Timeline... "}) - public ShowFileInTimelineAction(AbstractFile file) { - super(Bundle.ShowFileInTimelineAction_displayName()); + @NbBundle.Messages({"ViewFileInTimelineAction.fileSource.displayName=View File in Timeline... ", + "ViewFileInTimelineAction.artifactSource.displayName=View Source File in Timeline... "}) + public ViewFileInTimelineAction(AbstractFile file, boolean isArtifactSource) { + super(isArtifactSource + ? Bundle.ViewFileInTimelineAction_artifactSource_displayName() + : Bundle.ViewFileInTimelineAction_fileSource_displayName()); this.file = file; }