From 5c1bb1fba43f4e500a31186e66b1c0aee6e80c72 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Tue, 10 May 2016 17:00:19 -0400 Subject: [PATCH 01/92] UTF-8 sanitize text before adding it to solr index --- .../sleuthkit/autopsy/coreutils/TextUtil.java | 22 +++++++++++++++++ .../keywordsearch/HtmlTextExtractor.java | 8 +++++++ .../keywordsearch/SolrSearchService.java | 9 ++++++- .../keywordsearch/TikaTextExtractor.java | 24 ++----------------- 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/TextUtil.java b/Core/src/org/sleuthkit/autopsy/coreutils/TextUtil.java index 053968d598..f2eed8e474 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/TextUtil.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/TextUtil.java @@ -57,4 +57,26 @@ public class TextUtil { return orientation; } + + + /** + * This method determines if a passed-in Java char (16 bits) is a valid + * UTF-8 printable character, returning true if so, false if not. + * + * Note that this method can have ramifications for characters outside the + * Unicode Base Multilingual Plane (BMP), which require more than 16 bits. + * We are using Java characters (16 bits) to look at the data and this will + * not accurately identify any non-BMP character (larger than 16 bits) + * ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we + * have chosen to ignore the extended planes above Unicode BMP for the time + * being. The net result of this is some non-BMP characters may be + * interspersed with '^' characters in Autopsy. + * + * @param ch the character to test + * + * @return Returns true if the character is valid UTF-8, false if not. + */ + public static boolean isValidSolrUTF8(char ch) { + return ((ch <= 0xFDD0 || ch >= 0xFDEF) && (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD) && (ch != 0xFFFF) && (ch != 0xFFFE)); + } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java index 4e03038b39..cdbead3e7f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java @@ -28,6 +28,7 @@ import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; +import org.sleuthkit.autopsy.coreutils.TextUtil; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.ReadContentInputStream; @@ -162,6 +163,13 @@ class HtmlTextExtractor implements TextExtractor { } else { sb.append(textChunkBuf); } + + // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index + for (int i = 0; i < sb.length(); i++) { + if (!TextUtil.isValidSolrUTF8(sb.charAt(i))) { + sb.setCharAt(i, '^'); + } + } //reset for next chunk totalRead = 0; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 26c93f21e3..589ddc4fa2 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -25,7 +25,6 @@ import org.apache.solr.client.solrj.impl.HttpSolrServer; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchService; import org.apache.solr.common.util.ContentStreamBase.StringStream; import org.openide.util.lookup.ServiceProvider; @@ -37,6 +36,7 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.openide.util.NbBundle; import java.net.InetAddress; import java.util.MissingResourceException; +import org.sleuthkit.autopsy.coreutils.TextUtil; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; /** @@ -156,6 +156,13 @@ public class SolrSearchService implements KeywordSearchService { documentId += "_" + Long.toString(chunkId); solrFields.replace(Server.Schema.ID.toString(), documentId); + + // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index + for (int i = 0; i < artifactContents.length(); i++) { + if (!TextUtil.isValidSolrUTF8(artifactContents.charAt(i))) { + artifactContents.setCharAt(i, '^'); + } + } StringStream contentStream = new StringStream(artifactContents.toString()); diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java index 3ccf669b58..7022fb1b8b 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/TikaTextExtractor.java @@ -32,6 +32,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.sleuthkit.autopsy.coreutils.TextUtil; import java.util.concurrent.TimeoutException; import java.util.logging.Level; @@ -188,7 +189,7 @@ class TikaTextExtractor implements TextExtractor { // Sanitize by replacing non-UTF-8 characters with caret '^' for (int i = 0; i < totalRead; ++i) { - if (!isValidSolrUTF8(textChunkBuf[i])) { + if (!TextUtil.isValidSolrUTF8(textChunkBuf[i])) { textChunkBuf[i] = '^'; } } @@ -255,27 +256,6 @@ class TikaTextExtractor implements TextExtractor { return success; } - /** - * This method determines if a passed-in Java char (16 bits) is a valid - * UTF-8 printable character, returning true if so, false if not. - * - * Note that this method can have ramifications for characters outside the - * Unicode Base Multilingual Plane (BMP), which require more than 16 bits. - * We are using Java characters (16 bits) to look at the data and this will - * not accurately identify any non-BMP character (larger than 16 bits) - * ending with 0xFFFF and 0xFFFE. In the interest of a fast solution, we - * have chosen to ignore the extended planes above Unicode BMP for the time - * being. The net result of this is some non-BMP characters may be - * interspersed with '^' characters in Autopsy. - * - * @param ch the character to test - * - * @return Returns true if the character is valid UTF-8, false if not. - */ - private static boolean isValidSolrUTF8(char ch) { - return ((ch <= 0xFDD0 || ch >= 0xFDEF) && (ch > 0x1F || ch == 0x9 || ch == 0xA || ch == 0xD) && (ch != 0xFFFF) && (ch != 0xFFFE)); - } - @Override public boolean isContentTypeSpecific() { return true; From d53637e7950e8b3638b902941379f6a875d1f793 Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 12 May 2016 15:39:49 -0400 Subject: [PATCH 02/92] skeleton of new ListViewPane --- .../autopsy/timeline/VisualizationMode.java | 4 +- .../datamodel/FilteredEventsModel.java | 4 + .../autopsy/timeline/images/table.png | Bin 0 -> 920 bytes .../ui/AbstractVisualizationPane.java | 38 +++-- .../timeline/ui/VisualizationPanel.fxml | 12 ++ .../timeline/ui/VisualizationPanel.java | 9 ++ .../ui/countsview/CountsViewPane.java | 22 ++- .../ui/detailview/DetailViewPane.java | 20 +++ .../timeline/ui/detailview/DetailsChart.java | 28 ++-- .../timeline/ui/listvew/ListChart.java | 106 ++++++++++++++ .../timeline/ui/listvew/ListViewPane.java | 138 ++++++++++++++++++ 11 files changed, 351 insertions(+), 30 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/images/table.png create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java b/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java index 8990b792bc..c624b1da9d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java @@ -23,5 +23,7 @@ package org.sleuthkit.autopsy.timeline; */ public enum VisualizationMode { - COUNTS, DETAIL; + COUNTS, + DETAIL, + LIST; } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index ad5bc8786e..b2b9136136 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -288,6 +288,10 @@ public final class FilteredEventsModel { return repo.getEventIDs(overlap, intersect); } + public Set getEventIDs() { + return getEventIDs(requestedTimeRange.get(), requestedFilter.get()); + } + /** * return the number of events that pass the requested filter and are within * the given time range. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/table.png b/Core/src/org/sleuthkit/autopsy/timeline/images/table.png new file mode 100644 index 0000000000000000000000000000000000000000..0d1e11a83410d46c6912689110b05a8e16eee606 GIT binary patch literal 920 zcmV;J184k+P)`(YZ2MPt(oFg$RqAXOr6{HS)~KtZIc zucWeB_fla+?&fR;-;e`YBve~MLWpuOLP8B~HPgD3A6iJIO0r=xzV4CCC*c^AQ2IUz zu_P7C*lbCvG~ABx-?g(<5|;5k+!&d($E6jPSsu^K+xN;p;L>C*Su_oyR zyn{IwR)!RROt#mjbBi1%YLcyMoQgnSIo%g0;qIH08=mK%%`I#RE^Cz}Y$~t=7n5oh z?A7q&;OOgptCF+pUCqipev`PpnsHbfD_`Gzb1=FZeTe(LEIsj~ update(); + public Pane getSpecificLabelPane() { + return specificLabelPane; + } + + public Pane getContextLabelPane() { + return contextLabelPane; + } + + public Region getSpacer() { + return spacer; + } + /** * The visualization nodes that are selected. * @@ -410,17 +419,17 @@ public abstract class AbstractVisualizationPane { - VBox vBox = new VBox(specificLabelPane, contextLabelPane); - vBox.setFillWidth(false); - HBox hBox = new HBox(spacer, vBox); - hBox.setFillHeight(false); - setBottom(hBox); - DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); - spacer.minWidthProperty().bind(spacerSize); - spacer.prefWidthProperty().bind(spacerSize); - spacer.maxWidthProperty().bind(spacerSize); - }); +// Platform.runLater(() -> { +// VBox vBox = new VBox(specificLabelPane, contextLabelPane); +// vBox.setFillWidth(false); +// HBox hBox = new HBox(spacer, vBox); +// hBox.setFillHeight(false); +// setBottom(hBox); +// DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); +// spacer.minWidthProperty().bind(spacerSize); +// spacer.prefWidthProperty().bind(spacerSize); +// spacer.maxWidthProperty().bind(spacerSize); +// }); createSeries(); @@ -693,7 +702,6 @@ public abstract class AbstractVisualizationPane + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index 1ed89786db..bf5a37a0e7 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -85,6 +85,7 @@ import org.sleuthkit.autopsy.timeline.events.TagsUpdatedEvent; import org.sleuthkit.autopsy.timeline.ui.countsview.CountsViewPane; import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; +import org.sleuthkit.autopsy.timeline.ui.listvew.ListViewPane; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** @@ -161,6 +162,8 @@ final public class VisualizationPanel extends BorderPane { @FXML private ToggleButton detailsToggle; @FXML + private ToggleButton listToggle; + @FXML private Button snapShotButton; @FXML private Button refreshButton; @@ -292,6 +295,8 @@ final public class VisualizationPanel extends BorderPane { controller.setViewMode(VisualizationMode.COUNTS); } else if (newValue == detailsToggle && oldValue != null) { controller.setViewMode(VisualizationMode.DETAIL); + } else if (newValue == listToggle && oldValue != null) { + controller.setViewMode(VisualizationMode.LIST); } }; @@ -551,6 +556,10 @@ final public class VisualizationPanel extends BorderPane { //make new visualization. switch (visMode) { + case LIST: + vizPane = new ListViewPane(controller); + Platform.runLater(() -> listToggle.setSelected(true)); + break; case COUNTS: vizPane = new CountsViewPane(controller); Platform.runLater(() -> countsToggle.setSelected(true)); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 91649dcf37..75374caf23 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -25,6 +25,7 @@ import java.util.function.Function; import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.BooleanBinding; +import javafx.beans.binding.DoubleBinding; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.concurrent.Task; @@ -43,6 +44,7 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; +import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; @@ -116,6 +118,18 @@ public class CountsViewPane extends AbstractVisualizationPane { + VBox vBox = new VBox(getSpecificLabelPane(), getContextLabelPane()); + vBox.setFillWidth(false); + HBox hBox = new HBox(getSpacer(), vBox); + hBox.setFillHeight(false); + setBottom(hBox); + DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); + getSpacer().minWidthProperty().bind(spacerSize); + getSpacer().prefWidthProperty().bind(spacerSize); + getSpacer().maxWidthProperty().bind(spacerSize); + }); + setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes())); getChart().setData(dataSeries); Tooltip.install(getChart(), getDefaultTooltip()); @@ -220,7 +234,7 @@ public class CountsViewPane extends AbstractVisualizationPane { + VBox vBox = new VBox(getSpecificLabelPane(), getContextLabelPane()); + vBox.setFillWidth(false); + HBox hBox = new HBox(getSpacer(), vBox); + hBox.setFillHeight(false); + setBottom(hBox); + DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); + getSpacer().minWidthProperty().bind(spacerSize); + getSpacer().prefWidthProperty().bind(spacerSize); + getSpacer().maxWidthProperty().bind(spacerSize); + }); + this.selectedEvents = new MappedList<>(getSelectedNodes(), EventNodeBase::getEvent); //initialize chart; @@ -416,5 +430,11 @@ public class DetailViewPane extends AbstractVisualizationPane { pinnedView = new ScrollingLaneWrapper(pinnedLane); pinnedLane.setMinHeight(MIN_PINNED_LANE_HEIGHT); - pinnedLane.maxVScrollProperty().addListener((Observable observable) -> syncPinnedHeight()); + pinnedLane.maxVScrollProperty().addListener(maxVSCrollProp -> syncPinnedHeight()); syncPinnedHeight(); //assemble scene graph @@ -427,17 +426,8 @@ final class DetailsChart extends Control implements TimeLineChart { configureMouseListeners(pinnedLane, mouseClickedHandler, chartDragHandler); //show and hide pinned lane in response to settings property change - getSkinnable().getLayoutSettings().pinnedLaneShowing().addListener(observable -> { - boolean selected = getSkinnable().getLayoutSettings().isPinnedLaneShowing(); - if (selected == false) { - dividerPosition = masterDetailPane.getDividerPosition(); - } - masterDetailPane.setShowDetailNode(selected); - if (selected) { - syncPinnedHeight(); - masterDetailPane.setDividerPosition(dividerPosition); - } - }); + getSkinnable().getLayoutSettings().pinnedLaneShowing().addListener(observable -> syncPinnedLaneShowing()); + syncPinnedLaneShowing(); //show and remove interval selector in sync with control state change getSkinnable().intervalSelector().addListener((observable, oldIntervalSelector, newIntervalSelector) -> { @@ -483,5 +473,17 @@ final class DetailsChart extends Control implements TimeLineChart { chartLane.setOnMouseClicked(chartDragHandler); chartLane.addEventHandler(MouseEvent.MOUSE_CLICKED, mouseClickedHandler); } + + private void syncPinnedLaneShowing() { + boolean selected = getSkinnable().getLayoutSettings().isPinnedLaneShowing(); + if (selected == false) { + dividerPosition = masterDetailPane.getDividerPosition(); + } + masterDetailPane.setShowDetailNode(selected); + if (selected) { + syncPinnedHeight(); + masterDetailPane.setDividerPosition(dividerPosition); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java new file mode 100644 index 0000000000..6f95bb2efe --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -0,0 +1,106 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui.listvew; + +import java.util.Arrays; +import javafx.beans.property.SimpleObjectProperty; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.scene.Node; +import javafx.scene.chart.Axis; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.TableCell; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableView; +import javafx.scene.input.MouseEvent; +import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.ui.IntervalSelector; +import org.sleuthkit.autopsy.timeline.ui.TimeLineChart; + +/** + * + */ +class ListChart extends TableView implements TimeLineChart { + + private final TimeLineController controller; + private final TableColumn idColumn = new TableColumn<>(); + private final TableColumn millisColumn = new TableColumn<>(); + + ListChart(TimeLineController controller) { + this.controller = controller; + getColumns().addAll(Arrays.asList(idColumn, millisColumn)); + + idColumn.setCellValueFactory(param -> new SimpleObjectProperty<>(param.getValue())); + millisColumn.setCellValueFactory(param -> { + return new SimpleObjectProperty<>(controller.getEventsModel().getEventById(param.getValue()).getStartMillis()); + }); + millisColumn.setCellFactory(col -> new TableCell() { + @Override + protected void updateItem(Long item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + setText(""); + } else { + setText(TimeLineController.getZonedFormatter().print(item)); + + } + } + }); + + millisColumn.setSortType(TableColumn.SortType.DESCENDING); + millisColumn.setSortable(true); + millisColumn.setComparator(Long::compare); + getSortOrder().setAll(Arrays.asList(millisColumn)); + + } + + @Override + public ObservableList getSelectedNodes() { + return FXCollections.observableArrayList(); + } + + @Override + public IntervalSelector getIntervalSelector() { + return null; + } + + @Override + public void setIntervalSelector(IntervalSelector newIntervalSelector) { +// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public IntervalSelector newIntervalSelector() { + return null; + } + + @Override + public void clearIntervalSelector() { +// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public Axis getXAxis() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public TimeLineController getController() { + return controller; + } + + @Override + public void clearContextMenu() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + public ContextMenu getContextMenu(MouseEvent m) { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java new file mode 100644 index 0000000000..60468f4952 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -0,0 +1,138 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui.listvew; + +import java.util.Set; +import javafx.concurrent.Task; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.chart.Axis; +import org.joda.time.Interval; +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.ui.AbstractVisualizationPane; + +/** + * @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 { + */ +public class ListViewPane extends AbstractVisualizationPane { + + /** + * Constructor + * + * @param controller + */ + public ListViewPane(TimeLineController controller) { + super(controller); + + //initialize chart; + setChart(new ListChart(controller)); + setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable()); + } + + @Override + protected Boolean isTickBold(Long value) { + return true; + } + + @Override + protected void applySelectionEffect(Node node, Boolean applied) { +// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected Task getNewUpdateTask() { + return new ListUpdateTask(); + } + + @Override + protected String getTickMarkLabel(Long tickValue) { + return ""; + } + + @Override + protected double getTickSpacing() { + return 0; + } + + @Override + protected Axis getXAxis() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected Axis getYAxis() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + + @Override + protected double getAxisMargin() { + return 0; + } + + @Override + protected void clearChartData() { + getChart().getItems().clear(); + } + + private static class ListViewSettingsPane extends Parent { + + ListViewSettingsPane() { + } + } + + private class ListUpdateTask extends VisualizationUpdateTask { + + ListUpdateTask() { + super("List update task", true); + } + + @Override + protected Boolean call() throws Exception { + super.call(); //To change body of generated methods, choose Tools | Templates. + if (isCancelled()) { + return null; + } + FilteredEventsModel eventsModel = getEventsModel(); + + //clear the chart and set the horixontal axis + resetChart(eventsModel.getTimeRange()); + +// updateMessage(Bundle.DetailViewPane_loggedTask_queryDb()); + //get the event stripes to be displayed + Set eventIDs = eventsModel.getEventIDs(); + getChart().getItems().setAll(eventIDs); + +// updateMessage(Bundle.DetailViewPane_loggedTask_updateUI()); + return eventIDs.isEmpty() == false; + } + + @Override + protected void cancelled() { + super.cancelled(); + getController().retreat(); + } + + @Override + protected void setDateAxisValues(Interval timeRange) { +// detailsChartDateAxis.setRange(timeRange, true); +// pinnedDateAxis.setRange(timeRange, true); + } + } +} From 5678c0ca610337307ff6970530b2ddd6030daba2 Mon Sep 17 00:00:00 2001 From: Oliver Spohngellert Date: Fri, 13 May 2016 15:32:42 -0400 Subject: [PATCH 03/92] Fixed index artifact error messages. --- .../ExternalResultsImporter.java | 6 ++++-- .../android/BrowserLocationAnalyzer.java | 6 +++--- .../modules/android/CacheLocationAnalyzer.java | 8 ++++---- .../modules/android/CallLogAnalyzer.java | 11 ++++++----- .../modules/android/ContactAnalyzer.java | 7 ++++--- .../android/GoogleMapLocationAnalyzer.java | 7 ++++--- .../modules/android/TangoMessageAnalyzer.java | 8 +++++--- .../modules/android/TextMessageAnalyzer.java | 9 +++++---- .../modules/android/WWFMessageAnalyzer.java | 9 +++++---- .../SevenZipExtractor.java | 7 ++++--- .../exif/ExifParserFileIngestModule.java | 8 +++++--- .../FileExtMismatchIngestModule.java | 6 ++++-- .../hashdatabase/HashDbIngestModule.java | 14 ++++++++------ .../autopsy/modules/iOS/CallLogAnalyzer.java | 7 ++++--- .../autopsy/modules/iOS/ContactAnalyzer.java | 11 ++++++----- .../modules/iOS/TextMessageAnalyzer.java | 8 +++++--- .../FilesIdentifierIngestModule.java | 7 +++---- .../autopsy/modules/stix/StixArtifactData.java | 10 +++++----- .../autopsy/recentactivity/Extract.java | 12 ++++++------ .../ThunderbirdMboxFileIngestModule.java | 17 +++++++++-------- 20 files changed, 99 insertions(+), 79 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java index 9deeb0e841..0b19827c48 100644 --- a/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java +++ b/Core/src/org/sleuthkit/autopsy/externalresults/ExternalResultsImporter.java @@ -27,6 +27,7 @@ import java.util.HashSet; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -127,6 +128,7 @@ public final class ExternalResultsImporter { } } + @Messages({"ExternalResultsImporter.indexError.message=Failed to index imported artifact for keyword search."}) private void importArtifacts(ExternalResults results) { SleuthkitCase caseDb = Case.getCurrentCase().getSleuthkitCase(); for (ExternalResults.Artifact artifactData : results.getArtifacts()) { @@ -200,9 +202,9 @@ public final class ExternalResultsImporter { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", artifact.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), artifact.getDisplayName()); + Bundle.ExternalResultsImporter_indexError_message(), artifact.getDisplayName()); } if (standardArtifactTypeIds.contains(artifactTypeId)) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java index d7f0543c49..5668721246 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/BrowserLocationAnalyzer.java @@ -26,7 +26,6 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; @@ -72,6 +71,7 @@ class BrowserLocationAnalyzer { } } + @NbBundle.Messages({"BrowserLocationAnalyzer.indexError.message=Failed to index gps trackpoint artifact for keyword search."}) private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -110,9 +110,9 @@ class BrowserLocationAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.BrowserLocationAnalyzer_indexError_message(), bba.getDisplayName()); } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java index 266bf09469..e1896a88d5 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/CacheLocationAnalyzer.java @@ -25,8 +25,8 @@ import java.math.BigInteger; import java.nio.ByteBuffer; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -78,6 +78,7 @@ class CacheLocationAnalyzer { } } + @Messages({"CacheLocationAnalyzer.indexError.message=Failed to index gps trackpoint artifact for keyword search."}) private static void findGeoLocationsInFile(File file, AbstractFile f) { byte[] bytes; // will temporarily hold bytes to be converted into the correct data types @@ -140,14 +141,13 @@ class CacheLocationAnalyzer { //Not storing these for now. // bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_VALUE.getTypeID(),moduleName, accuracy)); // bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_COMMENT.getTypeID(),moduleName, confidence)); - try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.CacheLocationAnalyzer_indexError_message(), bba.getDisplayName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java index 7df25e15ff..62594572eb 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/CallLogAnalyzer.java @@ -28,7 +28,7 @@ import java.sql.Statement; import java.util.Arrays; import java.util.List; import java.util.logging.Level; -import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -58,7 +58,7 @@ class CallLogAnalyzer { private static final Iterable tableNames = Arrays.asList("calls", "logs"); //NON-NLS public static void findCallLogs(Content dataSource, FileManager fileManager) { - blackboard = Case.getCurrentCase().getServices().getBlackboard(); + blackboard = Case.getCurrentCase().getServices().getBlackboard(); try { List absFiles = fileManager.findFiles(dataSource, "logs.db"); //NON-NLS absFiles.addAll(fileManager.findFiles(dataSource, "contacts.db")); //NON-NLS @@ -77,6 +77,7 @@ class CallLogAnalyzer { } } + @Messages({"CallLogAnalyzer.indexError.message=Failed to index call log artifact for keyword search."}) private static void findCallLogsInDB(String DatabasePath, AbstractFile f) { if (DatabasePath == null || DatabasePath.isEmpty()) { @@ -113,16 +114,16 @@ class CallLogAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.CallLogAnalyzer_indexError_message(), bba.getDisplayName()); } } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error posting call log record to the Blackboard", ex); //NON-NLS } } } catch (SQLException e) { - logger.log(Level.WARNING, "Could not read table {0} in db {1}", new Object[]{tableName, DatabasePath}); //NON-NLS + logger.log(Level.WARNING, String.format("Could not read table %s in db %s", tableName, DatabasePath), e); //NON-NLS } } } catch (SQLException e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java index 25affa740f..cff91bf689 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/ContactAnalyzer.java @@ -27,7 +27,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; -import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -79,6 +79,7 @@ class ContactAnalyzer { * path The fileId will be the Abstract file associated * with the artifacts */ + @Messages({"ContactAnalyzer.indexError.message=Failed to index contact artifact for keyword search."}) private static void findContactsInDB(String databasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -154,9 +155,9 @@ class ContactAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.ContactAnalyzer_indexError_message(), bba.getDisplayName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java index d7889257dc..a073800354 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/GoogleMapLocationAnalyzer.java @@ -26,8 +26,8 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -71,6 +71,7 @@ class GoogleMapLocationAnalyzer { } } + @Messages({"GoogleMapLocationAnalyzer.indexError.message=Failed to index gps route artifact for keyword search."}) private static void findGeoLocationsInDB(String DatabasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -136,9 +137,9 @@ class GoogleMapLocationAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.GoogleMapLocationAnalyzer_indexError_message(), bba.getDisplayName()); } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java index 09dba30821..c8decb163e 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/TangoMessageAnalyzer.java @@ -28,6 +28,7 @@ import java.util.List; import java.util.logging.Level; import org.apache.commons.codec.binary.Base64; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -68,6 +69,7 @@ class TangoMessageAnalyzer { } } + @Messages({"TangoMessageAnalyzer.indexError.message=Failed to index message artifact for keyword search."}) private static void findTangoMessagesInDB(String DatabasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -110,14 +112,14 @@ class TangoMessageAnalyzer { bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(TangoMessageAnalyzer.class, "TangoMessageAnalyzer.bbAttribute.tangoMessage"))); - + try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.TangoMessageAnalyzer_indexError_message(), bba.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java index edf12e0b66..bb6f4d0896 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/TextMessageAnalyzer.java @@ -26,8 +26,8 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -68,6 +68,7 @@ class TextMessageAnalyzer { } } + @Messages({"TextMessageAnalyzer.indexError.message=Failed to index message artifact for keyword search."}) private static void findTextsInDB(String DatabasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -123,14 +124,14 @@ class TextMessageAnalyzer { bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(TextMessageAnalyzer.class, "TextMessageAnalyzer.bbAttribute.smsMessage"))); - + try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.TextMessageAnalyzer_indexError_message(), bba.getDisplayName()); } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java index 87bbd8dc4d..33a3b2b186 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/android/WWFMessageAnalyzer.java @@ -26,8 +26,8 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; - import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -71,6 +71,7 @@ class WWFMessageAnalyzer { } } + @Messages({"WWFMessageAnalyzer.indexError.message=Failed to index message artifact for keyword search."}) private static void findWWFMessagesInDB(String DatabasePath, AbstractFile f) { Connection connection = null; ResultSet resultSet = null; @@ -110,14 +111,14 @@ class WWFMessageAnalyzer { bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(WWFMessageAnalyzer.class, "WWFMessageAnalyzer.bbAttribute.wordsWithFriendsMsg"))); - + try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.WWFMessageAnalyzer_indexError_message(), bba.getDisplayName()); } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java index 8e25feaa23..47a38d6a7d 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java +++ b/Core/src/org/sleuthkit/autopsy/modules/embeddedfileextractor/SevenZipExtractor.java @@ -42,6 +42,7 @@ import net.sf.sevenzipjbinding.simple.ISimpleInArchive; import net.sf.sevenzipjbinding.simple.ISimpleInArchiveItem; import org.netbeans.api.progress.ProgressHandle; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -50,7 +51,6 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestMessage; -import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.autopsy.ingest.IngestMonitor; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleContentEvent; @@ -267,6 +267,7 @@ class SevenZipExtractor { * * @return list of unpacked derived files */ + @Messages({"SevenZipExtractor.indexError.message=Failed to index encription detected artifact for keyword search."}) void unpack(AbstractFile archiveFile) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); String archiveFilePath; @@ -586,9 +587,9 @@ class SevenZipExtractor { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", artifact.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), artifact.getDisplayName()); + Bundle.SevenZipExtractor_indexError_message(), artifact.getDisplayName()); } services.fireModuleDataEvent(new ModuleDataEvent(EmbeddedFileExtractorModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED)); diff --git a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java index 2534d376f7..3d30bc0972 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/exif/ExifParserFileIngestModule.java @@ -38,15 +38,16 @@ import java.util.TimeZone; import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.IngestJobContext; +import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; -import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -133,6 +134,7 @@ public final class ExifParserFileIngestModule implements FileIngestModule { return processFile(content); } + @Messages({"ExifParserFileIngestModule.indexError.message=Failed to index Exif Metadata artifact for keyword search."}) ProcessResult processFile(AbstractFile f) { InputStream in = null; BufferedInputStream bin = null; @@ -206,9 +208,9 @@ public final class ExifParserFileIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.ExifParserFileIngestModule_indexError_message(), bba.getDisplayName()); } filesToFire = true; } diff --git a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java index 6f9a9ea98c..827b75ae49 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/fileextmismatch/FileExtMismatchIngestModule.java @@ -23,6 +23,7 @@ import java.util.HashMap; import java.util.Set; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; @@ -105,6 +106,7 @@ public class FileExtMismatchIngestModule implements FileIngestModule { } @Override + @Messages({"FileExtMismatchIngestModule.indexError.message=Failed to index extension mismatch artifact for keyword search."}) public ProcessResult process(AbstractFile abstractFile) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); if (this.settings.skipKnownFiles() && (abstractFile.getKnown() == FileKnown.KNOWN)) { @@ -139,9 +141,9 @@ public class FileExtMismatchIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(bart); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bart.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bart.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bart.getDisplayName()); + Bundle.FileExtMismatchIngestModule_indexError_message(), bart.getDisplayName()); } services.fireModuleDataEvent(new ModuleDataEvent(FileExtMismatchDetectorModuleFactory.getModuleName(), ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED, Collections.singletonList(bart))); diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java index 0199727cb2..97317734b3 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbIngestModule.java @@ -26,27 +26,28 @@ import java.util.List; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.ingest.FileIngestModule; import org.sleuthkit.autopsy.ingest.IngestMessage; +import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; import org.sleuthkit.autopsy.ingest.IngestServices; import org.sleuthkit.autopsy.ingest.ModuleDataEvent; +import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE; +import org.sleuthkit.datamodel.HashHitInfo; import org.sleuthkit.datamodel.HashUtility; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskException; -import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; -import org.sleuthkit.autopsy.ingest.FileIngestModule; -import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter; -import org.sleuthkit.datamodel.HashHitInfo; @NbBundle.Messages({ "HashDbIngestModule.noKnownBadHashDbSetMsg=No known bad hash database set.", @@ -285,6 +286,7 @@ public class HashDbIngestModule implements FileIngestModule { return ret; } + @Messages({"HashDbIngestModule.indexError.message=Failed to index hashset hit artifact for keyword search."}) private void postHashSetHitToBlackboard(AbstractFile abstractFile, String md5Hash, String hashSetName, String comment, boolean showInboxMessage) { try { String MODULE_NAME = NbBundle.getMessage(HashDbIngestModule.class, "HashDbIngestModule.moduleName"); @@ -303,9 +305,9 @@ public class HashDbIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(badFile); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", badFile.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + badFile.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), badFile.getDisplayName()); + Bundle.HashDbIngestModule_indexError_message(), badFile.getDisplayName()); } if (showInboxMessage) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java index 22ca91c064..00858550f3 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/CallLogAnalyzer.java @@ -25,7 +25,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; -import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; @@ -74,6 +74,7 @@ class CallLogAnalyzer { } } + @Messages({"CallLogAnalyzer.indexError.message=Failed to index call log artifact for keyword search."}) private void findCallLogsInDB(String DatabasePath, long fId) { if (DatabasePath == null || DatabasePath.isEmpty()) { return; @@ -128,9 +129,9 @@ class CallLogAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.CallLogAnalyzer_indexError_message(), bba.getDisplayName()); } } } catch (Exception e) { diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java index 7c63e28a8d..fbfdaf18b4 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/ContactAnalyzer.java @@ -30,18 +30,18 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.List; import java.util.logging.Level; -import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.ReadContentInputStream; import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; -import org.sleuthkit.autopsy.datamodel.ContentUtils; -import org.sleuthkit.datamodel.ReadContentInputStream; class ContactAnalyzer { @@ -91,6 +91,7 @@ class ContactAnalyzer { * path The fileId will be the Abstract file associated * with the artifacts */ + @Messages({"ContactAnalyzer.indexError.message=Failed to index contact artifact for keyword search."}) private void findContactsInDB(String DatabasePath, long fId) { if (DatabasePath == null || DatabasePath.isEmpty()) { return; @@ -149,9 +150,9 @@ class ContactAnalyzer { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.ContactAnalyzer_indexError_message(), bba.getDisplayName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java index 44c08ae0cf..1cbe6a2e2b 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java +++ b/Core/src/org/sleuthkit/autopsy/modules/iOS/TextMessageAnalyzer.java @@ -26,6 +26,7 @@ import java.sql.Statement; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; @@ -74,6 +75,7 @@ class TextMessageAnalyzer { } } + @Messages({"TextMessageAnalyzer.indexError.message=Failed to index message artifact for keyword search."}) private void findTextsInDB(String DatabasePath, long fId) { if (DatabasePath == null || DatabasePath.isEmpty()) { return; @@ -127,14 +129,14 @@ class TextMessageAnalyzer { bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT, moduleName, subject)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TEXT, moduleName, body)); bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_MESSAGE_TYPE, moduleName, NbBundle.getMessage(this.getClass(), "TextMessageAnalyzer.bbAttribute.smsMessage"))); - + try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + Bundle.TextMessageAnalyzer_indexError_message(), bba.getDisplayName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java index 62b423015b..dd389b90fb 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesIdentifierIngestModule.java @@ -24,7 +24,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Level; -import org.openide.util.Exceptions; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; @@ -100,6 +99,7 @@ final class FilesIdentifierIngestModule implements FileIngestModule { * @inheritDoc */ @Override + @Messages({"FilesIdentifierIngestModule.indexError.message=Failed to index interesting file hit artifact for keyword search."}) public ProcessResult process(AbstractFile file) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); @@ -131,9 +131,8 @@ final class FilesIdentifierIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", artifact.getDisplayName()), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), artifact.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getDisplayName(), ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.FilesIdentifierIngestModule_indexError_message(), artifact.getDisplayName()); } IngestServices.getInstance().fireModuleDataEvent(new ModuleDataEvent(moduleName, BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT, Collections.singletonList(artifact))); diff --git a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java index 9a19e4cedf..d2440e8861 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java +++ b/Core/src/org/sleuthkit/autopsy/modules/stix/StixArtifactData.java @@ -19,7 +19,7 @@ package org.sleuthkit.autopsy.modules.stix; import java.util.logging.Level; -import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.coreutils.Logger; @@ -58,6 +58,7 @@ class StixArtifactData { objType = a_objType; } + @Messages({"StixArtifactData.indexError.message=Failed to index interesting file hit artifact for keyword search."}) public void createArtifact(String a_title) throws TskCoreException { Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); @@ -72,14 +73,13 @@ class StixArtifactData { bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SET_NAME, "Stix", setName)); //NON-NLS bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_TITLE, "Stix", observableId)); //NON-NLS bba.addAttribute(new BlackboardAttribute(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_CATEGORY, "Stix", objType)); //NON-NLS - + try { // index the artifact for keyword search blackboard.indexArtifact(bba); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bba.getDisplayName()), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bba.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bba.getDisplayName(), ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.StixArtifactData_indexError_message(), bba.getDisplayName()); } } diff --git a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java index 1bf8eecb5c..0c2c2e1ffb 100644 --- a/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java +++ b/RecentActivity/src/org/sleuthkit/autopsy/recentactivity/Extract.java @@ -22,18 +22,18 @@ */ package org.sleuthkit.autopsy.recentactivity; -import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.*; import java.util.logging.Level; - import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.coreutils.Logger; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; +import org.sleuthkit.autopsy.coreutils.SQLiteDBConnect; import org.sleuthkit.autopsy.ingest.IngestJobContext; import org.sleuthkit.autopsy.ingest.IngestModule.IngestModuleException; import org.sleuthkit.datamodel.*; @@ -103,15 +103,15 @@ abstract class Extract { * * @param bbart Blackboard artifact to be indexed */ + @Messages({"Extract.indexError.message=Failed to index artifact for keyword search."}) void indexArtifact(BlackboardArtifact bbart) { Blackboard blackboard = Case.getCurrentCase().getServices().getBlackboard(); try { // index the artifact for keyword search blackboard.indexArtifact(bbart); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bbart.getDisplayName()), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bbart.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getDisplayName(), ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.Extract_indexError_message(), bbart.getDisplayName()); } } diff --git a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java index 3df364b32e..663aa01999 100644 --- a/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java +++ b/thunderbirdparser/src/org/sleuthkit/autopsy/thunderbirdparser/ThunderbirdMboxFileIngestModule.java @@ -24,6 +24,7 @@ import java.util.ArrayList; import java.util.List; import java.util.logging.Level; import org.openide.util.NbBundle; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.services.Blackboard; import org.sleuthkit.autopsy.casemodule.services.FileManager; @@ -73,7 +74,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { public ProcessResult process(AbstractFile abstractFile) { blackboard = Case.getCurrentCase().getServices().getBlackboard(); - + // skip known if (abstractFile.getKnown().equals(TskData.FileKnown.KNOWN)) { return ProcessResult.OK; @@ -120,6 +121,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * * @return */ + @Messages({"ThunderbirdMboxFileIngestModule.processPst.indexError.message=Failed to index encryption detected artifact for keyword search."}) private ProcessResult processPst(AbstractFile abstractFile) { String fileName = getTempPath() + File.separator + abstractFile.getName() + "-" + String.valueOf(abstractFile.getId()); @@ -159,11 +161,10 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(artifact); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", artifact.getDisplayName()), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), artifact.getDisplayName()); + MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_processPst_indexError_message(), artifact.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + artifact.getDisplayName(), ex); //NON-NLS } - + services.fireModuleDataEvent(new ModuleDataEvent(EmailParserModuleFactory.getModuleName(), BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED)); } catch (TskCoreException ex) { logger.log(Level.INFO, "Failed to add encryption attribute to file: {0}", abstractFile.getName()); //NON-NLS @@ -354,6 +355,7 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { * @param email * @param abstractFile */ + @Messages({"ThunderbirdMboxFileIngestModule.addArtifact.indexError.message=Failed to index email message detected artifact for keyword search."}) private void addArtifact(EmailMessage email, AbstractFile abstractFile) { List bbattributes = new ArrayList<>(); String to = email.getRecipients(); @@ -413,9 +415,8 @@ public final class ThunderbirdMboxFileIngestModule implements FileIngestModule { // index the artifact for keyword search blackboard.indexArtifact(bbart); } catch (Blackboard.BlackboardException ex) { - logger.log(Level.SEVERE, NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.error.msg", bbart.getDisplayName()), ex); //NON-NLS - MessageNotifyUtil.Notify.error( - NbBundle.getMessage(Blackboard.class, "Blackboard.unableToIndexArtifact.exception.msg"), bbart.getDisplayName()); + logger.log(Level.SEVERE, "Unable to index blackboard artifact " + bbart.getDisplayName(), ex); //NON-NLS + MessageNotifyUtil.Notify.error(Bundle.ThunderbirdMboxFileIngestModule_addArtifact_indexError_message(), bbart.getDisplayName()); } } catch (TskCoreException ex) { logger.log(Level.WARNING, null, ex); From 2858421d46a35031158b7de7b60b56f05d314db2 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 13 May 2016 16:57:33 -0400 Subject: [PATCH 04/92] flesh out ListViewPane, keep events sorted by date --- .../datamodel/FilteredEventsModel.java | 4 +- .../autopsy/timeline/db/EventDB.java | 10 +- .../autopsy/timeline/db/EventsRepository.java | 2 +- .../timeline/ui/VisualizationPanel.java | 37 ++-- .../timeline/ui/listvew/ListChart.java | 163 +++++++++++++++--- .../timeline/ui/listvew/ListViewPane.java | 4 +- 6 files changed, 163 insertions(+), 57 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java index 8917c0cb84..8a744a5e20 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/datamodel/FilteredEventsModel.java @@ -279,7 +279,7 @@ public final class FilteredEventsModel { return repo.getTagCountsByTagName(eventIDsWithTags); } - public Set getEventIDs(Interval timeRange, Filter filter) { + public List 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 getEventIDs() { + public List getEventIDs() { return getEventIDs(requestedTimeRange.get(), requestedFilter.get()); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 996c35ef3b..2374e7a032 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -343,18 +343,19 @@ public class EventDB { return result; } - Set getEventIDs(Interval timeRange, RootFilter filter) { + List getEventIDs(Interval timeRange, RootFilter filter) { return getEventIDs(timeRange.getStartMillis() / 1000, timeRange.getEndMillis() / 1000, filter); } - Set getEventIDs(Long startTime, Long endTime, RootFilter filter) { + List getEventIDs(Long startTime, Long endTime, RootFilter filter) { if (Objects.equals(startTime, endTime)) { endTime++; } - Set resultIDs = new HashSet<>(); + ArrayList 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); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java index a93bf9dc18..9686abb142 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventsRepository.java @@ -214,7 +214,7 @@ public class EventsRepository { idToEventCache.invalidateAll(); } - public Set getEventIDs(Interval timeRange, RootFilter filter) { + public List getEventIDs(Interval timeRange, RootFilter filter) { return eventDB.getEventIDs(timeRange, filter); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index 7cb033f9a8..f31b5e4f0d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -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 toggleListener = (ObservableValue 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 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(); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index b01e9a605a..242c8f6578 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -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 implements TimeLineChart { + Callback, ObservableValue> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue()); + private final TimeLineController controller; - private final TableColumn idColumn = new TableColumn<>(); - private final TableColumn millisColumn = new TableColumn<>(); - private final TableColumn iconColumn = new TableColumn<>(); - private final TableColumn descriptionColumn = new TableColumn<>(); - private final TableColumn baseTypeColumn = new TableColumn<>(); - private final TableColumn subTypeColumn = new TableColumn<>(); - private final TableColumn knownColumn = new TableColumn<>(); + private final TableColumn idColumn = new TableColumn<>("Event ID"); + private final TableColumn millisColumn = new TableColumn<>("Date/Time"); + private final TableColumn iconColumn = new TableColumn<>("Icon"); + private final TableColumn descriptionColumn = new TableColumn<>("Description"); + private final TableColumn baseTypeColumn = new TableColumn<>("Base Type"); + private final TableColumn subTypeColumn = new TableColumn<>("Sub Type"); + private final TableColumn 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 implements TimeLineChart { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. } - private static class ImageCell extends TableCell { + private static class ImageCell extends TableCell { @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 { + + @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 { + + @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 { + + @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 { + + @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 implements TimeLineChart { 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 { + + 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); + } + } + } } 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 68b08506df..1bc0c5b088 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -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 eventIDs = eventsModel.getEventIDs(); + List eventIDs = eventsModel.getEventIDs(); getChart().getItems().setAll(eventIDs); // updateMessage(Bundle.DetailViewPane_loggedTask_updateUI()); From 0ffa31d309a8523d7538098f3bfeb530168974c7 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 16 May 2016 17:49:17 -0400 Subject: [PATCH 05/92] fxmlize the ListView --- .../autopsy/timeline/db/EventDB.java | 2 +- .../ui/AbstractVisualizationPane.java | 2 +- .../autopsy/timeline/ui/TimeLineView.java | 14 +++ .../timeline/ui/listvew/ListChart.java | 115 +++++++++++------- .../timeline/ui/listvew/ListViewChart.fxml | 39 ++++++ .../timeline/ui/listvew/ListViewPane.java | 12 +- 6 files changed, 134 insertions(+), 50 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 2374e7a032..559b039b41 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -584,7 +584,7 @@ public class EventDB { insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, event_id) values (?,?)"); //NON-NLS insertTagStmt = prepareStatement("INSERT OR IGNORE INTO tags (tag_id, tag_name_id,tag_name_display_name, event_id) values (?,?,?,?)"); //NON-NLS deleteTagStmt = prepareStatement("DELETE FROM tags WHERE tag_id = ?"); //NON-NLS - countAllEventsStmt = prepareStatement("SELECT count(*) AS count FROM events"); //NON-NLS + countAllEventsStmt = prepareStatement("SELECT count(event_id) AS count FROM events WHERE event_id IS NOT null"); //NON-NLS dropEventsTableStmt = prepareStatement("DROP TABLE IF EXISTS events"); //NON-NLS dropHashSetHitsTableStmt = prepareStatement("DROP TABLE IF EXISTS hash_set_hits"); //NON-NLS dropHashSetsTableStmt = prepareStatement("DROP TABLE IF EXISTS hash_sets"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java index fb645bdcd3..ac58f65ca5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java @@ -85,7 +85,7 @@ import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; * * TODO: pull up common history context menu items out of derived classes? -jm */ -public abstract class AbstractVisualizationPane> extends BorderPane { +public abstract class AbstractVisualizationPane> extends BorderPane implements TimeLineView { private static final Logger LOGGER = Logger.getLogger(AbstractVisualizationPane.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java new file mode 100644 index 0000000000..cfbd3c9f5a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java @@ -0,0 +1,14 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui; + +/** + * + * @author jmillman + */ +public interface TimeLineView { + +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index 242c8f6578..9235b763da 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -5,21 +5,27 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; -import java.util.Arrays; +import java.util.List; +import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.fxml.FXML; import javafx.scene.Node; import javafx.scene.chart.Axis; import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; +import javafx.scene.layout.BorderPane; import javafx.util.Callback; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; +import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.ui.IntervalSelector; @@ -29,7 +35,13 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; /** * */ -class ListChart extends TableView implements TimeLineChart { +class ListChart extends BorderPane implements TimeLineChart { + + @FXML + private Label eventCountLabel; + + @FXML + private TableView table; Callback, ObservableValue> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue()); @@ -44,37 +56,42 @@ class ListChart extends TableView implements TimeLineChart { ListChart(TimeLineController controller) { this.controller = controller; - setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY); - getColumns().addAll(Arrays.asList(idColumn, millisColumn, iconColumn, descriptionColumn, baseTypeColumn, subTypeColumn, knownColumn)); + FXMLConstructor.construct(this, ListChart.class, "ListViewChart.fxml"); + } - setRowFactory(tableView -> new EventRow()); + @FXML + 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'."; + assert idColumn != null : "fx:id=\"idColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert millisColumn != null : "fx:id=\"millisColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert iconColumn != null : "fx:id=\"iconColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert descriptionColumn != null : "fx:id=\"descriptionColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert baseTypeColumn != null : "fx:id=\"baseTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; +// setRowFactory(tableView -> new EventRow()); idColumn.setCellValueFactory(cellValueFactory); - idColumn.setSortable(false); millisColumn.setCellValueFactory(cellValueFactory); millisColumn.setCellFactory(col -> new EpochMillisCell()); - millisColumn.setSortable(false); iconColumn.setCellValueFactory(cellValueFactory); iconColumn.setCellFactory(col -> new ImageCell()); - iconColumn.setSortable(false); 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); + eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } @Override @@ -120,9 +137,20 @@ class ListChart extends TableView implements TimeLineChart { @Override public ContextMenu getContextMenu(MouseEvent m) { throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } - private static class ImageCell extends TableCell { + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + void clear() { + table.getItems().clear(); + } + + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + void setEventIDs(List eventIDs) { + table.getItems().setAll(eventIDs); + } + + private class ImageCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -130,15 +158,12 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setGraphic(null); } else { - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setGraphic(new ImageView(tableRow.getEvent().getEventType().getFXImage())); - } + setGraphic(new ImageView(getEvent().getEventType().getFXImage())); } } } - private static class DescriptionCell extends TableCell { + private class DescriptionCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -147,15 +172,12 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setText(""); } else { - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setText(tableRow.getEvent().getDescription(DescriptionLoD.FULL)); - } + setText(getEvent().getDescription(DescriptionLoD.FULL)); } } } - private static class BaseTypeCell extends TableCell { + private class BaseTypeCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -164,15 +186,12 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setText(""); } else { - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setText(tableRow.getEvent().getEventType().getBaseType().getDisplayName()); - } + setText(getEvent().getEventType().getBaseType().getDisplayName()); } } } - private static class EventTypeCell extends TableCell { + private class EventTypeCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -181,15 +200,12 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setText(""); } else { - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setText(tableRow.getEvent().getEventType().getDisplayName()); - } + setText(getEvent().getEventType().getDisplayName()); } } } - private static class KnownCell extends TableCell { + private class KnownCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -198,15 +214,32 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setText(""); } else { - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setText(tableRow.getEvent().getKnown().getName()); - } + setText(getEvent().getKnown().getName()); } } } - private class EpochMillisCell extends TableCell { + private class EventTableCell extends TableCell { + + 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); + } + } + } + + private class EpochMillisCell extends EventTableCell { @Override protected void updateItem(Long item, boolean empty) { @@ -215,11 +248,7 @@ class ListChart extends TableView implements TimeLineChart { if (empty || item == null) { setText(""); } else { - - EventRow tableRow = (EventRow) getTableRow(); - if (tableRow != null) { - setText(TimeLineController.getZonedFormatter().print(tableRow.getEvent().getStartMillis())); - } + setText(TimeLineController.getZonedFormatter().print(getEvent().getStartMillis())); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml new file mode 100644 index 0000000000..f3b45572c2 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + +
+
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 1bc0c5b088..aa6468298d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -6,6 +6,7 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.List; +import javafx.application.Platform; import javafx.concurrent.Task; import javafx.scene.Node; import javafx.scene.Parent; @@ -15,6 +16,7 @@ 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.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.TimeLineView; /** * @param The type of data plotted along the x axis @@ -31,7 +33,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; * public abstract class AbstractVisualizationPane> extends BorderPane { */ -public class ListViewPane extends AbstractVisualizationPane { +public class ListViewPane extends AbstractVisualizationPane implements TimeLineView { /** * Constructor @@ -88,7 +90,7 @@ public class ListViewPane extends AbstractVisualizationPane eventIDs = eventsModel.getEventIDs(); - getChart().getItems().setAll(eventIDs); + Platform.runLater(() -> getChart().setEventIDs(eventIDs)); -// updateMessage(Bundle.DetailViewPane_loggedTask_updateUI()); + updateMessage("updating ui"); return eventIDs.isEmpty() == false; } From 4d14c32a94e3fce033f8b52b0423047191fd0659 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 12:15:41 -0400 Subject: [PATCH 06/92] split AbstractVisualizationPane into AbstractTimeLineView and AbstractTimelineChart CountsViewPane and DetailViewPane extend AbstractTimelineChart, ListViewPane extends AbstractTimeLineView --- .../timeline/ui/AbstractTimeLineView.java | 360 +++++++++++++++++ ...onPane.java => AbstractTimelineChart.java} | 377 +----------------- .../autopsy/timeline/ui/TimeLineView.java | 14 - .../timeline/ui/VisualizationPanel.java | 4 +- .../ui/countsview/CountsViewPane.java | 12 +- .../ui/detailview/DetailViewPane.java | 13 +- .../timeline/ui/detailview/DetailsChart.java | 5 + .../ui/detailview/DetailsChartLane.java | 4 +- .../timeline/ui/detailview/EventNodeBase.java | 6 +- .../timeline/ui/detailview/GuideLine.java | 4 +- .../timeline/ui/listvew/ListChart.java | 51 ++- .../timeline/ui/listvew/ListViewChart.fxml | 12 +- .../timeline/ui/listvew/ListViewPane.java | 63 +-- 13 files changed, 452 insertions(+), 473 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java rename Core/src/org/sleuthkit/autopsy/timeline/ui/{AbstractVisualizationPane.java => AbstractTimelineChart.java} (57%) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java new file mode 100644 index 0000000000..e890a7387a --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -0,0 +1,360 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.ui; + +import com.google.common.eventbus.Subscribe; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutionException; +import java.util.logging.Level; +import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; +import javafx.beans.property.ReadOnlyBooleanProperty; +import javafx.beans.property.ReadOnlyBooleanWrapper; +import javafx.concurrent.Task; +import javafx.scene.Cursor; +import javafx.scene.Node; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.StackPane; +import org.controlsfx.control.MaskerPane; +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.TimeLineController; +import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; +import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; + +public abstract class AbstractTimeLineView extends BorderPane { + + private static final Logger LOGGER = Logger.getLogger(AbstractTimeLineView.class.getName()); + + /** + * Boolean property that holds true if the visualization does not show any + * events with the current zoom and filter settings. + */ + private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true); + + /** + * Boolean property that holds true if the visualization 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 outOfDate = new ReadOnlyBooleanWrapper(false); + + /** + * List of Nodes to insert into the toolbar. This should be set in an + * implementations constructor. + */ + private List settingsNodes; + + /** + * Listener that is attached to various properties that should trigger a vis + * update when they change. + */ + private InvalidationListener updateListener = (Observable any) -> refresh(); + + /** + * task used to reload the content of this visualization + */ + private Task updateTask; + + private final TimeLineController controller; + private final FilteredEventsModel filteredEvents; + + /** + * Constructor + * + * @param controller + */ + public AbstractTimeLineView(TimeLineController controller) { + this.controller = controller; + this.filteredEvents = controller.getEventsModel(); + this.filteredEvents.registerForEvents(this); + this.filteredEvents.zoomParametersProperty().addListener(updateListener); + TimeLineController.getTimeZone().addListener(updateListener); + } + + /** + * Handle a RefreshRequestedEvent from the events model by updating the + * visualization. + * + * @param event The RefreshRequestedEvent to handle. + */ + @Subscribe + public void handleRefreshRequested(RefreshRequestedEvent event) { + refresh(); + } + + /** + * Does the visualization represent an out-of-date state of the DB. It might + * if, for example, tags have been updated but the vis. was not refreshed. + * + * @return True if the visualization does not represent the curent state of + * the DB. + */ + public boolean isOutOfDate() { + return outOfDate.get(); + } + + /** + * Get a ReadOnlyBooleanProperty that holds true if this visualization does + * not represent the current state of the DB> + * + * @return A ReadOnlyBooleanProperty that holds the out-of-date state for + * this visualization. + */ + public ReadOnlyBooleanProperty outOfDateProperty() { + return outOfDate.getReadOnlyProperty(); + } + + /** + * Get the TimelineController for this visualization. + * + * @return The TimelineController for this visualization. + */ + protected TimeLineController getController() { + return controller; + } + + /** + * Refresh this visualization based on current state of zoom / filters. + * Primarily this invokes the background VisualizationUpdateTask returned by + * getUpdateTask(), which derived classes must implement. + * + * TODO: replace this logic with a javafx Service ? -jm + */ + protected final synchronized void refresh() { + if (updateTask != null) { + updateTask.cancel(true); + updateTask = null; + } + updateTask = getNewUpdateTask(); + updateTask.stateProperty().addListener((Observable observable) -> { + switch (updateTask.getState()) { + case CANCELLED: + case FAILED: + case READY: + case RUNNING: + case SCHEDULED: + break; + case SUCCEEDED: + try { + this.hasVisibleEvents.set(updateTask.get()); + } catch (InterruptedException | ExecutionException ex) { + LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS + } + break; + } + }); + getController().monitorTask(updateTask); + } + + /** + * Get the FilteredEventsModel for this visualization. + * + * @return The FilteredEventsModel for this visualization. + */ + protected FilteredEventsModel getEventsModel() { + return filteredEvents; + } + + /** + * Get a new background Task that fetches the appropriate data and loads it + * into this visualization. + * + * @return A new task to execute on a background thread to reload this + * visualization with different data. + */ + protected abstract Task getNewUpdateTask(); + + /** + * Get a List of nodes containing settings widgets to insert into this + * visualization's header. + * + * @return The List of settings Nodes. + */ + protected List getSettingsNodes() { + return Collections.unmodifiableList(settingsNodes); + } + + /** + * Set the List of nodes containing settings widgets to insert into this + * visualization's header. + * + * + * @param settingsNodes The List of nodes containing settings widgets to + * insert into this visualization's header. + */ + final protected void setSettingsNodes(List settingsNodes) { + this.settingsNodes = new ArrayList<>(settingsNodes); + } + + /** + * Dispose of this visualization and any resources it holds onto. + */ + final synchronized void dispose() { + //cancel and gc updateTask + if (updateTask != null) { + updateTask.cancel(true); + updateTask = null; + } + //remvoe and gc updateListener + this.filteredEvents.zoomParametersProperty().removeListener(updateListener); + TimeLineController.getTimeZone().removeListener(updateListener); + updateListener = null; + filteredEvents.unRegisterForEvents(this); + } + + /** + * Are there are any events visible in this visualization with the current + * view parameters? + * + * @return True if there are events visible in this visualization with the + * current view parameters. + */ + boolean hasVisibleEvents() { + return hasVisibleEventsProperty().get(); + } + + /** + * A property that indicates whether there are any events visible in this + * visualization with the current view parameters. + * + * @return A property that indicates whether there are any events visible in + * this visualization with the current view parameters. + */ + ReadOnlyBooleanProperty hasVisibleEventsProperty() { + return hasVisibleEvents.getReadOnlyProperty(); + } + + /** + * Set this visualization out of date because, for example, tags have been + * updated but the vis. was not refreshed. + */ + void setOutOfDate() { + outOfDate.set(true); + } + + /** + * Clear all data items from this chart. + */ + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + abstract protected void clearData(); + + /** + * Base class for Tasks that refreshes a view when the view settings change. + * + * @param The type of a single object that can represent + * the range of data displayed along the X-Axis. + */ + protected abstract class ViewRefreshTask extends LoggedTask { + + private final Node center; + + /** + * Constructor + * + * @param taskName The name of this task. + * @param logStateChanges Whether or not task state changes should be + * logged. + */ + protected ViewRefreshTask(String taskName, boolean logStateChanges) { + super(taskName, logStateChanges); + this.center = getCenter(); + } + + /** + * Sets initial progress value and message and shows blocking progress + * indicator over the visualization. Derived Tasks should be sure to + * call this as part of their call() implementation. + * + * @return True + * + * @throws Exception If there is an unhandled exception during the + * background operation + */ + @NbBundle.Messages(value = {"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) + @Override + protected Boolean call() throws Exception { + updateProgress(-1, 1); + updateMessage(Bundle.VisualizationUpdateTask_preparing()); + Platform.runLater(() -> { + MaskerPane maskerPane = new MaskerPane(); + maskerPane.textProperty().bind(messageProperty()); + maskerPane.progressProperty().bind(progressProperty()); + setCenter(new StackPane(center, maskerPane)); + setCursor(Cursor.WAIT); + }); + return true; + } + + /** + * Updates the horizontal axis and removes the blocking progress + * indicator. Derived Tasks should be sure to call this as part of their + * succeeded() implementation. + */ + @Override + protected void succeeded() { + super.succeeded(); + outOfDate.set(false); + cleanup(); + } + + /** + * Removes the blocking progress indicator. Derived Tasks should be sure + * to call this as part of their cancelled() implementation. + */ + @Override + protected void cancelled() { + super.cancelled(); + cleanup(); + } + + /** + * Removes the blocking progress indicator. Derived Tasks should be sure + * to call this as part of their failed() implementation. + */ + @Override + protected void failed() { + super.failed(); + cleanup(); + } + + /** + * Removes the blocking progress indicator and reset the cursor to the + * default. + */ + private void cleanup() { + setCenter(center); //clear masker pane installed in call() + setCursor(Cursor.DEFAULT); + } + + /** + * Set the horizontal range that this chart will show. + * + * @param values A single object representing the range that this chart + * will show. + */ + protected abstract void setDateValues(AxisValuesType values); + + /** + * Clears the chart data and sets the horizontal axis range. For use + * within the derived implementation of the call() method. + * + * @param axisValues + */ + @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) + protected void resetView(AxisValuesType axisValues) { + Platform.runLater(() -> { + clearData(); + setDateValues(axisValues); + }); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java similarity index 57% rename from Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java rename to Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java index ac58f65ca5..17209b9a4b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualizationPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java @@ -18,27 +18,14 @@ */ package org.sleuthkit.autopsy.timeline.ui; -import com.google.common.eventbus.Subscribe; -import java.util.ArrayList; -import java.util.Collections; import java.util.Comparator; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.ExecutionException; -import java.util.logging.Level; -import javafx.application.Platform; -import javafx.beans.InvalidationListener; -import javafx.beans.Observable; -import javafx.beans.property.ReadOnlyBooleanProperty; -import javafx.beans.property.ReadOnlyBooleanWrapper; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.collections.transformation.SortedList; -import javafx.concurrent.Task; import javafx.geometry.Pos; -import javafx.scene.Cursor; import javafx.scene.Node; import javafx.scene.chart.Axis; import javafx.scene.chart.XYChart; @@ -46,14 +33,12 @@ import javafx.scene.control.Label; import javafx.scene.control.OverrunStyle; import javafx.scene.control.Tooltip; import javafx.scene.layout.Border; -import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderWidths; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; -import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; @@ -61,15 +46,11 @@ import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import javax.annotation.concurrent.Immutable; import org.apache.commons.lang3.StringUtils; -import org.controlsfx.control.MaskerPane; 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.TimeLineController; -import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; /** * Abstract base class for TimeLineChart based visualizations. @@ -85,9 +66,9 @@ import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; * * TODO: pull up common history context menu items out of derived classes? -jm */ -public abstract class AbstractVisualizationPane> extends BorderPane implements TimeLineView { +public abstract class AbstractTimelineChart> extends AbstractTimeLineView { - private static final Logger LOGGER = Logger.getLogger(AbstractVisualizationPane.class.getName()); + private static final Logger LOGGER = Logger.getLogger(AbstractTimelineChart.class.getName()); @NbBundle.Messages("AbstractVisualization.Default_Tooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.") private static final Tooltip DEFAULT_TOOLTIP = new Tooltip(Bundle.AbstractVisualization_Default_Tooltip_text()); @@ -99,22 +80,19 @@ public abstract class AbstractVisualizationPane of the nodes that are selected in + * this visualization. */ - private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false); - - /** - * Boolean property that holds true if the visualization does not show any - * events with the current zoom and filter settings. - */ - private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true); + protected ObservableList getSelectedNodes() { + return selectedNodes; + } /** * Access to chart data via series @@ -127,54 +105,11 @@ public abstract class AbstractVisualizationPane updateTask; - - final private TimeLineController controller; - final private FilteredEventsModel filteredEvents; - final private ObservableList selectedNodes = FXCollections.observableArrayList(); - /** - * Listener that is attached to various properties that should trigger a vis - * update when they change. - */ - private InvalidationListener updateListener = any -> refresh(); - - /** - * Does the visualization represent an out-of-date state of the DB. It might - * if, for example, tags have been updated but the vis. was not refreshed. - * - * @return True if the visualization does not represent the curent state of - * the DB. - */ - public boolean isOutOfDate() { - return outOfDate.get(); - } - - /** - * Set this visualization out of date because, for example, tags have been - * updated but the vis. was not refreshed. - */ - void setOutOfDate() { - outOfDate.set(true); - } - - /** - * Get a ReadOnlyBooleanProperty that holds true if this visualization does - * not represent the current state of the DB> - * - * @return A ReadOnlyBooleanProperty that holds the out-of-date state for - * this visualization. - */ - public ReadOnlyBooleanProperty outOfDateProperty() { - return outOfDate.getReadOnlyProperty(); - } - public Pane getSpecificLabelPane() { return specificLabelPane; } @@ -187,53 +122,6 @@ public abstract class AbstractVisualizationPane of the nodes that are selected in - * this visualization. - */ - protected ObservableList getSelectedNodes() { - return selectedNodes; - } - - /** - * List of Nodes to insert into the toolbar. This should be set in an - * implementations constructor. - */ - private List settingsNodes; - - /** - * Get a List of nodes containing settings widgets to insert into this - * visualization's header. - * - * @return The List of settings Nodes. - */ - protected List getSettingsNodes() { - return Collections.unmodifiableList(settingsNodes); - } - - /** - * Set the List of nodes containing settings widgets to insert into this - * visualization's header. - * - * - * @param settingsNodes The List of nodes containing settings widgets to - * insert into this visualization's header. - */ - protected void setSettingsNodes(List settingsNodes) { - this.settingsNodes = new ArrayList<>(settingsNodes); - } - - /** - * Get the TimelineController for this visualization. - * - * @return The TimelineController for this visualization. - */ - protected TimeLineController getController() { - return controller; - } - /** * Get the CharType that implements this visualization. * @@ -243,15 +131,6 @@ public abstract class AbstractVisualizationPane getNewUpdateTask(); - /** * Get the label that should be used for a tick mark at the given value. * @@ -373,74 +221,6 @@ public abstract class AbstractVisualizationPane { - switch (updateTask.getState()) { - case CANCELLED: - case FAILED: - case READY: - case RUNNING: - case SCHEDULED: - break; - case SUCCEEDED: - try { - this.hasVisibleEvents.set(updateTask.get()); - } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS - } - break; - } - }); - controller.monitorTask(updateTask); - } - - /** - * Handle a RefreshRequestedEvent from the events model by updating the - * visualization. - * - * @param event The RefreshRequestedEvent to handle. - */ - @Subscribe - public void handleRefreshRequested(RefreshRequestedEvent event) { - refresh(); - } - - /** - * Dispose of this visualization and any resources it holds onto. - */ - final synchronized void dispose() { - - //cancel and gc updateTask - if (updateTask != null) { - updateTask.cancel(true); - updateTask = null; - } - //remvoe and gc updateListener - this.filteredEvents.zoomParametersProperty().removeListener(updateListener); - TimeLineController.getTimeZone().removeListener(updateListener); - updateListener = null; - - filteredEvents.unRegisterForEvents(this); - } - /** * Make a series for each event type in a consistent order. */ @@ -470,23 +250,8 @@ public abstract class AbstractVisualizationPane { -// VBox vBox = new VBox(specificLabelPane, contextLabelPane); -// vBox.setFillWidth(false); -// HBox hBox = new HBox(spacer, vBox); -// hBox.setFillHeight(false); -// setBottom(hBox); -// DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); -// spacer.minWidthProperty().bind(spacerSize); -// spacer.prefWidthProperty().bind(spacerSize); -// spacer.maxWidthProperty().bind(spacerSize); -// }); - + protected AbstractTimelineChart(TimeLineController controller) { + super(controller); createSeries(); selectedNodes.addListener((ListChangeListener.Change change) -> { @@ -496,10 +261,8 @@ public abstract class AbstractVisualizationPane controller.setStatusMessage(isHover() ? DEFAULT_TOOLTIP.getText() : "")); + hoverProperty().addListener(hoverProp -> controller.setStatusMessage(isHover() ? getDefaultTooltip().getText() : "")); } @@ -690,116 +453,4 @@ public abstract class AbstractVisualizationPane The type of a single object that can represent - * the range of data displayed along the X-Axis. - */ - abstract protected class VisualizationRefreshTask extends LoggedTask { - - private final Node center; - - /** - * Constructor - * - * @param taskName The name of this task. - * @param logStateChanges Whether or not task state changes should be - * logged. - */ - protected VisualizationRefreshTask(String taskName, boolean logStateChanges) { - super(taskName, logStateChanges); - this.center = getCenter(); - } - - /** - * Sets initial progress value and message and shows blocking progress - * indicator over the visualization. Derived Tasks should be sure to - * call this as part of their call() implementation. - * - * @return True - * - * @throws Exception If there is an unhandled exception during the - * background operation - */ - @NbBundle.Messages({"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) - @Override - protected Boolean call() throws Exception { - updateProgress(-1, 1); - updateMessage(Bundle.VisualizationUpdateTask_preparing()); - Platform.runLater(() -> { - MaskerPane maskerPane = new MaskerPane(); - maskerPane.textProperty().bind(messageProperty()); - maskerPane.progressProperty().bind(progressProperty()); - setCenter(new StackPane(center, maskerPane)); - setCursor(Cursor.WAIT); - }); - - return true; - } - - /** - * Updates the horizontal axis and removes the blocking progress - * indicator. Derived Tasks should be sure to call this as part of their - * succeeded() implementation. - */ - @Override - protected void succeeded() { - super.succeeded(); - outOfDate.set(false); - cleanup(); - } - - /** - * Removes the blocking progress indicator. Derived Tasks should be sure - * to call this as part of their cancelled() implementation. - */ - @Override - protected void cancelled() { - super.cancelled(); - cleanup(); - } - - /** - * Removes the blocking progress indicator. Derived Tasks should be sure - * to call this as part of their failed() implementation. - */ - @Override - protected void failed() { - super.failed(); - cleanup(); - } - - /** - * Removes the blocking progress indicator and reset the cursor to the - * default. - */ - private void cleanup() { - setCenter(center); //clear masker pane installed in call() - setCursor(Cursor.DEFAULT); - } - - /** - * Clears the chart data and sets the horizontal axis range. For use - * within the derived implementation of the call() method. - * - * @param axisValues - */ - @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) - protected void resetChart(AxisValuesType axisValues) { - Platform.runLater(() -> { - clearChartData(); - setDateAxisValues(axisValues); - }); - } - - /** - * Set the horizontal range that this chart will show. - * - * @param values A single object representing the range that this chart - * will show. - */ - abstract protected void setDateAxisValues(AxisValuesType values); - } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java deleted file mode 100644 index cfbd3c9f5a..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineView.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.timeline.ui; - -/** - * - * @author jmillman - */ -public interface TimeLineView { - -} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index f31b5e4f0d..14e92b2998 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -121,7 +121,7 @@ final public class VisualizationPanel extends BorderPane { private LoggedTask histogramTask; private final EventsTree eventsTree; - private AbstractVisualizationPane visualization; + private AbstractTimeLineView visualization; /* * HBox that contains the histogram bars. @@ -580,7 +580,7 @@ final public class VisualizationPanel extends BorderPane { * AbstractVislualization for one of the correct type. */ private void syncVisualizationMode() { - AbstractVisualizationPane vizPane; + AbstractTimeLineView vizPane; VisualizationMode visMode = controller.visualizationModeProperty().get(); //make new visualization. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index 575a6ca900..c5d9ddb6a4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -60,7 +60,7 @@ import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** @@ -79,7 +79,7 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; * Platform.runLater(java.lang.Runnable). The FilteredEventsModel should * encapsulate all need synchronization internally. */ -public class CountsViewPane extends AbstractVisualizationPane { +public class CountsViewPane extends AbstractTimelineChart { private static final Logger LOGGER = Logger.getLogger(CountsViewPane.class.getName()); @@ -170,7 +170,7 @@ public class CountsViewPane extends AbstractVisualizationPane series : dataSeries) { series.getData().clear(); } @@ -349,7 +349,7 @@ public class CountsViewPane extends AbstractVisualizationPane> { + private class CountsUpdateTask extends ViewRefreshTask> { CountsUpdateTask() { super(Bundle.CountsViewPane_loggedTask_name(), true); @@ -374,7 +374,7 @@ public class CountsViewPane extends AbstractVisualizationPane intervals = rangeInfo.getIntervals(); //clear old data, and reset ranges and series - resetChart(Lists.transform(intervals, rangeInfo::formatForTick)); + resetView(Lists.transform(intervals, rangeInfo::formatForTick)); updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts()); int chartMax = 0; @@ -432,7 +432,7 @@ public class CountsViewPane extends AbstractVisualizationPane categories) { + protected void setDateValues(List categories) { dateAxis.getCategories().setAll(categories); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index 13d76c6977..c3998e7cda 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -56,7 +56,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.utils.MappedList; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; @@ -75,7 +75,7 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams; * grouped EventStripes, etc, etc. The leaves of the trees are EventClusters or * SingleEvents. */ -public class DetailViewPane extends AbstractVisualizationPane, DetailsChart> { +public class DetailViewPane extends AbstractTimelineChart, DetailsChart> { private final static Logger LOGGER = Logger.getLogger(DetailViewPane.class.getName()); @@ -218,7 +218,7 @@ public class DetailViewPane extends AbstractVisualizationPane { + private class DetailsUpdateTask extends ViewRefreshTask { DetailsUpdateTask() { super(Bundle.DetailViewPane_loggedTask_name(), true); @@ -423,7 +422,7 @@ public class DetailViewPane extends AbstractVisualizationPane { return nestedEvents; } + Tooltip getDefaultTooltip() { + throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. + } + /** * Clear any time based UI elements (GuideLines, IntervalSelector,...) from * this chart. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java index b1e67e7eca..8dc24adff2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChartLane.java @@ -58,7 +58,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; import org.sleuthkit.autopsy.timeline.filters.DescriptionFilter; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider; /** @@ -178,7 +178,7 @@ abstract class DetailsChartLane extends XYChart()))); - Tooltip.install(this, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.install(this, AbstractTimelineChart.getDefaultTooltip()); dateAxis.setAutoRanging(false); setLegendVisible(false); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java index 66bdb7fdf3..4af8dd8c06 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventNodeBase.java @@ -77,7 +77,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.events.TagsAddedEvent; import org.sleuthkit.autopsy.timeline.events.TagsDeletedEvent; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; import org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider; import static org.sleuthkit.autopsy.timeline.ui.detailview.EventNodeBase.show; import static org.sleuthkit.autopsy.timeline.ui.detailview.MultiEventNodeBase.CORNER_RADII_3; @@ -167,7 +167,7 @@ public abstract class EventNodeBase extends StackPan //set up mouse hover effect and tooltip setOnMouseEntered(mouseEntered -> { - Tooltip.uninstall(chartLane, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.uninstall(chartLane, AbstractTimelineChart.getDefaultTooltip()); showHoverControls(true); toFront(); }); @@ -176,7 +176,7 @@ public abstract class EventNodeBase extends StackPan if (parentNode != null) { parentNode.showHoverControls(true); } else { - Tooltip.install(chartLane, AbstractVisualizationPane.getDefaultTooltip()); + Tooltip.install(chartLane, AbstractTimelineChart.getDefaultTooltip()); } }); setOnMouseClicked(new ClickHandler()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java index bbdd039a11..67e4de1c05 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java @@ -25,7 +25,6 @@ import javafx.scene.shape.Line; import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; /** * Subclass of {@link Line} with appropriate behavior (mouse listeners) to act @@ -35,7 +34,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; "GuideLine.tooltip.text={0}\nRight-click to remove.\nDrag to reposition."}) class GuideLine extends Line { - private static final Tooltip CHART_DEFAULT_TOOLTIP = AbstractVisualizationPane.getDefaultTooltip(); + private final Tooltip CHART_DEFAULT_TOOLTIP ; private final Tooltip tooltip = new Tooltip(); private final DetailsChart chart; @@ -49,6 +48,7 @@ class GuideLine extends Line { */ GuideLine(DetailsChart chart) { super(0, 0, 0, 0); + CHART_DEFAULT_TOOLTIP = chart.getDefaultTooltip(); this.chart = chart; Axis xAxis = chart.getXAxis(); endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty()))); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index 9235b763da..99aac7e3f0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -5,7 +5,7 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; -import java.util.List; +import java.util.Collection; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -43,16 +43,30 @@ class ListChart extends BorderPane implements TimeLineChart { @FXML private TableView table; - Callback, ObservableValue> cellValueFactory = param -> new SimpleObjectProperty<>(param.getValue()); + private static final Callback, ObservableValue> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue()); private final TimeLineController controller; - private final TableColumn idColumn = new TableColumn<>("Event ID"); - private final TableColumn millisColumn = new TableColumn<>("Date/Time"); - private final TableColumn iconColumn = new TableColumn<>("Icon"); - private final TableColumn descriptionColumn = new TableColumn<>("Description"); - private final TableColumn baseTypeColumn = new TableColumn<>("Base Type"); - private final TableColumn subTypeColumn = new TableColumn<>("Sub Type"); - private final TableColumn knownColumn = new TableColumn<>("Known"); + + @FXML + private TableColumn idColumn; + + @FXML + private TableColumn millisColumn; + + @FXML + private TableColumn iconColumn; + + @FXML + private TableColumn descriptionColumn; + + @FXML + private TableColumn baseTypeColumn; + + @FXML + private TableColumn subTypeColumn; + + @FXML + private TableColumn knownColumn; ListChart(TimeLineController controller) { this.controller = controller; @@ -71,26 +85,27 @@ class ListChart extends BorderPane implements TimeLineChart { assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; -// setRowFactory(tableView -> new EventRow()); - idColumn.setCellValueFactory(cellValueFactory); + table.setRowFactory(tableView -> new EventRow()); + idColumn.setCellValueFactory(CELL_VALUE_FACTORY); - millisColumn.setCellValueFactory(cellValueFactory); + millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); millisColumn.setCellFactory(col -> new EpochMillisCell()); - iconColumn.setCellValueFactory(cellValueFactory); + iconColumn.setCellValueFactory(CELL_VALUE_FACTORY); iconColumn.setCellFactory(col -> new ImageCell()); - descriptionColumn.setCellValueFactory(cellValueFactory); + descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY); descriptionColumn.setCellFactory(col -> new DescriptionCell()); - baseTypeColumn.setCellValueFactory(cellValueFactory); + baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); - subTypeColumn.setCellValueFactory(cellValueFactory); + subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); subTypeColumn.setCellFactory(col -> new EventTypeCell()); - knownColumn.setCellValueFactory(cellValueFactory); + knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); knownColumn.setCellFactory(col -> new KnownCell()); + eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } @@ -146,7 +161,7 @@ class ListChart extends BorderPane implements TimeLineChart { } @ThreadConfined(type = ThreadConfined.ThreadType.JFX) - void setEventIDs(List eventIDs) { + void setEventIDs(Collection eventIDs) { table.getItems().setAll(eventIDs); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml index f3b45572c2..87dfee3f19 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml @@ -8,7 +8,7 @@ - + @@ -23,13 +23,13 @@
- - + - + - - + + + 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 aa6468298d..363f734d4a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -8,15 +8,11 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.List; import javafx.application.Platform; import javafx.concurrent.Task; -import javafx.scene.Node; import javafx.scene.Parent; -import javafx.scene.chart.Axis; import org.joda.time.Interval; 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.ui.AbstractVisualizationPane; -import org.sleuthkit.autopsy.timeline.ui.TimeLineView; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView; /** * @param The type of data plotted along the x axis @@ -33,7 +29,9 @@ import org.sleuthkit.autopsy.timeline.ui.TimeLineView; * public abstract class AbstractVisualizationPane> extends BorderPane { */ -public class ListViewPane extends AbstractVisualizationPane implements TimeLineView { +public class ListViewPane extends AbstractTimeLineView { + + private final ListChart listChart; /** * Constructor @@ -42,55 +40,21 @@ public class ListViewPane extends AbstractVisualizationPane getNewUpdateTask() { return new ListUpdateTask(); } @Override - protected String getTickMarkLabel(Long tickValue) { - return ""; - } - - @Override - protected double getTickSpacing() { - return 0; - } - - @Override - protected Axis getXAxis() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - protected Axis getYAxis() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - protected double getAxisMargin() { - return 0; - } - - @Override - protected void clearChartData() { - getChart().clear(); + protected void clearData() { + listChart.clear(); } private static class ListViewSettingsPane extends Parent { @@ -99,7 +63,7 @@ public class ListViewPane extends AbstractVisualizationPane { + private class ListUpdateTask extends ViewRefreshTask { ListUpdateTask() { super("List update task", true); @@ -114,12 +78,12 @@ public class ListViewPane extends AbstractVisualizationPane eventIDs = eventsModel.getEventIDs(); - Platform.runLater(() -> getChart().setEventIDs(eventIDs)); + Platform.runLater(() -> listChart.setEventIDs(eventIDs)); updateMessage("updating ui"); return eventIDs.isEmpty() == false; @@ -132,9 +96,8 @@ public class ListViewPane extends AbstractVisualizationPane Date: Tue, 17 May 2016 12:21:18 -0400 Subject: [PATCH 07/92] cleanup, remove unneeded interface implementation --- .../timeline/ui/AbstractTimeLineView.java | 19 ++++- .../timeline/ui/listvew/ListChart.java | 71 +++++-------------- .../timeline/ui/listvew/ListViewPane.java | 19 ++++- 3 files changed, 49 insertions(+), 60 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java index e890a7387a..cfcb03d001 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.timeline.ui; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index 99aac7e3f0..6f01fbbec0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.timeline.ui.listvew; @@ -9,33 +22,25 @@ import java.util.Collection; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; import javafx.fxml.FXML; -import javafx.scene.Node; -import javafx.scene.chart.Axis; -import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; import javafx.scene.control.TableView; import javafx.scene.image.ImageView; -import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; import javafx.util.Callback; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; -import org.sleuthkit.autopsy.timeline.ui.IntervalSelector; -import org.sleuthkit.autopsy.timeline.ui.TimeLineChart; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; /** * */ -class ListChart extends BorderPane implements TimeLineChart { +class ListChart extends BorderPane { @FXML private Label eventCountLabel; @@ -109,52 +114,10 @@ class ListChart extends BorderPane implements TimeLineChart { eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } - @Override - public ObservableList getSelectedNodes() { - return FXCollections.observableArrayList(); - } - - @Override - public IntervalSelector getIntervalSelector() { - return null; - } - - @Override - public void setIntervalSelector(IntervalSelector newIntervalSelector) { -// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public IntervalSelector newIntervalSelector() { - return null; - } - - @Override - public void clearIntervalSelector() { -// throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public Axis getXAxis() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override public TimeLineController getController() { return controller; } - @Override - public void clearContextMenu() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - - @Override - public ContextMenu getContextMenu(MouseEvent m) { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - - } - @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void clear() { table.getItems().clear(); 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 363f734d4a..6f3fdec60f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -1,7 +1,20 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.sleuthkit.autopsy.timeline.ui.listvew; From 035d35856a727ae3e981d3bdf71af10d96689c4d Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 12:28:18 -0400 Subject: [PATCH 08/92] VisualizationMode ->ViewMode again --- .../autopsy/timeline/TimeLineController.java | 47 ++++++++++--------- .../timeline/TimeLineTopComponent.java | 30 ++++++------ .../{VisualizationMode.java => ViewMode.java} | 2 +- .../timeline/ui/VisualizationPanel.java | 16 +++---- .../ui/countsview/EventCountsChart.java | 4 +- .../timeline/ui/filtering/FilterSetPanel.java | 6 +-- .../timeline/zooming/ZoomSettingsPane.java | 4 +- 7 files changed, 57 insertions(+), 52 deletions(-) rename Core/src/org/sleuthkit/autopsy/timeline/{VisualizationMode.java => ViewMode.java} (96%) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index bbc213de10..a24a4ef85b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -199,11 +199,7 @@ public class TimeLineController { private final PropertyChangeListener ingestModuleListener = new AutopsyIngestModuleListener(); @GuardedBy("this") - private final ReadOnlyObjectWrapper visualizationMode = new ReadOnlyObjectWrapper<>(VisualizationMode.COUNTS); - - synchronized public ReadOnlyObjectProperty visualizationModeProperty() { - return visualizationMode.getReadOnlyProperty(); - } + private final ReadOnlyObjectWrapper viewMode = new ReadOnlyObjectWrapper<>(ViewMode.COUNTS); @GuardedBy("filteredEvents") private final FilteredEventsModel filteredEvents; @@ -223,6 +219,13 @@ public class TimeLineController { @GuardedBy("this") private final ObservableList selectedEventIDs = FXCollections.synchronizedObservableList(FXCollections.observableArrayList()); + @GuardedBy("this") + private final ReadOnlyObjectWrapper selectedTimeRange = new ReadOnlyObjectWrapper<>(); + + private final ReadOnlyBooleanWrapper eventsDBStale = new ReadOnlyBooleanWrapper(true); + + private final PromptDialogManager promptDialogManager = new PromptDialogManager(this); + /** * @return A list of the selected event ids */ @@ -230,9 +233,6 @@ public class TimeLineController { return selectedEventIDs; } - @GuardedBy("this") - private final ReadOnlyObjectWrapper selectedTimeRange = new ReadOnlyObjectWrapper<>(); - /** * @return a read only view of the selected interval. */ @@ -282,9 +282,25 @@ public class TimeLineController { synchronized public ReadOnlyBooleanProperty canRetreatProperty() { return historyManager.getCanRetreat(); } - private final ReadOnlyBooleanWrapper eventsDBStale = new ReadOnlyBooleanWrapper(true); - private final PromptDialogManager promptDialogManager = new PromptDialogManager(this); + synchronized public ReadOnlyObjectProperty viewModeProperty() { + return viewMode.getReadOnlyProperty(); + } + + /** + * Set a new ViewMode as the active one. + * + * @param viewMode The new ViewMode to set. + */ + synchronized public void setViewMode(ViewMode viewMode) { + if (this.viewMode.get() != viewMode) { + this.viewMode.set(viewMode); + } + } + + public ViewMode getViewMode() { + return viewMode.get(); + } public TimeLineController(Case autoCase) throws IOException { this.autoCase = autoCase; @@ -582,17 +598,6 @@ public class TimeLineController { pushTimeRange(new Interval(start, end)); } - /** - * Set a new Visualization mode as the active one. - * - * @param visualizationMode The new VisaualizationMode to set. - */ - synchronized public void setVisualizationMode(VisualizationMode visualizationMode) { - if (this.visualizationMode.get() != visualizationMode) { - this.visualizationMode.set(visualizationMode); - } - } - public void selectEventIDs(Collection events) { final LoggedTask selectEventIDsTask = new LoggedTask("Select Event IDs", true) { //NON-NLS @Override diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index f91d2e8bba..cb4c70b4b6 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -108,13 +108,13 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer final Tab eventsTreeTab = new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree); eventsTreeTab.setClosable(false); eventsTreeTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/timeline_marker.png")); // NON-NLS - eventsTreeTab.disableProperty().bind(controller.visualizationModeProperty().isEqualTo(VisualizationMode.COUNTS)); + eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL)); final TabPane leftTabPane = new TabPane(filterTab, eventsTreeTab); VBox.setVgrow(leftTabPane, Priority.ALWAYS); - controller.visualizationModeProperty().addListener((Observable observable) -> { - if (controller.visualizationModeProperty().get().equals(VisualizationMode.COUNTS)) { - //if view mode is counts, make sure events tabd is not active + controller.viewModeProperty().addListener((Observable observable) -> { + if (controller.viewModeProperty().get().equals(ViewMode.DETAIL) == false) { + //if view mode is counts, make sure events tab is not active leftTabPane.getSelectionModel().select(filterTab); } }); @@ -125,7 +125,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer final ZoomSettingsPane zoomSettingsPane = new ZoomSettingsPane(controller); - final VBox leftVBox = new VBox(5, timeZonePanel,historyToolBar, zoomSettingsPane, leftTabPane); + final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane); SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE); final SplitPane mainSplitPane = new SplitPane(leftVBox, visualizationPanel); @@ -134,16 +134,16 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer final Scene scene = new Scene(mainSplitPane); scene.addEventFilter(KeyEvent.KEY_PRESSED, (KeyEvent event) -> { - if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(event)) { - new Back(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(event)) { - new Back(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(event)) { - new Forward(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(event)) { - new Forward(controller).handle(null); - } - }); + if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(event)) { + new Back(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(event)) { + new Back(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(event)) { + new Forward(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(event)) { + new Forward(controller).handle(null); + } + }); //add ui componenets to JFXPanels jFXVizPanel.setScene(scene); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java b/Core/src/org/sleuthkit/autopsy/timeline/ViewMode.java similarity index 96% rename from Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java rename to Core/src/org/sleuthkit/autopsy/timeline/ViewMode.java index c624b1da9d..bbbc3fa11b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/VisualizationMode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ViewMode.java @@ -21,7 +21,7 @@ package org.sleuthkit.autopsy.timeline; /** * */ -public enum VisualizationMode { +public enum ViewMode { COUNTS, DETAIL, diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index 14e92b2998..17d989f04c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -72,7 +72,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.VisualizationMode; +import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.actions.Back; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; import org.sleuthkit.autopsy.timeline.actions.SaveSnapshotAsReport; @@ -284,18 +284,18 @@ final public class VisualizationPanel extends BorderPane { detailsToggle.setText(Bundle.VisualizationPanel_detailsToggle_text()); listToggle.setText(Bundle.VisualizationPanel_listToggle_text()); - ToggleGroupValue visModeToggleGroup = new ToggleGroupValue<>(); - visModeToggleGroup.add(listToggle, VisualizationMode.LIST); - visModeToggleGroup.add(detailsToggle, VisualizationMode.DETAIL); - visModeToggleGroup.add(countsToggle, VisualizationMode.COUNTS); + ToggleGroupValue visModeToggleGroup = new ToggleGroupValue<>(); + visModeToggleGroup.add(listToggle, ViewMode.LIST); + visModeToggleGroup.add(detailsToggle, ViewMode.DETAIL); + visModeToggleGroup.add(countsToggle, ViewMode.COUNTS); modeSegButton.setToggleGroup(visModeToggleGroup); visModeToggleGroup.valueProperty().addListener((observable, oldVisMode, newValue) -> { - controller.setVisualizationMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : VisualizationMode.COUNTS)); + controller.setViewMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : ViewMode.COUNTS)); }); - controller.visualizationModeProperty().addListener(visualizationMode -> syncVisualizationMode()); + controller.viewModeProperty().addListener(visualizationMode -> syncVisualizationMode()); syncVisualizationMode(); ActionUtils.configureButton(new SaveSnapshotAsReport(controller, notificationPane::getContent), snapShotButton); @@ -581,7 +581,7 @@ final public class VisualizationPanel extends BorderPane { */ private void syncVisualizationMode() { AbstractTimeLineView vizPane; - VisualizationMode visMode = controller.visualizationModeProperty().get(); + ViewMode visMode = controller.viewModeProperty().get(); //make new visualization. switch (visMode) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java index 031101ab1e..84502d62f4 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/EventCountsChart.java @@ -47,7 +47,7 @@ import org.joda.time.Seconds; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.ColorUtilities; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.VisualizationMode; +import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.RootEventType; @@ -403,7 +403,7 @@ final class EventCountsChart extends StackedBarChart implements Bundle.CountsViewPane_detailSwitchMessage(), Bundle.CountsViewPane_detailSwitchTitle(), JOptionPane.YES_NO_OPTION); if (showConfirmDialog == JOptionPane.YES_OPTION) { - controller.setVisualizationMode(VisualizationMode.DETAIL); + controller.setViewMode(ViewMode.DETAIL); } /* diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index d7168013ca..819a7998de 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -41,7 +41,7 @@ import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.VisualizationMode; +import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; @@ -129,9 +129,9 @@ final public class FilterSetPanel extends BorderPane { hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); hiddenDescriptionsListView.setCellFactory(listView -> getNewDiscriptionFilterListCell()); - controller.visualizationModeProperty().addListener(observable -> { + controller.viewModeProperty().addListener(observable -> { applyFilters(); - if (controller.visualizationModeProperty().get() == VisualizationMode.COUNTS) { + if (controller.viewModeProperty().get() == ViewMode.COUNTS) { dividerPosition = splitPane.getDividerPositions()[0]; splitPane.setDividerPositions(1); hiddenDescriptionsPane.setExpanded(false); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java index 9877150e8b..800c132f8d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java @@ -31,7 +31,7 @@ import javafx.util.StringConverter; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.VisualizationMode; +import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; @@ -102,7 +102,7 @@ public class ZoomSettingsPane extends TitledPane { Function.identity()); descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text()); //the description slider is only usefull in the detail view - descrLODSlider.disableProperty().bind(controller.visualizationModeProperty().isEqualTo(VisualizationMode.COUNTS)); + descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.COUNTS)); /** * In order for the selected value in the time unit slider to correspond From affc212b893f99ed68435353c9d930ecf03ded05 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 12:28:18 -0400 Subject: [PATCH 09/92] disable unneeded ui elements when list mode is selected --- .../timeline/TimeLineTopComponent.java | 5 +- .../timeline/ui/VisualizationPanel.java | 72 ++++++++++--------- .../timeline/ui/filtering/FilterSetPanel.java | 30 ++++---- .../timeline/zooming/ZoomSettingsPane.java | 4 +- 4 files changed, 62 insertions(+), 49 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index cb4c70b4b6..5ab8584112 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -22,7 +22,6 @@ import java.awt.BorderLayout; import java.util.Collections; import java.util.List; import javafx.application.Platform; -import javafx.beans.Observable; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.control.SplitPane; @@ -112,8 +111,8 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer final TabPane leftTabPane = new TabPane(filterTab, eventsTreeTab); VBox.setVgrow(leftTabPane, Priority.ALWAYS); - controller.viewModeProperty().addListener((Observable observable) -> { - if (controller.viewModeProperty().get().equals(ViewMode.DETAIL) == false) { + controller.viewModeProperty().addListener(viewMode -> { + if (controller.getViewMode().equals(ViewMode.DETAIL) == false) { //if view mode is counts, make sure events tab is not active leftTabPane.getSelectionModel().select(filterTab); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java index 17d989f04c..7176440656 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.java @@ -91,8 +91,8 @@ import org.sleuthkit.autopsy.timeline.ui.listvew.ListViewPane; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; /** - * A container for an AbstractVisualizationPane. Has a Toolbar on top to hold - * settings widgets supplied by contained AbstractVisualizationPane, and the + * A container for an AbstractTimelineView. Has a Toolbar on top to hold + * settings widgets supplied by contained AbstractTimelineView, and the * histogram / time selection on bottom. * * TODO: Refactor common code out of histogram and CountsView? -jm @@ -121,7 +121,7 @@ final public class VisualizationPanel extends BorderPane { private LoggedTask histogramTask; private final EventsTree eventsTree; - private AbstractTimeLineView visualization; + private AbstractTimeLineView hostedView; /* * HBox that contains the histogram bars. @@ -295,8 +295,8 @@ final public class VisualizationPanel extends BorderPane { controller.setViewMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : ViewMode.COUNTS)); }); - controller.viewModeProperty().addListener(visualizationMode -> syncVisualizationMode()); - syncVisualizationMode(); + controller.viewModeProperty().addListener(viewMode -> syncViewMode()); + syncViewMode(); ActionUtils.configureButton(new SaveSnapshotAsReport(controller, notificationPane::getContent), snapShotButton); ActionUtils.configureButton(new UpdateDB(controller), updateDBButton); @@ -326,7 +326,7 @@ final public class VisualizationPanel extends BorderPane { rangeHistogramStack.getChildren().add(rangeSlider); /* - * this padding attempts to compensates for the fact that the + * This padding attempts to compensates for the fact that the * rangeslider track doesn't extend to edge of node,and so the * histrogram doesn't quite line up with the rangeslider */ @@ -372,7 +372,7 @@ final public class VisualizationPanel extends BorderPane { */ @Subscribe public void handleTimeLineTagUpdate(TagsUpdatedEvent event) { - visualization.setOutOfDate(); + hostedView.setOutOfDate(); Platform.runLater(() -> { if (notificationPane.isShowing() == false) { notificationPane.getActions().setAll(new Refresh()); @@ -410,7 +410,7 @@ final public class VisualizationPanel extends BorderPane { */ @Subscribe public void handleDBUpdated(DBUpdatedEvent event) { - visualization.refresh(); + hostedView.refresh(); refreshHistorgram(); Platform.runLater(notificationPane::hide); } @@ -576,22 +576,28 @@ final public class VisualizationPanel extends BorderPane { } /** - * Switch to the given VisualizationMode, by swapping out the hosted - * AbstractVislualization for one of the correct type. + * Switch to the given ViewMode, by swapping out the hosted + * AbstractTimelineView for one of the correct type. */ - private void syncVisualizationMode() { - AbstractTimeLineView vizPane; - ViewMode visMode = controller.viewModeProperty().get(); + private void syncViewMode() { + AbstractTimeLineView view; + ViewMode viewMode = controller.viewModeProperty().get(); //make new visualization. - switch (visMode) { + switch (viewMode) { case LIST: - vizPane = new ListViewPane(controller); - Platform.runLater(() -> listToggle.setSelected(true)); + view = new ListViewPane(controller); + Platform.runLater(() -> { + listToggle.setSelected(true); + //TODO: should remove listeners from events tree + }); break; case COUNTS: - vizPane = new CountsViewPane(controller); - Platform.runLater(() -> countsToggle.setSelected(true)); + view = new CountsViewPane(controller); + Platform.runLater(() -> { + countsToggle.setSelected(true); + //TODO: should remove listeners from events tree + }); break; case DETAIL: DetailViewPane detailViewPane = new DetailViewPane(controller); @@ -600,34 +606,34 @@ final public class VisualizationPanel extends BorderPane { detailViewPane.setHighLightedEvents(eventsTree.getSelectedEvents()); eventsTree.setDetailViewPane(detailViewPane); }); - vizPane = detailViewPane; + view = detailViewPane; break; default: - throw new IllegalArgumentException("Unknown VisualizationMode: " + visMode.toString()); + throw new IllegalArgumentException("Unknown VisualizationMode: " + viewMode.toString()); } //Set the new AbstractVisualizationPane as the one hosted by this VisualizationPanel. Platform.runLater(() -> { //clear out old vis. - if (visualization != null) { - toolBar.getItems().removeAll(visualization.getSettingsNodes()); - visualization.dispose(); + if (hostedView != null) { + toolBar.getItems().removeAll(hostedView.getSettingsNodes()); + hostedView.dispose(); } - visualization = vizPane; + hostedView = view; //setup new vis. ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new visualization - visualization.refresh(); - toolBar.getItems().addAll(2, vizPane.getSettingsNodes()); - notificationPane.setContent(visualization); + hostedView.refresh(); + toolBar.getItems().addAll(2, view.getSettingsNodes()); + notificationPane.setContent(hostedView); //listen to has events property and show "dialog" if it is false. - visualization.hasVisibleEventsProperty().addListener(hasEvents -> { - notificationPane.setContent(visualization.hasVisibleEvents() - ? visualization - : new StackPane(visualization, + hostedView.hasVisibleEventsProperty().addListener(hasEvents -> { + notificationPane.setContent(hostedView.hasVisibleEvents() + ? hostedView + : new StackPane(hostedView, NO_EVENTS_BACKGROUND, - new NoEventsDialog(() -> notificationPane.setContent(visualization)) + new NoEventsDialog(() -> notificationPane.setContent(hostedView)) ) ); }); @@ -773,7 +779,7 @@ final public class VisualizationPanel extends BorderPane { setLongText(Bundle.VisualizationPanel_refresh_longText()); setGraphic(new ImageView(REFRESH)); setEventHandler(actionEvent -> filteredEvents.postRefreshRequest()); - disabledProperty().bind(visualization.outOfDateProperty().not()); + disabledProperty().bind(hostedView.outOfDateProperty().not()); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index 819a7998de..c1db261b2f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -131,18 +131,24 @@ final public class FilterSetPanel extends BorderPane { controller.viewModeProperty().addListener(observable -> { applyFilters(); - if (controller.viewModeProperty().get() == ViewMode.COUNTS) { - dividerPosition = splitPane.getDividerPositions()[0]; - splitPane.setDividerPositions(1); - hiddenDescriptionsPane.setExpanded(false); - hiddenDescriptionsPane.setCollapsible(false); - hiddenDescriptionsPane.setDisable(true); - } else { - splitPane.setDividerPositions(dividerPosition); - hiddenDescriptionsPane.setDisable(false); - hiddenDescriptionsPane.setCollapsible(true); - hiddenDescriptionsPane.setExpanded(true); - hiddenDescriptionsPane.setCollapsible(false); + switch (controller.getViewMode()) { + case COUNTS: + case LIST: + dividerPosition = splitPane.getDividerPositions()[0]; + splitPane.setDividerPositions(1); + hiddenDescriptionsPane.setExpanded(false); + hiddenDescriptionsPane.setCollapsible(false); + hiddenDescriptionsPane.setDisable(true); + break; + case DETAIL: + splitPane.setDividerPositions(dividerPosition); + hiddenDescriptionsPane.setDisable(false); + hiddenDescriptionsPane.setCollapsible(true); + hiddenDescriptionsPane.setExpanded(true); + hiddenDescriptionsPane.setCollapsible(false); + break; + default: + throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java index 800c132f8d..1b783a6e3d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java @@ -92,6 +92,7 @@ public class ZoomSettingsPane extends TitledPane { EventTypeZoomLevel::ordinal, Function.identity()); typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text()); + typeZoomSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.LIST)); descrLODSlider.setMax(DescriptionLoD.values().length - 1); configureSliderListeners(descrLODSlider, @@ -102,7 +103,7 @@ public class ZoomSettingsPane extends TitledPane { Function.identity()); descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text()); //the description slider is only usefull in the detail view - descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.COUNTS)); + descrLODSlider.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL)); /** * In order for the selected value in the time unit slider to correspond @@ -121,6 +122,7 @@ public class ZoomSettingsPane extends TitledPane { modelTimeRange -> RangeDivisionInfo.getRangeDivisionInfo(modelTimeRange).getPeriodSize().ordinal() - 1, index -> index + 1); //compensate for the -1 above when mapping to the Enum whose displayName will be shown at index timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text()); + timeUnitSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.LIST)); } /** From f82ec3aa8d4666506fc3b25e7f97b13a266aad91 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 13:34:50 -0400 Subject: [PATCH 10/92] remove ResultTable when ViewMode.List is active --- .../timeline/TimeLineTopComponent.java | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 5ab8584112..c116279ee5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -22,6 +22,7 @@ import java.awt.BorderLayout; import java.util.Collections; import java.util.List; import javafx.application.Platform; +import javafx.beans.Observable; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.control.SplitPane; @@ -33,6 +34,7 @@ import javafx.scene.input.KeyCodeCombination; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; +import javax.swing.SwingUtilities; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.util.NbBundle; @@ -86,10 +88,34 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer dataContentPanel = DataContentPanel.createInstance(); this.contentViewerContainerPanel.add(dataContentPanel, BorderLayout.CENTER); tlrv = new TimeLineResultView(controller, dataContentPanel); - DataResultPanel dataResultPanel = tlrv.getDataResultPanel(); + final DataResultPanel dataResultPanel = tlrv.getDataResultPanel(); this.resultContainerPanel.add(dataResultPanel, BorderLayout.CENTER); dataResultPanel.open(); customizeFXComponents(); + + controller.viewModeProperty().addListener((Observable observable) -> { + switch (controller.getViewMode()) { + case COUNTS: + case DETAIL: + SwingUtilities.invokeLater(() -> { + splitYPane.remove(contentViewerContainerPanel); + if ((lowerSplitXPane.getParent() == splitYPane) == false) { + splitYPane.add(lowerSplitXPane); + lowerSplitXPane.add(contentViewerContainerPanel); + } + }); + break; + case LIST: + SwingUtilities.invokeLater(() -> { + splitYPane.remove(lowerSplitXPane); + splitYPane.add(contentViewerContainerPanel); + dataResultPanel.setNode(null); + }); + break; + default: + throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); + } + }); } @NbBundle.Messages({"TimeLineTopComponent.eventsTab.name=Events", @@ -147,7 +173,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer //add ui componenets to JFXPanels jFXVizPanel.setScene(scene); jFXstatusPanel.setScene(new Scene(new StatusBar(controller))); - }); } From 2b5c0d11eb628636442a12ba96d9315cb111edc6 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 15:20:55 -0400 Subject: [PATCH 11/92] address code review comments --- .../autopsy/timeline/TimeLineController.java | 7 +- .../timeline/TimeLineTopComponent.java | 18 ++-- .../autopsy/timeline/db/EventDB.java | 7 ++ .../timeline/ui/AbstractTimeLineView.java | 87 +++++++++---------- .../timeline/ui/AbstractTimelineChart.java | 16 ++++ .../ui/countsview/CountsViewPane.java | 14 +-- .../ui/detailview/DetailViewPane.java | 14 --- .../timeline/ui/detailview/DetailsChart.java | 5 -- .../timeline/ui/detailview/GuideLine.java | 4 +- .../timeline/ui/filtering/FilterSetPanel.java | 4 +- 10 files changed, 91 insertions(+), 85 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index a24a4ef85b..5ac1eb68aa 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -298,7 +298,12 @@ public class TimeLineController { } } - public ViewMode getViewMode() { + /** + * Get the currently active ViewMode. + * + * @return The currently active ViewMode. + */ + synchronized public ViewMode getViewMode() { return viewMode.get(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index c116279ee5..aca7219998 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -22,7 +22,6 @@ import java.awt.BorderLayout; import java.util.Collections; import java.util.List; import javafx.application.Platform; -import javafx.beans.Observable; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.control.SplitPane; @@ -70,7 +69,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private final DataContentPanel dataContentPanel; - private final TimeLineResultView tlrv; + private final TimeLineResultView tlResultView; private final ExplorerManager em = new ExplorerManager(); @@ -87,16 +86,21 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer dataContentPanel = DataContentPanel.createInstance(); this.contentViewerContainerPanel.add(dataContentPanel, BorderLayout.CENTER); - tlrv = new TimeLineResultView(controller, dataContentPanel); - final DataResultPanel dataResultPanel = tlrv.getDataResultPanel(); + tlResultView = new TimeLineResultView(controller, dataContentPanel); + final DataResultPanel dataResultPanel = tlResultView.getDataResultPanel(); this.resultContainerPanel.add(dataResultPanel, BorderLayout.CENTER); dataResultPanel.open(); customizeFXComponents(); - controller.viewModeProperty().addListener((Observable observable) -> { + //Listen to ViewMode and adjust GUI componenets as needed. + controller.viewModeProperty().addListener(viewMode -> { switch (controller.getViewMode()) { case COUNTS: case DETAIL: + /* + * For counts and details mode, restore the result table at + * the bottom left, if neccesary. + */ SwingUtilities.invokeLater(() -> { splitYPane.remove(contentViewerContainerPanel); if ((lowerSplitXPane.getParent() == splitYPane) == false) { @@ -106,6 +110,10 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer }); break; case LIST: + /* + * For list mode, remove the result table, and let the + * content viewer expand across the bottom. + */ SwingUtilities.invokeLater(() -> { splitYPane.remove(lowerSplitXPane); splitYPane.add(contentViewerContainerPanel); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java index 559b039b41..1767089cdb 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/db/EventDB.java @@ -584,6 +584,13 @@ public class EventDB { insertHashHitStmt = prepareStatement("INSERT OR IGNORE INTO hash_set_hits (hash_set_id, event_id) values (?,?)"); //NON-NLS insertTagStmt = prepareStatement("INSERT OR IGNORE INTO tags (tag_id, tag_name_id,tag_name_display_name, event_id) values (?,?,?,?)"); //NON-NLS deleteTagStmt = prepareStatement("DELETE FROM tags WHERE tag_id = ?"); //NON-NLS + + /* + * This SQL query is really just a select count(*), but that has + * performance problems on very large tables unless you include + * a where clause see http://stackoverflow.com/a/9338276/4004683 + * for more. + */ countAllEventsStmt = prepareStatement("SELECT count(event_id) AS count FROM events WHERE event_id IS NOT null"); //NON-NLS dropEventsTableStmt = prepareStatement("DROP TABLE IF EXISTS events"); //NON-NLS dropHashSetHitsTableStmt = prepareStatement("DROP TABLE IF EXISTS hash_set_hits"); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java index cfcb03d001..faf55aef15 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -48,32 +48,32 @@ public abstract class AbstractTimeLineView extends BorderPane { private static final Logger LOGGER = Logger.getLogger(AbstractTimeLineView.class.getName()); /** - * Boolean property that holds true if the visualization does not show any - * events with the current zoom and filter settings. + * Boolean property that holds true if the view does not show any events + * with the current zoom and filter settings. */ private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true); /** - * Boolean property that holds true if the visualization may not represent - * the current state of the DB, because, for example, tags have been updated - * but the vis. was not refreshed. + * Boolean property that holds true if the view may not represent the + * current state of the DB, because, for example, tags have been updated but + * the view. was not refreshed. */ private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false); /** * List of Nodes to insert into the toolbar. This should be set in an - * implementations constructor. + * implementation's constructor. */ private List settingsNodes; /** - * Listener that is attached to various properties that should trigger a vis - * update when they change. + * Listener that is attached to various properties that should trigger a + * view update when they change. */ private InvalidationListener updateListener = (Observable any) -> refresh(); /** - * task used to reload the content of this visualization + * Task used to reload the content of this view */ private Task updateTask; @@ -95,7 +95,7 @@ public abstract class AbstractTimeLineView extends BorderPane { /** * Handle a RefreshRequestedEvent from the events model by updating the - * visualization. + * view. * * @param event The RefreshRequestedEvent to handle. */ @@ -105,39 +105,38 @@ public abstract class AbstractTimeLineView extends BorderPane { } /** - * Does the visualization represent an out-of-date state of the DB. It might - * if, for example, tags have been updated but the vis. was not refreshed. + * Does the view represent an out-of-date state of the DB. It might if, for + * example, tags have been updated but the view was not refreshed. * - * @return True if the visualization does not represent the curent state of - * the DB. + * @return True if the view does not represent the current state of the DB. */ public boolean isOutOfDate() { return outOfDate.get(); } /** - * Get a ReadOnlyBooleanProperty that holds true if this visualization does - * not represent the current state of the DB> + * Get a ReadOnlyBooleanProperty that holds true if this view does not + * represent the current state of the DB> * * @return A ReadOnlyBooleanProperty that holds the out-of-date state for - * this visualization. + * this view. */ public ReadOnlyBooleanProperty outOfDateProperty() { return outOfDate.getReadOnlyProperty(); } /** - * Get the TimelineController for this visualization. + * Get the TimelineController for this view. * - * @return The TimelineController for this visualization. + * @return The TimelineController for this view. */ protected TimeLineController getController() { return controller; } /** - * Refresh this visualization based on current state of zoom / filters. - * Primarily this invokes the background VisualizationUpdateTask returned by + * Refresh this view based on current state of zoom / filters. Primarily + * this invokes the background VisualizationUpdateTask returned by * getUpdateTask(), which derived classes must implement. * * TODO: replace this logic with a javafx Service ? -jm @@ -160,7 +159,7 @@ public abstract class AbstractTimeLineView extends BorderPane { try { this.hasVisibleEvents.set(updateTask.get()); } catch (InterruptedException | ExecutionException ex) { - LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS + LOGGER.log(Level.SEVERE, "Unexpected exception updating view", ex); //NON-NLS } break; } @@ -169,9 +168,9 @@ public abstract class AbstractTimeLineView extends BorderPane { } /** - * Get the FilteredEventsModel for this visualization. + * Get the FilteredEventsModel for this view. * - * @return The FilteredEventsModel for this visualization. + * @return The FilteredEventsModel for this view. */ protected FilteredEventsModel getEventsModel() { return filteredEvents; @@ -179,16 +178,16 @@ public abstract class AbstractTimeLineView extends BorderPane { /** * Get a new background Task that fetches the appropriate data and loads it - * into this visualization. + * into this view. * - * @return A new task to execute on a background thread to reload this - * visualization with different data. + * @return A new task to execute on a background thread to reload this view + * with different data. */ protected abstract Task getNewUpdateTask(); /** - * Get a List of nodes containing settings widgets to insert into this - * visualization's header. + * Get a List of Nodes containing settings widgets to insert into this + * view's header. * * @return The List of settings Nodes. */ @@ -197,19 +196,19 @@ public abstract class AbstractTimeLineView extends BorderPane { } /** - * Set the List of nodes containing settings widgets to insert into this - * visualization's header. + * Set the List of Nodes containing settings widgets to insert into this + * view's header. * * - * @param settingsNodes The List of nodes containing settings widgets to - * insert into this visualization's header. + * @param settingsNodes The List of Nodes containing settings widgets to + * insert into this view's header. */ final protected void setSettingsNodes(List settingsNodes) { this.settingsNodes = new ArrayList<>(settingsNodes); } /** - * Dispose of this visualization and any resources it holds onto. + * Dispose of this view and any resources it holds onto. */ final synchronized void dispose() { //cancel and gc updateTask @@ -225,11 +224,11 @@ public abstract class AbstractTimeLineView extends BorderPane { } /** - * Are there are any events visible in this visualization with the current - * view parameters? + * Are there are any events visible in this view with the current view + * parameters? * - * @return True if there are events visible in this visualization with the - * current view parameters. + * @return True if there are events visible in this view with the current + * view parameters. */ boolean hasVisibleEvents() { return hasVisibleEventsProperty().get(); @@ -237,18 +236,18 @@ public abstract class AbstractTimeLineView extends BorderPane { /** * A property that indicates whether there are any events visible in this - * visualization with the current view parameters. + * view with the current view parameters. * * @return A property that indicates whether there are any events visible in - * this visualization with the current view parameters. + * this view with the current view parameters. */ ReadOnlyBooleanProperty hasVisibleEventsProperty() { return hasVisibleEvents.getReadOnlyProperty(); } /** - * Set this visualization out of date because, for example, tags have been - * updated but the vis. was not refreshed. + * Set this view out of date because, for example, tags have been updated + * but the view was not refreshed. */ void setOutOfDate() { outOfDate.set(true); @@ -284,8 +283,8 @@ public abstract class AbstractTimeLineView extends BorderPane { /** * Sets initial progress value and message and shows blocking progress - * indicator over the visualization. Derived Tasks should be sure to - * call this as part of their call() implementation. + * indicator over the view. Derived Tasks should be sure to call this as + * part of their call() implementation. * * @return True * diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java index 17209b9a4b..fc2ee5ca63 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java @@ -21,6 +21,8 @@ package org.sleuthkit.autopsy.timeline.ui; import java.util.Comparator; import java.util.HashMap; import java.util.Map; +import javafx.application.Platform; +import javafx.beans.binding.DoubleBinding; import javafx.collections.FXCollections; import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; @@ -37,8 +39,10 @@ import javafx.scene.layout.BorderStroke; import javafx.scene.layout.BorderStrokeStyle; import javafx.scene.layout.BorderWidths; import javafx.scene.layout.CornerRadii; +import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; import javafx.scene.paint.Color; import javafx.scene.text.Font; import javafx.scene.text.FontWeight; @@ -252,6 +256,18 @@ public abstract class AbstractTimelineChart { + VBox vBox = new VBox(getSpecificLabelPane(), getContextLabelPane()); + vBox.setFillWidth(false); + HBox hBox = new HBox(getSpacer(), vBox); + hBox.setFillHeight(false); + setBottom(hBox); + DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); + getSpacer().minWidthProperty().bind(spacerSize); + getSpacer().prefWidthProperty().bind(spacerSize); + getSpacer().maxWidthProperty().bind(spacerSize); + }); + createSeries(); selectedNodes.addListener((ListChangeListener.Change change) -> { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java index c5d9ddb6a4..28d6b72d1a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/countsview/CountsViewPane.java @@ -25,7 +25,6 @@ import java.util.function.Function; import javafx.application.Platform; import javafx.beans.Observable; import javafx.beans.binding.BooleanBinding; -import javafx.beans.binding.DoubleBinding; import javafx.beans.property.SimpleObjectProperty; import javafx.collections.FXCollections; import javafx.concurrent.Task; @@ -45,7 +44,6 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; -import javafx.scene.layout.VBox; import javafx.scene.text.Font; import javafx.scene.text.FontPosture; import javafx.scene.text.FontWeight; @@ -114,17 +112,7 @@ public class CountsViewPane extends AbstractTimelineChart { - VBox vBox = new VBox(getSpecificLabelPane(), getContextLabelPane()); - vBox.setFillWidth(false); - HBox hBox = new HBox(getSpacer(), vBox); - hBox.setFillHeight(false); - setBottom(hBox); - DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); - getSpacer().minWidthProperty().bind(spacerSize); - getSpacer().prefWidthProperty().bind(spacerSize); - getSpacer().maxWidthProperty().bind(spacerSize); - }); + setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes())); getChart().setData(dataSeries); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java index c3998e7cda..0a0b71837d 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailViewPane.java @@ -26,7 +26,6 @@ import java.util.stream.Collectors; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; -import javafx.beans.binding.DoubleBinding; import javafx.collections.ObservableList; import javafx.concurrent.Task; import javafx.fxml.FXML; @@ -42,7 +41,6 @@ import javafx.scene.control.Slider; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; import javafx.stage.Modality; import org.apache.commons.lang3.StringUtils; import org.controlsfx.control.action.Action; @@ -110,18 +108,6 @@ public class DetailViewPane extends AbstractTimelineChart { - VBox vBox = new VBox(getSpecificLabelPane(), getContextLabelPane()); - vBox.setFillWidth(false); - HBox hBox = new HBox(getSpacer(), vBox); - hBox.setFillHeight(false); - setBottom(hBox); - DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin()); - getSpacer().minWidthProperty().bind(spacerSize); - getSpacer().prefWidthProperty().bind(spacerSize); - getSpacer().maxWidthProperty().bind(spacerSize); - }); - this.selectedEvents = new MappedList<>(getSelectedNodes(), EventNodeBase::getEvent); //initialize chart; diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChart.java index 33153227f5..a2fac7feb9 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/DetailsChart.java @@ -32,7 +32,6 @@ import javafx.scene.control.ContextMenu; import javafx.scene.control.Control; import javafx.scene.control.Skin; import javafx.scene.control.SkinBase; -import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.MouseEvent; @@ -217,10 +216,6 @@ final class DetailsChart extends Control implements TimeLineChart { return nestedEvents; } - Tooltip getDefaultTooltip() { - throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates. - } - /** * Clear any time based UI elements (GuideLines, IntervalSelector,...) from * this chart. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java index 67e4de1c05..1067ed291a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/GuideLine.java @@ -25,6 +25,7 @@ import javafx.scene.shape.Line; import org.joda.time.DateTime; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.TimeLineController; +import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; /** * Subclass of {@link Line} with appropriate behavior (mouse listeners) to act @@ -34,7 +35,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; "GuideLine.tooltip.text={0}\nRight-click to remove.\nDrag to reposition."}) class GuideLine extends Line { - private final Tooltip CHART_DEFAULT_TOOLTIP ; + private final Tooltip CHART_DEFAULT_TOOLTIP = AbstractTimelineChart.getDefaultTooltip(); private final Tooltip tooltip = new Tooltip(); private final DetailsChart chart; @@ -48,7 +49,6 @@ class GuideLine extends Line { */ GuideLine(DetailsChart chart) { super(0, 0, 0, 0); - CHART_DEFAULT_TOOLTIP = chart.getDefaultTooltip(); this.chart = chart; Axis xAxis = chart.getXAxis(); endYProperty().bind(chart.heightProperty().subtract(xAxis.heightProperty().subtract(xAxis.tickLengthProperty()))); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java index c1db261b2f..ef54ca9373 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java @@ -41,7 +41,6 @@ import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.ViewMode; import org.sleuthkit.autopsy.timeline.actions.ResetFilters; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.filters.AbstractFilter; @@ -129,11 +128,13 @@ final public class FilterSetPanel extends BorderPane { hiddenDescriptionsListView.setItems(controller.getQuickHideFilters()); hiddenDescriptionsListView.setCellFactory(listView -> getNewDiscriptionFilterListCell()); + //show and hide the "hidden descriptions" panel depending on the current view mode controller.viewModeProperty().addListener(observable -> { applyFilters(); switch (controller.getViewMode()) { case COUNTS: case LIST: + //hide for counts and lists, but remember divider position dividerPosition = splitPane.getDividerPositions()[0]; splitPane.setDividerPositions(1); hiddenDescriptionsPane.setExpanded(false); @@ -141,6 +142,7 @@ final public class FilterSetPanel extends BorderPane { hiddenDescriptionsPane.setDisable(true); break; case DETAIL: + //show and restore divider position. splitPane.setDividerPositions(dividerPosition); hiddenDescriptionsPane.setDisable(false); hiddenDescriptionsPane.setCollapsible(true); From cb9c162ea440a27dfb0ee4689fc80592fa042610 Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 16:34:59 -0400 Subject: [PATCH 12/92] drive ContentViewer from List view. --- .../timeline/TimeLineTopComponent.java | 25 +++++++++- .../timeline/explorernodes/EventNode.java | 50 +++++++++++++++++-- .../timeline/explorernodes/EventRootNode.java | 33 +----------- .../timeline/ui/TimeLineResultView.java | 8 +-- .../timeline/ui/listvew/ListChart.java | 10 ++++ .../timeline/ui/listvew/ListViewPane.java | 6 +++ 6 files changed, 88 insertions(+), 44 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index aca7219998..8470175a0c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-15 Basis Technology Corp. + * Copyright 2013-16 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,10 +18,13 @@ */ package org.sleuthkit.autopsy.timeline; +import com.google.common.collect.Iterables; import java.awt.BorderLayout; import java.util.Collections; import java.util.List; import javafx.application.Platform; +import javafx.beans.InvalidationListener; +import javafx.beans.Observable; import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; 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.timeline.actions.Back; 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.StatusBar; 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; /** - * TopComponent for the timeline feature. + * TopComponent for the Timeline feature. */ @TopComponent.Description( preferredID = "TimeLineTopComponent", @@ -75,6 +79,21 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer 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) { initComponents(); this.controller = controller; @@ -108,6 +127,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer lowerSplitXPane.add(contentViewerContainerPanel); } }); + controller.getSelectedEventIDs().removeListener(selectionListener); break; case LIST: /* @@ -119,6 +139,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer splitYPane.add(contentViewerContainerPanel); dataResultPanel.setNode(null); }); + controller.getSelectedEventIDs().addListener(selectionListener); break; default: throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java index abb7245eda..9ea0b37e39 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014 Basis Technology Corp. + * Copyright 2014-16 Basis Technology Corp. * Contact: carrier sleuthkit org * * 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.Sheet; import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DataModelActionsFactory; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.datamodel.NodeProperty; import org.sleuthkit.autopsy.timeline.TimeLineController; +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.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()); @@ -155,10 +159,48 @@ class EventNode extends DisplayableItemNode { } @Override - public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + final public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { String oldValue = getValue(); value = t; 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; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java index 6fd3693514..7438a0f802 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java @@ -20,23 +20,16 @@ package org.sleuthkit.autopsy.timeline.explorernodes; import java.util.Collection; import java.util.List; -import java.util.logging.Level; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; 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. @@ -132,31 +125,7 @@ public class EventRootNode extends DisplayableItemNode { */ return new TooManyNode(eventIDs.size()); } else { - /* - * 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; - } + return EventNode.createEventNode(eventID, filteredEvents); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java index f61e2b5b28..9fb308229f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java @@ -62,13 +62,9 @@ public class TimeLineResultView { dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, dataContent); //set up listeners on relevant properties - TimeLineController.getTimeZone().addListener((Observable observable) -> { - dataResultPanel.setPath(getSummaryString()); - }); + TimeLineController.getTimeZone().addListener(timeZone -> dataResultPanel.setPath(getSummaryString())); - controller.getSelectedEventIDs().addListener((Observable o) -> { - refresh(); - }); + controller.getSelectedEventIDs().addListener((Observable selectedIDs) -> refresh()); refresh(); } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index 6f01fbbec0..e9b5f12d1a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -22,6 +22,7 @@ import java.util.Collection; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; +import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TableCell; @@ -128,6 +129,15 @@ class ListChart extends BorderPane { 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 getSelectedEventIDs() { + return table.getSelectionModel().getSelectedItems(); + } + private class ImageCell extends EventTableCell { @Override 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 6f3fdec60f..574de0fcfe 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.List; import javafx.application.Platform; +import javafx.beans.Observable; import javafx.concurrent.Task; import javafx.scene.Parent; import org.joda.time.Interval; @@ -58,6 +59,11 @@ public class ListViewPane extends AbstractTimeLineView { //initialize chart; setCenter(listChart); 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 From 716f8c0b5d74a322b3c5b475745917987f263a4e Mon Sep 17 00:00:00 2001 From: jmillman Date: Tue, 17 May 2016 16:57:21 -0400 Subject: [PATCH 13/92] begin work on enabling actions. --- .../timeline/SwingMenuItemAdapter.java | 85 ++++++++++++++ .../timeline/ui/listvew/ListChart.java | 111 ++++++++++-------- .../actions/SwingMenuItemAdapter.java | 5 +- 3 files changed, 146 insertions(+), 55 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java new file mode 100644 index 0000000000..aee3b216a7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java @@ -0,0 +1,85 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2013-14 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline; + +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.MenuElement; + +//TODO: move this into CoreUtils? -jm +public class SwingMenuItemAdapter extends MenuItem { + + JMenuItem jMenuItem; + + SwingMenuItemAdapter(final JMenuItem jMenuItem) { + super(jMenuItem.getText()); + this.jMenuItem = jMenuItem; + setOnAction(actionEvent -> jMenuItem.doClick()); + } + + public static MenuItem create(MenuElement jmenuItem) { + if (jmenuItem instanceof JMenu) { + return new SwingMenuAdapter((JMenu) jmenuItem); + } else if (jmenuItem instanceof JPopupMenu) { + return new SwingMenuAdapter((JPopupMenu) jmenuItem); + } else { + return new SwingMenuItemAdapter((JMenuItem) jmenuItem); + } + + } +} + +class SwingMenuAdapter extends Menu { + + private final MenuElement jMenu; + + SwingMenuAdapter(final JMenu jMenu) { + super(jMenu.getText()); + this.jMenu = jMenu; + buildChildren(jMenu); + + } + + SwingMenuAdapter(JPopupMenu jPopupMenu) { + super(jPopupMenu.getLabel()); + this.jMenu = jPopupMenu; + + buildChildren(jMenu); + } + + private void buildChildren(MenuElement jMenu) { + + for (MenuElement menuE : jMenu.getSubElements()) { + if (menuE instanceof JMenu) { + getItems().add(SwingMenuItemAdapter.create((JMenu) menuE)); + } else if (menuE instanceof JMenuItem) { + getItems().add(SwingMenuItemAdapter.create((JMenuItem) menuE)); + } else if (menuE instanceof JPopupMenu) { + buildChildren(menuE); + } else { + + System.out.println(menuE.toString()); +// throw new UnsupportedOperationException(); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java index e9b5f12d1a..f26c1367ba 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java @@ -24,7 +24,9 @@ import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; @@ -34,51 +36,53 @@ import javafx.scene.layout.BorderPane; import javafx.util.Callback; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; +import org.sleuthkit.autopsy.timeline.SwingMenuItemAdapter; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; +import org.sleuthkit.autopsy.timeline.explorernodes.EventNode; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; /** * */ class ListChart extends BorderPane { - + @FXML private Label eventCountLabel; - + @FXML private TableView table; - + private static final Callback, ObservableValue> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue()); - + private final TimeLineController controller; - + @FXML private TableColumn idColumn; - + @FXML private TableColumn millisColumn; - + @FXML private TableColumn iconColumn; - + @FXML private TableColumn descriptionColumn; - + @FXML private TableColumn baseTypeColumn; - + @FXML private TableColumn subTypeColumn; - + @FXML private TableColumn knownColumn; - + ListChart(TimeLineController controller) { this.controller = controller; FXMLConstructor.construct(this, ListChart.class, "ListViewChart.fxml"); } - + @FXML void initialize() { assert eventCountLabel != null : "fx:id=\"eventCountLabel\" was not injected: check your FXML file 'ListViewPane.fxml'."; @@ -90,40 +94,40 @@ class ListChart extends BorderPane { assert baseTypeColumn != null : "fx:id=\"baseTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; - + table.setRowFactory(tableView -> new EventRow()); idColumn.setCellValueFactory(CELL_VALUE_FACTORY); - + millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); millisColumn.setCellFactory(col -> new EpochMillisCell()); - + iconColumn.setCellValueFactory(CELL_VALUE_FACTORY); iconColumn.setCellFactory(col -> new ImageCell()); - + descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY); descriptionColumn.setCellFactory(col -> new DescriptionCell()); - + baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); - + subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); subTypeColumn.setCellFactory(col -> new EventTypeCell()); - + knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); knownColumn.setCellFactory(col -> new KnownCell()); - + eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } - + public TimeLineController getController() { return controller; } - + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void clear() { table.getItems().clear(); } - + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void setEventIDs(Collection eventIDs) { table.getItems().setAll(eventIDs); @@ -137,40 +141,45 @@ class ListChart extends BorderPane { ObservableList getSelectedEventIDs() { return table.getSelectionModel().getSelectedItems(); } - + private class ImageCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); if (empty || item == null) { setGraphic(null); + setContextMenu(null); } else { setGraphic(new ImageView(getEvent().getEventType().getFXImage())); + EventNode node = EventNode.createEventNode(item, controller.getEventsModel()); + setContextMenu(new ContextMenu(SwingMenuItemAdapter.create(node.getContextMenu()))); } + } } - + private class DescriptionCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { setText(getEvent().getDescription(DescriptionLoD.FULL)); } + setContextMenu(new ContextMenu(new MenuItem("sampleS"))); } } - + private class BaseTypeCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -178,13 +187,13 @@ class ListChart extends BorderPane { } } } - + private class EventTypeCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -192,13 +201,13 @@ class ListChart extends BorderPane { } } } - + private class KnownCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -206,19 +215,19 @@ class ListChart extends BorderPane { } } } - + private class EventTableCell extends TableCell { - + 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 { @@ -226,13 +235,13 @@ class ListChart extends BorderPane { } } } - + private class EpochMillisCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -240,19 +249,19 @@ class ListChart extends BorderPane { } } } - + private class EventRow extends TableRow { - + 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 { diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/SwingMenuItemAdapter.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/SwingMenuItemAdapter.java index 5f995433da..59f48dc094 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/SwingMenuItemAdapter.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/SwingMenuItemAdapter.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.imagegallery.actions; -import javafx.event.ActionEvent; import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; import javax.swing.JMenu; @@ -34,9 +33,7 @@ public class SwingMenuItemAdapter extends MenuItem { SwingMenuItemAdapter(final JMenuItem jMenuItem) { super(jMenuItem.getText()); this.jMenuItem = jMenuItem; - setOnAction((ActionEvent t) -> { - jMenuItem.doClick(); - }); + setOnAction(actionEvent -> jMenuItem.doClick()); } public static MenuItem create(MenuElement jmenuItem) { From b8d5eb2233bf990ba5c826a64e8a150b24fe8efe Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 18 May 2016 13:40:17 -0400 Subject: [PATCH 14/92] more work to enable explorer based actions --- .../timeline/explorernodes/ExplorerUtils.java | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java new file mode 100644 index 0000000000..1245f2d642 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java @@ -0,0 +1,31 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.timeline.explorernodes; + +import javafx.scene.Node; +import javafx.scene.Parent; +import org.openide.explorer.ExplorerManager; + +/** + * + */ +public class ExplorerUtils { + + static public ExplorerManager find(Node node) { + + if (node instanceof ExplorerManager.Provider) { + return ((ExplorerManager.Provider) node).getExplorerManager(); + } else { + Parent parent = node.getParent(); + if (parent == null) { + System.out.println(node.getScene().getWindow().getClass()); + return null; + } else { + return find(parent); + } + } + } +} From f7e7404ec4d9c06d832daf6cb307b059326d299c Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 18 May 2016 14:29:27 -0400 Subject: [PATCH 15/92] address review comments: fix typos, rename variables, catch exceptions higher up. --- .../timeline/TimeLineTopComponent.java | 38 ++++++++++++++----- .../timeline/explorernodes/EventNode.java | 32 ++++++---------- .../timeline/explorernodes/EventRootNode.java | 18 ++++++++- 3 files changed, 56 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 8470175a0c..6ae2e1dcf2 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -22,6 +22,7 @@ import com.google.common.collect.Iterables; import java.awt.BorderLayout; import java.util.Collections; import java.util.List; +import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; @@ -37,6 +38,7 @@ import javafx.scene.input.KeyEvent; import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javax.swing.SwingUtilities; +import org.controlsfx.control.Notifications; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.util.NbBundle; @@ -58,6 +60,7 @@ import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel; import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane; +import org.sleuthkit.datamodel.TskCoreException; /** * TopComponent for the Timeline feature. @@ -71,7 +74,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private static final Logger LOGGER = Logger.getLogger(TimeLineTopComponent.class.getName()); - private final DataContentPanel dataContentPanel; + private final DataContentPanel contentViewerPanel; private final TimeLineResultView tlResultView; @@ -79,17 +82,32 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private final TimeLineController controller; + @NbBundle.Messages({ + "TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."}) /** * Listener that drives the ContentViewer when in List ViewMode. */ - private final InvalidationListener selectionListener = new InvalidationListener() { + private final InvalidationListener selectedEventListener = 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)); + try { + EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel()); + SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(eventNode)); + } catch (IllegalStateException ex) { + //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. + LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + Platform.runLater(() -> { + Notifications.create() + .owner(jFXVizPanel.getScene().getWindow()) + .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg()) + .showError(); + }); + } } else { - SwingUtilities.invokeLater(() -> dataContentPanel.setNode(null)); + SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(null)); } } }; @@ -103,9 +121,9 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer setToolTipText(NbBundle.getMessage(TimeLineTopComponent.class, "HINT_TimeLineTopComponent")); setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application - dataContentPanel = DataContentPanel.createInstance(); - this.contentViewerContainerPanel.add(dataContentPanel, BorderLayout.CENTER); - tlResultView = new TimeLineResultView(controller, dataContentPanel); + contentViewerPanel = DataContentPanel.createInstance(); + this.contentViewerContainerPanel.add(contentViewerPanel, BorderLayout.CENTER); + tlResultView = new TimeLineResultView(controller, contentViewerPanel); final DataResultPanel dataResultPanel = tlResultView.getDataResultPanel(); this.resultContainerPanel.add(dataResultPanel, BorderLayout.CENTER); dataResultPanel.open(); @@ -127,7 +145,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer lowerSplitXPane.add(contentViewerContainerPanel); } }); - controller.getSelectedEventIDs().removeListener(selectionListener); + controller.getSelectedEventIDs().removeListener(selectedEventListener); break; case LIST: /* @@ -139,7 +157,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer splitYPane.add(contentViewerContainerPanel); dataResultPanel.setNode(null); }); - controller.getSelectedEventIDs().addListener(selectionListener); + controller.getSelectedEventIDs().addListener(selectedEventListener); break; default: throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java index 9ea0b37e39..09ffec0ff0 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java @@ -167,7 +167,7 @@ public class EventNode extends DisplayableItemNode { } /** - * Factory method to create an EventNode form the event ID and the events + * Factory method to create an EventNode from the event ID and the events * model. * * @param eventID The ID of the event this node is for. @@ -176,31 +176,21 @@ public class EventNode extends DisplayableItemNode { * @return An EventNode with the file (and artifact) backing this event in * its lookup. */ - public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) { + public static EventNode createEventNode(final Long eventID, FilteredEventsModel eventsModel) throws TskCoreException, IllegalStateException { /* * 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; + + SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); + AbstractFile file = sleuthkitCase.getAbstractFileById(eventById.getFileID()); + + if (eventById.getArtifactID().isPresent()) { + BlackboardArtifact blackboardArtifact = sleuthkitCase.getBlackboardArtifact(eventById.getArtifactID().get()); + return new EventNode(eventById, file, blackboardArtifact); + } else { + return new EventNode(eventById, file); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java index 7438a0f802..eb84883808 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline.explorernodes; import java.util.Collection; import java.util.List; +import java.util.logging.Level; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -30,6 +31,7 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.DisplayableItemNode; import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; +import org.sleuthkit.datamodel.TskCoreException; /** * Root Explorer node to represent events. @@ -125,7 +127,21 @@ public class EventRootNode extends DisplayableItemNode { */ return new TooManyNode(eventIDs.size()); } else { - return EventNode.createEventNode(eventID, filteredEvents); + try { + return EventNode.createEventNode(eventID, filteredEvents); + } catch (IllegalStateException ex) { + //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. + LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + return null; + } catch (TskCoreException ex) { + /* + * Just log it: There might be lots of these errors, and we + * don't want to flood the user with notifications. It will + * be obvious the UI is broken anyways + */ + LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + return null; + } } } } From 87358d3ee1d5f643874204035e64a21518ede298 Mon Sep 17 00:00:00 2001 From: jmillman Date: Wed, 18 May 2016 17:17:03 -0400 Subject: [PATCH 16/92] closer to working actions. --- .../timeline/SwingMenuItemAdapter.java | 59 +++---- .../timeline/TimeLineTopComponent.java | 26 ++- .../{ListViewChart.fxml => ListTimeline.fxml} | 0 .../{ListChart.java => ListTimeline.java} | 165 ++++++++++++------ .../timeline/ui/listvew/ListViewPane.java | 4 +- 5 files changed, 159 insertions(+), 95 deletions(-) rename Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/{ListViewChart.fxml => ListTimeline.fxml} (100%) rename Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/{ListChart.java => ListTimeline.java} (74%) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java index aee3b216a7..a875057538 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java @@ -20,24 +20,25 @@ package org.sleuthkit.autopsy.timeline; import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.MenuElement; +import javax.swing.SwingUtilities; //TODO: move this into CoreUtils? -jm public class SwingMenuItemAdapter extends MenuItem { - JMenuItem jMenuItem; - SwingMenuItemAdapter(final JMenuItem jMenuItem) { super(jMenuItem.getText()); - this.jMenuItem = jMenuItem; - setOnAction(actionEvent -> jMenuItem.doClick()); + setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick)); } public static MenuItem create(MenuElement jmenuItem) { - if (jmenuItem instanceof JMenu) { + if (jmenuItem == null) { + return new SeparatorMenuItem(); + } else if (jmenuItem instanceof JMenu) { return new SwingMenuAdapter((JMenu) jmenuItem); } else if (jmenuItem instanceof JPopupMenu) { return new SwingMenuAdapter((JPopupMenu) jmenuItem); @@ -46,40 +47,34 @@ public class SwingMenuItemAdapter extends MenuItem { } } -} -class SwingMenuAdapter extends Menu { + private static class SwingMenuAdapter extends Menu { - private final MenuElement jMenu; + SwingMenuAdapter(final JMenu jMenu) { + super(jMenu.getText()); + buildChildren(jMenu); + } - SwingMenuAdapter(final JMenu jMenu) { - super(jMenu.getText()); - this.jMenu = jMenu; - buildChildren(jMenu); + SwingMenuAdapter(JPopupMenu jPopupMenu) { + super(jPopupMenu.getLabel()); + buildChildren(jPopupMenu); + } - } + private void buildChildren(MenuElement jMenu) { - SwingMenuAdapter(JPopupMenu jPopupMenu) { - super(jPopupMenu.getLabel()); - this.jMenu = jPopupMenu; - - buildChildren(jMenu); - } - - private void buildChildren(MenuElement jMenu) { - - for (MenuElement menuE : jMenu.getSubElements()) { - if (menuE instanceof JMenu) { - getItems().add(SwingMenuItemAdapter.create((JMenu) menuE)); - } else if (menuE instanceof JMenuItem) { - getItems().add(SwingMenuItemAdapter.create((JMenuItem) menuE)); - } else if (menuE instanceof JPopupMenu) { - buildChildren(menuE); - } else { - - System.out.println(menuE.toString()); + for (MenuElement menuE : jMenu.getSubElements()) { + if (menuE instanceof JMenu) { + getItems().add(SwingMenuItemAdapter.create((JMenu) menuE)); + } else if (menuE instanceof JMenuItem) { + getItems().add(SwingMenuItemAdapter.create((JMenuItem) menuE)); + } else if (menuE instanceof JPopupMenu) { + buildChildren(menuE); + } else { + System.out.println(menuE.toString()); // throw new UnsupportedOperationException(); + } } } } + } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 6ae2e1dcf2..2a37c6ef1b 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.collect.Iterables; import java.awt.BorderLayout; +import java.beans.PropertyVetoException; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -41,7 +42,11 @@ import javax.swing.SwingUtilities; import org.controlsfx.control.Notifications; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Node; import org.openide.util.NbBundle; +import org.openide.util.Utilities; import org.openide.windows.Mode; import org.openide.windows.TopComponent; import static org.openide.windows.TopComponent.PROP_UNDOCKING_DISABLED; @@ -60,6 +65,7 @@ import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel; import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane; +import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** @@ -79,7 +85,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private final TimeLineResultView tlResultView; private final ExplorerManager em = new ExplorerManager(); - private final TimeLineController controller; @NbBundle.Messages({ @@ -93,7 +98,23 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer if (controller.getSelectedEventIDs().size() == 1) { try { EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel()); - SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(eventNode)); + SwingUtilities.invokeLater(() -> { + + Node[] eventNodes = new Node[]{eventNode}; + Children.Array children = new Children.Array(); + children.add(eventNodes); + + em.setRootContext(new AbstractNode(children)); + try { + em.setSelectedNodes(eventNodes); + System.out.println(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + + } catch (PropertyVetoException ex) { + LOGGER.log(Level.SEVERE, "Explorer manager selection was vetoed.", ex); //NON-NLS + } + contentViewerPanel.setNode(eventNode); + }); + } catch (IllegalStateException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS @@ -116,7 +137,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer initComponents(); this.controller = controller; associateLookup(ExplorerUtils.createLookup(em, getActionMap())); - setName(NbBundle.getMessage(TimeLineTopComponent.class, "CTL_TimeLineTopComponent")); setToolTipText(NbBundle.getMessage(TimeLineTopComponent.class, "HINT_TimeLineTopComponent")); setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.fxml similarity index 100% rename from Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewChart.fxml rename to Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.fxml diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java similarity index 74% rename from Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java rename to Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java index f26c1367ba..b89970e66a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -18,7 +18,11 @@ */ package org.sleuthkit.autopsy.timeline.ui.listvew; +import java.util.ArrayList; import java.util.Collection; +import java.util.List; +import java.util.logging.Level; +import javafx.application.Platform; import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; @@ -34,6 +38,14 @@ import javafx.scene.control.TableView; import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.util.Callback; +import javax.swing.Action; +import javax.swing.MenuElement; +import org.controlsfx.control.Notifications; +import org.openide.util.Lookup; +import org.openide.util.NbBundle; +import org.openide.util.actions.Presenter; +import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.SwingMenuItemAdapter; @@ -41,48 +53,51 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.explorernodes.EventNode; import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; +import org.sleuthkit.datamodel.TskCoreException; /** * */ -class ListChart extends BorderPane { - +class ListTimeline extends BorderPane { + + private static final Logger LOGGER = Logger.getLogger(ListTimeline.class.getName()); + @FXML private Label eventCountLabel; - + @FXML private TableView table; - + private static final Callback, ObservableValue> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue()); - + private final TimeLineController controller; - + @FXML private TableColumn idColumn; - + @FXML private TableColumn millisColumn; - + @FXML private TableColumn iconColumn; - + @FXML private TableColumn descriptionColumn; - + @FXML private TableColumn baseTypeColumn; - + @FXML private TableColumn subTypeColumn; - + @FXML private TableColumn knownColumn; - - ListChart(TimeLineController controller) { + + ListTimeline(TimeLineController controller) { this.controller = controller; - FXMLConstructor.construct(this, ListChart.class, "ListViewChart.fxml"); + FXMLConstructor.construct(this, ListTimeline.class, "ListTimeline.fxml"); } - + @FXML void initialize() { assert eventCountLabel != null : "fx:id=\"eventCountLabel\" was not injected: check your FXML file 'ListViewPane.fxml'."; @@ -94,40 +109,40 @@ class ListChart extends BorderPane { assert baseTypeColumn != null : "fx:id=\"baseTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; - + table.setRowFactory(tableView -> new EventRow()); idColumn.setCellValueFactory(CELL_VALUE_FACTORY); - + millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); millisColumn.setCellFactory(col -> new EpochMillisCell()); - + iconColumn.setCellValueFactory(CELL_VALUE_FACTORY); iconColumn.setCellFactory(col -> new ImageCell()); - + descriptionColumn.setCellValueFactory(CELL_VALUE_FACTORY); descriptionColumn.setCellFactory(col -> new DescriptionCell()); - + baseTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); baseTypeColumn.setCellFactory(col -> new BaseTypeCell()); - + subTypeColumn.setCellValueFactory(CELL_VALUE_FACTORY); subTypeColumn.setCellFactory(col -> new EventTypeCell()); - + knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); knownColumn.setCellFactory(col -> new KnownCell()); - + eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } - + public TimeLineController getController() { return controller; } - + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void clear() { table.getItems().clear(); } - + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void setEventIDs(Collection eventIDs) { table.getItems().setAll(eventIDs); @@ -141,9 +156,9 @@ class ListChart extends BorderPane { ObservableList getSelectedEventIDs() { return table.getSelectionModel().getSelectedItems(); } - + private class ImageCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); @@ -152,34 +167,32 @@ class ListChart extends BorderPane { setContextMenu(null); } else { setGraphic(new ImageView(getEvent().getEventType().getFXImage())); - EventNode node = EventNode.createEventNode(item, controller.getEventsModel()); - setContextMenu(new ContextMenu(SwingMenuItemAdapter.create(node.getContextMenu()))); + } - + } } - + private class DescriptionCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { setText(getEvent().getDescription(DescriptionLoD.FULL)); } - setContextMenu(new ContextMenu(new MenuItem("sampleS"))); } } - + private class BaseTypeCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -187,13 +200,13 @@ class ListChart extends BorderPane { } } } - + private class EventTypeCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -201,13 +214,13 @@ class ListChart extends BorderPane { } } } - + private class KnownCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -215,19 +228,19 @@ class ListChart extends BorderPane { } } } - + private class EventTableCell extends TableCell { - + 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 { @@ -235,13 +248,13 @@ class ListChart extends BorderPane { } } } - + private class EpochMillisCell extends EventTableCell { - + @Override protected void updateItem(Long item, boolean empty) { super.updateItem(item, empty); - + if (empty || item == null) { setText(""); } else { @@ -249,23 +262,59 @@ class ListChart extends BorderPane { } } } - + private class EventRow extends TableRow { - + private SingleEvent event; - + SingleEvent getEvent() { return event; } - + + @NbBundle.Messages({ + "ListChart.errorMsg=There was a problem getting the content for the selected 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); + try { + EventNode node = EventNode.createEventNode(item, controller.getEventsModel()); + + List menuItems = new ArrayList<>(); + + for (MenuElement element : node.getContextMenu().getSubElements()) { + menuItems.add(SwingMenuItemAdapter.create(element)); + }; + + Collection menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class); + + for (ContextMenuActionsProvider provider : menuProviders) { + for (final Action action : provider.getActions()) { + if (action instanceof Presenter.Popup) { + Presenter.Popup popUpPresenter = (Presenter.Popup) action; + menuItems.add(SwingMenuItemAdapter.create(popUpPresenter.getPopupPresenter())); + } + } + } + + setContextMenu(new ContextMenu(menuItems.toArray(new MenuItem[menuItems.size()]))); + + } catch (IllegalStateException ex) { + //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. + LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + Platform.runLater(() -> { + Notifications.create() + .owner(getScene().getWindow()) + .text(Bundle.ListChart_errorMsg()) + .showError(); + }); + } } } } 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 574de0fcfe..23278f0d86 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListViewPane.java @@ -45,7 +45,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView; */ public class ListViewPane extends AbstractTimeLineView { - private final ListChart listChart; + private final ListTimeline listChart; /** * Constructor @@ -54,7 +54,7 @@ public class ListViewPane extends AbstractTimeLineView { */ public ListViewPane(TimeLineController controller) { super(controller); - listChart = new ListChart(controller); + listChart = new ListTimeline(controller); //initialize chart; setCenter(listChart); From 3dd157de1b02a2245af2cae205a420ef232359df Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 19 May 2016 11:45:53 -0400 Subject: [PATCH 17/92] build context menus directly from Actions rather than from the swing/netbeans contenxtmenu --- .../timeline/SwingMenuItemAdapter.java | 9 +++-- .../timeline/ui/listvew/ListTimeline.java | 35 ++++++++++--------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java index a875057538..c513c76830 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java @@ -63,15 +63,14 @@ public class SwingMenuItemAdapter extends MenuItem { private void buildChildren(MenuElement jMenu) { for (MenuElement menuE : jMenu.getSubElements()) { - if (menuE instanceof JMenu) { - getItems().add(SwingMenuItemAdapter.create((JMenu) menuE)); + if (menuE == null) { + getItems().add(new SeparatorMenuItem()); } else if (menuE instanceof JMenuItem) { - getItems().add(SwingMenuItemAdapter.create((JMenuItem) menuE)); + getItems().add(SwingMenuItemAdapter.create(menuE)); } else if (menuE instanceof JPopupMenu) { buildChildren(menuE); } else { - System.out.println(menuE.toString()); -// throw new UnsupportedOperationException(); + throw new UnsupportedOperationException(); } } } 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 b89970e66a..1f96d9cd49 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.timeline.ui.listvew; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.logging.Level; @@ -31,6 +32,7 @@ import javafx.fxml.FXML; import javafx.scene.control.ContextMenu; import javafx.scene.control.Label; import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; import javafx.scene.control.TableCell; import javafx.scene.control.TableColumn; import javafx.scene.control.TableRow; @@ -39,12 +41,11 @@ import javafx.scene.image.ImageView; import javafx.scene.layout.BorderPane; import javafx.util.Callback; import javax.swing.Action; -import javax.swing.MenuElement; +import javax.swing.JMenuItem; import org.controlsfx.control.Notifications; -import org.openide.util.Lookup; +import org.openide.awt.Actions; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; -import org.sleuthkit.autopsy.corecomponentinterfaces.ContextMenuActionsProvider; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; @@ -167,9 +168,7 @@ class ListTimeline extends BorderPane { setContextMenu(null); } else { setGraphic(new ImageView(getEvent().getEventType().getFXImage())); - } - } } @@ -281,28 +280,30 @@ class ListTimeline extends BorderPane { event = null; } else { event = controller.getEventsModel().getEventById(item); + try { EventNode node = EventNode.createEventNode(item, controller.getEventsModel()); - List menuItems = new ArrayList<>(); - for (MenuElement element : node.getContextMenu().getSubElements()) { - menuItems.add(SwingMenuItemAdapter.create(element)); - }; + for (Action element : node.getActions(false)) { + if (element == null) { + menuItems.add(new SeparatorMenuItem()); + } else { + String actionName = element.getValue(Action.NAME).toString(); - Collection menuProviders = Lookup.getDefault().lookupAll(ContextMenuActionsProvider.class); + if (Arrays.asList("&Properties", "Tools").contains(actionName) == false) { - for (ContextMenuActionsProvider provider : menuProviders) { - for (final Action action : provider.getActions()) { - if (action instanceof Presenter.Popup) { - Presenter.Popup popUpPresenter = (Presenter.Popup) action; - menuItems.add(SwingMenuItemAdapter.create(popUpPresenter.getPopupPresenter())); + if (element instanceof Presenter.Popup) { + JMenuItem submenu = ((Presenter.Popup) element).getPopupPresenter(); + menuItems.add(SwingMenuItemAdapter.create(submenu)); + } else { + menuItems.add(SwingMenuItemAdapter.create(new Actions.MenuItem(element, false))); + } } } - } + }; setContextMenu(new ContextMenu(menuItems.toArray(new MenuItem[menuItems.size()]))); - } catch (IllegalStateException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS From b5edf4d48607d48024da05367b14e73dd65da82c Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 19 May 2016 16:22:22 -0400 Subject: [PATCH 18/92] actions that depend on actionsGlobalContext work. --- .../autopsy/timeline/Bundle.properties | 2 +- .../autopsy/timeline/Bundle_ja.properties | 89 +++---- .../timeline/TimeLineTopComponent.form | 40 +-- .../timeline/TimeLineTopComponent.java | 231 ++++++++++++------ .../autopsy/timeline/ui/Bundle.properties | 1 - .../autopsy/timeline/ui/Bundle_ja.properties | 2 +- .../timeline/ui/TimeLineResultView.java | 107 -------- .../timeline/ui/listvew/ListTimeline.java | 4 +- 8 files changed, 232 insertions(+), 244 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties index d23cfbb773..b417dc2072 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties @@ -24,4 +24,4 @@ TimelinePanel.jButton7.text=3d TimelinePanel.jButton2.text=1m TimelinePanel.jButton3.text=3m TimelinePanel.jButton4.text=2w -ProgressWindow.progressHeader.text=\ \ No newline at end of file +ProgressWindow.progressHeader.text=\ diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties index ba1ee4f94a..3bd24f9ef1 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties @@ -1,48 +1,49 @@ -CTL_MakeTimeline=\u300C\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u300D -CTL_TimeLineTopComponent=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30A6\u30A3\u30F3\u30C9\u30A6 -CTL_TimeLineTopComponentAction=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30C8\u30C3\u30D7\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8 -HINT_TimeLineTopComponent=\u3053\u308C\u306F\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30A6\u30A3\u30F3\u30C9\u30A6\u3067\u3059 -OpenTimelineAction.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3 -Timeline.frameName.text={0} - Autopsy\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3 -Timeline.goToButton.text=\u4E0B\u8A18\u3078\u79FB\u52D5\uFF1A -Timeline.node.root=\u30EB\u30FC\u30C8 -Timeline.pushDescrLOD.confdlg.msg={0}\u30A4\u30D9\u30F3\u30C8\u306E\u8A73\u7D30\u304C\u8868\u793A\u53EF\u80FD\u3067\u3059\u3002\u3053\u306E\u51E6\u7406\u306F\u9577\u6642\u9593\u304B\u304B\u308B\u3082\u3057\u304F\u306FAutopsy\u3092\u30AF\u30E9\u30C3\u30B7\u30E5\u3059\u308B\u53EF\u80FD\u6027\u304C\u3042\u308A\u307E\u3059\u3002\n\n\u5B9F\u884C\u3057\u307E\u3059\u304B\uFF1F -Timeline.resultPanel.loading=\u30ED\u30FC\u30C9\u4E2D\u30FB\u30FB\u30FB -Timeline.resultsPanel.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u7D50\u679C -Timeline.runJavaFxThread.progress.creating=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u4F5C\u6210\u4E2D\u30FB\u30FB\u30FB -Timeline.zoomOutButton.text=\u30BA\u30FC\u30E0\u30A2\u30A6\u30C8 -TimelineFrame.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3 -TimeLineTopComponent.eventsTab.name=\u30A4\u30D9\u30F3\u30C8 -TimeLineTopComponent.filterTab.name=\u30D5\u30A3\u30EB\u30BF\u30FC -OpenTimeLineAction.msgdlg.text=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u4F5C\u6210\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002\u30C7\u30FC\u30BF\u30BD\u30FC\u30B9\u304C\u3042\u308A\u307E\u305B\u3093\u3002 -PrompDialogManager.buttonType.continueNoUpdate=\u66F4\u65B0\u305B\u305A\u6B21\u3078 -PrompDialogManager.buttonType.showTimeline=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u8868\u793A -PrompDialogManager.buttonType.update=\u66F4\u65B0 -PromptDialogManager.confirmDuringIngest.contentText=\u6B21\u3078\u9032\u307F\u307E\u3059\u304B\uFF1F -PromptDialogManager.confirmDuringIngest.headerText=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u304C\u5B8C\u4E86\u3059\u308B\u524D\u306B\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u3092\u8868\u793A\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059\u3002\n\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u304C\u5B8C\u6210\u3057\u3066\u3044\u306A\u3044\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002 -PromptDialogManager.progressDialog.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30C7\u30FC\u30BF\u3092\u5165\u529B\u4E2D -PromptDialogManager.rebuildPrompt.details=\u8A73\u7D30\uFF1A -PromptDialogManager.rebuildPrompt.headerText=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u304C\u4E0D\u5B8C\u5168\u307E\u305F\u306F\u6700\u65B0\u3067\u306F\u306A\u3044\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\n \u6B20\u843D\u3057\u3066\u3044\u308B\u307E\u305F\u306F\u4E0D\u6B63\u78BA\u306A\u30A4\u30D9\u30F3\u30C8\u304C\u4E00\u90E8\u3042\u308B\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002\u4E00\u90E8\u306E\u6A5F\u80FD\u304C\u5229\u7528\u3067\u304D\u306A\u3044\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002 -Timeline.confirmation.dialogs.title=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u66F4\u65B0\u3057\u307E\u3059\u304B\uFF1F -Timeline.pushDescrLOD.confdlg.title=\u8AAC\u660E\u306E\u8A18\u8FF0\u30EC\u30D9\u30EB\u3092\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F -TimeLineController.errorTitle=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30A8\u30E9\u30FC -TimeLineController.outOfDate.errorMessage=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u304C\u6700\u65B0\u304B\u78BA\u8A8D\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002\u66F4\u65B0\u304C\u5FC5\u8981\u3060\u3068\u60F3\u5B9A\u3057\u307E\u3059\u3002 -TimeLineController.rebuildReasons.incompleteOldSchema=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30A4\u30D9\u30F3\u30C8\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306B\u4E0D\u5B8C\u5168\u306A\u60C5\u5831\u304C\u4EE5\u524D\u5165\u529B\u3055\u308C\u3066\u3044\u307E\u3057\u305F\uFF1A\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u66F4\u65B0\u3057\u306A\u3044\u3068\u3001\u4E00\u90E8\u306E\u6A5F\u80FD\u304C\u5229\u7528\u3067\u304D\u306A\u3044\u3001\u307E\u305F\u306F\u6A5F\u80FD\u3057\u306A\u3044\u304B\u3082\u3057\u308C\u306A\u3044\u3067\u3059\u3002 -TimeLineController.rebuildReasons.ingestWasRunning=\u30A4\u30F3\u30B8\u30A7\u30B9\u30C8\u304C\u5B9F\u884C\u4E2D\u306B\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30A4\u30D9\u30F3\u30C8\u306E\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u306B\u60C5\u5831\u304C\u5165\u529B\u3055\u308C\u3066\u3044\u307E\u3057\u305F\uFF1A\u30A4\u30D9\u30F3\u30C8\u304C\u6B20\u3051\u3066\u3044\u308B\u3001\u4E0D\u5B8C\u5168\u3001\u307E\u305F\u306F\u4E0D\u6B63\u78BA\u304B\u3082\u3057\u308C\u307E\u305B\u3093\u3002 -TimeLineController.rebuildReasons.outOfDate=\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u304C\u6700\u65B0\u3067\u306F\u3042\u308A\u307E\u305B\u3093\uFF1A\u898B\u308C\u306A\u3044\u30A4\u30D9\u30F3\u30C8\u304C\u3042\u308A\u307E\u3059\u3002 -TimeLineController.rebuildReasons.outOfDateError=\u30BF\u30A4\u30E0\u30E9\u30A4\u30F3\u30C7\u30FC\u30BF\u304C\u6700\u65B0\u304B\u78BA\u8A8D\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002 -TimeLinecontroller.updateNowQuestion=\u30A4\u30D9\u30F3\u30C8\u30C7\u30FC\u30BF\u30D9\u30FC\u30B9\u3092\u4ECA\u66F4\u65B0\u3057\u307E\u3059\u304B\uFF1F +CTL_MakeTimeline=\u300c\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u300d +CTL_TimeLineTopComponent=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a6\u30a3\u30f3\u30c9\u30a6 +CTL_TimeLineTopComponentAction=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c8\u30c3\u30d7\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8 +HINT_TimeLineTopComponent=\u3053\u308c\u306f\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a6\u30a3\u30f3\u30c9\u30a6\u3067\u3059 +OpenTimelineAction.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +Timeline.frameName.text={0} - Autopsy\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +Timeline.goToButton.text=\u4e0b\u8a18\u3078\u79fb\u52d5\uff1a +Timeline.node.root=\u30eb\u30fc\u30c8 +Timeline.pushDescrLOD.confdlg.msg={0}\u30a4\u30d9\u30f3\u30c8\u306e\u8a73\u7d30\u304c\u8868\u793a\u53ef\u80fd\u3067\u3059\u3002\u3053\u306e\u51e6\u7406\u306f\u9577\u6642\u9593\u304b\u304b\u308b\u3082\u3057\u304f\u306fAutopsy\u3092\u30af\u30e9\u30c3\u30b7\u30e5\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f +Timeline.resultPanel.loading=\u30ed\u30fc\u30c9\u4e2d\u30fb\u30fb\u30fb +Timeline.resultsPanel.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u7d50\u679c +Timeline.runJavaFxThread.progress.creating=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u4f5c\u6210\u4e2d\u30fb\u30fb\u30fb +Timeline.zoomOutButton.text=\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8 +TimelineFrame.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 +TimeLineTopComponent.eventsTab.name=\u30a4\u30d9\u30f3\u30c8 +TimeLineTopComponent.filterTab.name=\u30d5\u30a3\u30eb\u30bf\u30fc +OpenTimeLineAction.msgdlg.text=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u4f5c\u6210\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30bd\u30fc\u30b9\u304c\u3042\u308a\u307e\u305b\u3093\u3002 +PrompDialogManager.buttonType.continueNoUpdate=\u66f4\u65b0\u305b\u305a\u6b21\u3078 +PrompDialogManager.buttonType.showTimeline=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u8868\u793a +PrompDialogManager.buttonType.update=\u66f4\u65b0 +PromptDialogManager.confirmDuringIngest.contentText=\u6b21\u3078\u9032\u307f\u307e\u3059\u304b\uff1f +PromptDialogManager.confirmDuringIngest.headerText=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b8c\u4e86\u3059\u308b\u524d\u306b\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u3092\u8868\u793a\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002\n\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u5b8c\u6210\u3057\u3066\u3044\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +PromptDialogManager.progressDialog.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u3092\u5165\u529b\u4e2d +PromptDialogManager.rebuildPrompt.details=\u8a73\u7d30\uff1a +PromptDialogManager.rebuildPrompt.headerText=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u4e0d\u5b8c\u5168\u307e\u305f\u306f\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\n \u6b20\u843d\u3057\u3066\u3044\u308b\u307e\u305f\u306f\u4e0d\u6b63\u78ba\u306a\u30a4\u30d9\u30f3\u30c8\u304c\u4e00\u90e8\u3042\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +Timeline.confirmation.dialogs.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u307e\u3059\u304b\uff1f +Timeline.pushDescrLOD.confdlg.title=\u8aac\u660e\u306e\u8a18\u8ff0\u30ec\u30d9\u30eb\u3092\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f +TimeLineController.errorTitle=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a8\u30e9\u30fc +TimeLineController.outOfDate.errorMessage=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u304c\u6700\u65b0\u304b\u78ba\u8a8d\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f\u3057\u307e\u3057\u305f\u3002\u66f4\u65b0\u304c\u5fc5\u8981\u3060\u3068\u60f3\u5b9a\u3057\u307e\u3059\u3002 +TimeLineController.rebuildReasons.incompleteOldSchema=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a4\u30d9\u30f3\u30c8\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u4e0d\u5b8c\u5168\u306a\u60c5\u5831\u304c\u4ee5\u524d\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u3057\u305f\uff1a\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u66f4\u65b0\u3057\u306a\u3044\u3068\u3001\u4e00\u90e8\u306e\u6a5f\u80fd\u304c\u5229\u7528\u3067\u304d\u306a\u3044\u3001\u307e\u305f\u306f\u6a5f\u80fd\u3057\u306a\u3044\u304b\u3082\u3057\u308c\u306a\u3044\u3067\u3059\u3002 +TimeLineController.rebuildReasons.ingestWasRunning=\u30a4\u30f3\u30b8\u30a7\u30b9\u30c8\u304c\u5b9f\u884c\u4e2d\u306b\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30a4\u30d9\u30f3\u30c8\u306e\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306b\u60c5\u5831\u304c\u5165\u529b\u3055\u308c\u3066\u3044\u307e\u3057\u305f\uff1a\u30a4\u30d9\u30f3\u30c8\u304c\u6b20\u3051\u3066\u3044\u308b\u3001\u4e0d\u5b8c\u5168\u3001\u307e\u305f\u306f\u4e0d\u6b63\u78ba\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 +TimeLineController.rebuildReasons.outOfDate=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u3067\u306f\u3042\u308a\u307e\u305b\u3093\uff1a\u898b\u308c\u306a\u3044\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u3059\u3002 +TimeLineController.rebuildReasons.outOfDateError=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u30c7\u30fc\u30bf\u304c\u6700\u65b0\u304b\u78ba\u8a8d\u3067\u304d\u307e\u305b\u3093\u3067\u3057\u305f\u3002 +TimeLinecontroller.updateNowQuestion=\u30a4\u30d9\u30f3\u30c8\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u3092\u4eca\u66f4\u65b0\u3057\u307e\u3059\u304b\uff1f TimelinePanel.jButton13.text=\u5168\u3066 -Timeline.yearBarChart.x.years=\u5E74 -TimelinePanel.jButton1.text=6\u30F6\u6708 +Timeline.yearBarChart.x.years=\u5e74 +TimelinePanel.jButton1.text=6\u30f6\u6708 TimelinePanel.jButton10.text=1\u6642\u9593 TimelinePanel.jButton9.text=12\u6642\u9593 -TimelinePanel.jButton11.text=5\u5E74 -TimelinePanel.jButton12.text=10\u5E74 +TimelinePanel.jButton11.text=5\u5e74 +TimelinePanel.jButton12.text=10\u5e74 TimelinePanel.jButton6.text=1\u9031\u9593 -TimelinePanel.jButton5.text=1\u5E74 -TimelinePanel.jButton8.text=1\u65E5 -TimelinePanel.jButton7.text=3\u65E5 -TimelinePanel.jButton2.text=1\u30F6\u6708 -TimelinePanel.jButton3.text=3\u30F6\u6708 -TimelinePanel.jButton4.text=2\u9031\u9593 \ No newline at end of file +TimelinePanel.jButton5.text=1\u5e74 +TimelinePanel.jButton8.text=1\u65e5 +TimelinePanel.jButton7.text=3\u65e5 +TimelinePanel.jButton2.text=1\u30f6\u6708 +TimelinePanel.jButton3.text=3\u30f6\u6708 +TimelinePanel.jButton4.text=2\u9031\u9593 +TimeLineResultView.startDateToEndDate.text={0}\u304b\u3089{1} \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form index 1f0f367115..a0ca021ff5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form @@ -82,33 +82,45 @@ - - - - - - + - + + + + + + + + + + + + - - - - - - + - + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 2a37c6ef1b..1e616fb735 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2013-16 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,15 +19,15 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.collect.Iterables; -import java.awt.BorderLayout; import java.beans.PropertyVetoException; import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; -import javafx.embed.swing.JFXPanel; import javafx.scene.Scene; import javafx.scene.control.SplitPane; import javafx.scene.control.Tab; @@ -40,6 +40,7 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javax.swing.SwingUtilities; import org.controlsfx.control.Notifications; +import org.joda.time.format.DateTimeFormatter; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -54,12 +55,13 @@ import org.openide.windows.WindowManager; import org.sleuthkit.autopsy.corecomponents.DataContentPanel; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.actions.Back; import org.sleuthkit.autopsy.timeline.actions.Forward; import org.sleuthkit.autopsy.timeline.explorernodes.EventNode; +import org.sleuthkit.autopsy.timeline.explorernodes.EventRootNode; import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar; import org.sleuthkit.autopsy.timeline.ui.StatusBar; -import org.sleuthkit.autopsy.timeline.ui.TimeLineResultView; import org.sleuthkit.autopsy.timeline.ui.TimeZonePanel; import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; @@ -80,55 +82,31 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private static final Logger LOGGER = Logger.getLogger(TimeLineTopComponent.class.getName()); + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final DataContentPanel contentViewerPanel; - private final TimeLineResultView tlResultView; + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) + private DataResultPanel dataResultPanel; + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final ExplorerManager em = new ExplorerManager(); + private final TimeLineController controller; + private Set selectedEventIDs = new HashSet<>(); + @NbBundle.Messages({ "TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."}) /** * Listener that drives the ContentViewer when in List ViewMode. */ - private final InvalidationListener selectedEventListener = new InvalidationListener() { + private final InvalidationListener selectedEventsListener = new InvalidationListener() { @Override public void invalidated(Observable observable) { - if (controller.getSelectedEventIDs().size() == 1) { - try { - EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel()); - SwingUtilities.invokeLater(() -> { - - Node[] eventNodes = new Node[]{eventNode}; - Children.Array children = new Children.Array(); - children.add(eventNodes); - - em.setRootContext(new AbstractNode(children)); - try { - em.setSelectedNodes(eventNodes); - System.out.println(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); - - } catch (PropertyVetoException ex) { - LOGGER.log(Level.SEVERE, "Explorer manager selection was vetoed.", ex); //NON-NLS - } - contentViewerPanel.setNode(eventNode); - }); - - } catch (IllegalStateException ex) { - //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. - LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS - Platform.runLater(() -> { - Notifications.create() - .owner(jFXVizPanel.getScene().getWindow()) - .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg()) - .showError(); - }); - } + if (controller.getViewMode() == ViewMode.LIST) { + updateContentViewer(); } else { - SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(null)); + updateResultView(); } } }; @@ -142,11 +120,17 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application contentViewerPanel = DataContentPanel.createInstance(); - this.contentViewerContainerPanel.add(contentViewerPanel, BorderLayout.CENTER); - tlResultView = new TimeLineResultView(controller, contentViewerPanel); - final DataResultPanel dataResultPanel = tlResultView.getDataResultPanel(); - this.resultContainerPanel.add(dataResultPanel, BorderLayout.CENTER); + dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, contentViewerPanel); + + lowerSplitXPane.setLeftComponent(dataResultPanel); dataResultPanel.open(); + lowerSplitXPane.setRightComponent(contentViewerPanel); + + //set up listeners on relevant properties + TimeLineController.getTimeZone().addListener(timeZone -> dataResultPanel.setPath(getResultViewerSummaryString())); + + controller.getSelectedEventIDs().addListener(selectedEventsListener); + updateResultView(); customizeFXComponents(); //Listen to ViewMode and adjust GUI componenets as needed. @@ -156,28 +140,18 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer case DETAIL: /* * For counts and details mode, restore the result table at - * the bottom left, if neccesary. + * the bottom left. */ - SwingUtilities.invokeLater(() -> { - splitYPane.remove(contentViewerContainerPanel); - if ((lowerSplitXPane.getParent() == splitYPane) == false) { - splitYPane.add(lowerSplitXPane); - lowerSplitXPane.add(contentViewerContainerPanel); - } - }); - controller.getSelectedEventIDs().removeListener(selectedEventListener); + controller.getSelectedEventIDs().removeListener(selectedEventsListener); + SwingUtilities.invokeLater(this::showResultTable); break; case LIST: /* * For list mode, remove the result table, and let the * content viewer expand across the bottom. */ - SwingUtilities.invokeLater(() -> { - splitYPane.remove(lowerSplitXPane); - splitYPane.add(contentViewerContainerPanel); - dataResultPanel.setNode(null); - }); - controller.getSelectedEventIDs().addListener(selectedEventListener); + controller.getSelectedEventIDs().addListener(selectedEventsListener); + SwingUtilities.invokeLater(this::hideResultTable); break; default: throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); @@ -256,12 +230,12 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer // //GEN-BEGIN:initComponents private void initComponents() { - jFXstatusPanel = new JFXPanel(); + jFXstatusPanel = new javafx.embed.swing.JFXPanel(); splitYPane = new javax.swing.JSplitPane(); - jFXVizPanel = new JFXPanel(); + jFXVizPanel = new javafx.embed.swing.JFXPanel(); lowerSplitXPane = new javax.swing.JSplitPane(); - resultContainerPanel = new javax.swing.JPanel(); - contentViewerContainerPanel = new javax.swing.JPanel(); + leftFillerPanel = new javax.swing.JPanel(); + rightfillerPanel = new javax.swing.JPanel(); jFXstatusPanel.setPreferredSize(new java.awt.Dimension(100, 16)); @@ -276,13 +250,31 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer lowerSplitXPane.setPreferredSize(new java.awt.Dimension(1200, 300)); lowerSplitXPane.setRequestFocusEnabled(false); - resultContainerPanel.setPreferredSize(new java.awt.Dimension(700, 300)); - resultContainerPanel.setLayout(new java.awt.BorderLayout()); - lowerSplitXPane.setLeftComponent(resultContainerPanel); + javax.swing.GroupLayout leftFillerPanelLayout = new javax.swing.GroupLayout(leftFillerPanel); + leftFillerPanel.setLayout(leftFillerPanelLayout); + leftFillerPanelLayout.setHorizontalGroup( + leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 599, Short.MAX_VALUE) + ); + leftFillerPanelLayout.setVerticalGroup( + leftFillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 54, Short.MAX_VALUE) + ); - contentViewerContainerPanel.setPreferredSize(new java.awt.Dimension(500, 300)); - contentViewerContainerPanel.setLayout(new java.awt.BorderLayout()); - lowerSplitXPane.setRightComponent(contentViewerContainerPanel); + lowerSplitXPane.setLeftComponent(leftFillerPanel); + + javax.swing.GroupLayout rightfillerPanelLayout = new javax.swing.GroupLayout(rightfillerPanel); + rightfillerPanel.setLayout(rightfillerPanelLayout); + rightfillerPanelLayout.setHorizontalGroup( + rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 364, Short.MAX_VALUE) + ); + rightfillerPanelLayout.setVerticalGroup( + rightfillerPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGap(0, 54, Short.MAX_VALUE) + ); + + lowerSplitXPane.setRightComponent(rightfillerPanel); splitYPane.setRightComponent(lowerSplitXPane); @@ -291,10 +283,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(splitYPane, javax.swing.GroupLayout.DEFAULT_SIZE, 972, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(0, 0, 0)) + .addComponent(jFXstatusPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) @@ -306,11 +295,11 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JPanel contentViewerContainerPanel; private javafx.embed.swing.JFXPanel jFXVizPanel; private javafx.embed.swing.JFXPanel jFXstatusPanel; + private javax.swing.JPanel leftFillerPanel; private javax.swing.JSplitPane lowerSplitXPane; - private javax.swing.JPanel resultContainerPanel; + private javax.swing.JPanel rightfillerPanel; private javax.swing.JSplitPane splitYPane; // End of variables declaration//GEN-END:variables @@ -341,4 +330,98 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer public ExplorerManager getExplorerManager() { return em; } + + /** + * @return a String representation of all the Events displayed + */ + @NbBundle.Messages({ + "TimeLineResultView.startDateToEndDate.text={0} to {1}"}) + private String getResultViewerSummaryString() { + if (controller.getSelectedTimeRange().get() != null) { + final DateTimeFormatter zonedFormatter = TimeLineController.getZonedFormatter(); + String start = controller.getSelectedTimeRange().get().getStart() + .withZone(TimeLineController.getJodaTimeZone()) + .toString(zonedFormatter); + String end = controller.getSelectedTimeRange().get().getEnd() + .withZone(TimeLineController.getJodaTimeZone()) + .toString(zonedFormatter); + return Bundle.TimeLineResultView_startDateToEndDate_text(start, end); + } + return ""; + } + + /** + * refresh this view with the events selected in the controller + */ + private void updateResultView() { + Set newSelectedEventIDs = new HashSet<>(controller.getSelectedEventIDs()); + if (selectedEventIDs.equals(newSelectedEventIDs) == false) { + selectedEventIDs = newSelectedEventIDs; + final EventRootNode root = new EventRootNode( + NbBundle.getMessage(this.getClass(), "Timeline.node.root"), selectedEventIDs, + controller.getEventsModel()); + + //this must be in edt or exception is thrown + SwingUtilities.invokeLater(() -> { + dataResultPanel.setPath(getResultViewerSummaryString()); + dataResultPanel.setNode(root); + }); + } + } + + private void updateContentViewer() { + if (controller.getSelectedEventIDs().size() == 1) { + try { + EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel()); + SwingUtilities.invokeLater(() -> { + + Node[] eventNodes = new Node[]{eventNode}; + Children.Array children = new Children.Array(); + children.add(eventNodes); + + em.setRootContext(new AbstractNode(children)); + try { + em.setSelectedNodes(eventNodes); + System.out.println(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); + + } catch (PropertyVetoException ex) { + LOGGER.log(Level.SEVERE, "Explorer manager selection was vetoed.", ex); //NON-NLS + } + contentViewerPanel.setNode(eventNode); + }); + + } catch (IllegalStateException ex) { + //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. + LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + Platform.runLater(() -> { + Notifications.create() + .owner(jFXVizPanel.getScene().getWindow()) + .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg()) + .showError(); + }); + } + } else { + SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(null)); + } + } + + private void showResultTable() { + splitYPane.remove(contentViewerPanel); + + controller.getSelectedEventIDs().addListener(selectedEventsListener); + if ((lowerSplitXPane.getParent() == splitYPane) == false) { + splitYPane.setBottomComponent(lowerSplitXPane); + lowerSplitXPane.setRightComponent(contentViewerPanel); + } + lowerSplitXPane.setOneTouchExpandable(true); + lowerSplitXPane.setContinuousLayout(true); + lowerSplitXPane.resetToPreferredSizes(); + } + + private void hideResultTable() { + controller.getSelectedEventIDs().removeListener(selectedEventsListener); + splitYPane.setBottomComponent(contentViewerPanel); + } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties index b08bf41e42..498dfbaace 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle.properties @@ -35,7 +35,6 @@ Timeline.ui.ZoomRanges.threeyears.text=Three Years Timeline.ui.ZoomRanges.fiveyears.text=Five Years Timeline.ui.ZoomRanges.tenyears.text=Ten Years Timeline.ui.ZoomRanges.all.text=All -TimeLineResultView.startDateToEndDate.text={0} to {1} VisualizationPanel.histogramTask.title=Rebuild Histogram VisualizationPanel.histogramTask.preparing=preparing VisualizationPanel.histogramTask.resetUI=resetting ui diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties index 2ba0472b32..3e0fab3823 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/Bundle_ja.properties @@ -1,7 +1,7 @@ Timeline.node.root=\u30eb\u30fc\u30c8 Timeline.ui.TimeLineChart.tooltip.text=\u30c0\u30d6\u30eb\u30af\u30ea\u30c3\u30af\u3067\u4e0b\u8a18\u306e\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\uff1a\n{0}\u301c{1}\n\u53f3\u30af\u30ea\u30c3\u30af\u3067\u5143\u306b\u623b\u308a\u307e\u3059\u3002 Timeline.ui.ZoomRanges.all.text=\u5168\u3066 -TimeLineResultView.startDateToEndDate.text={0}\u304b\u3089{1} + VisualizationPanel.histogramTask.preparing=\u6e96\u5099\u4e2d VisualizationPanel.histogramTask.queryDb=DB\u3092\u30af\u30a8\u30ea\u4e2d VisualizationPanel.histogramTask.resetUI=ui\u3092\u518d\u8a2d\u5b9a\u4e2d diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java deleted file mode 100644 index 9fb308229f..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/TimeLineResultView.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.timeline.ui; - -import java.util.HashSet; -import java.util.Set; -import javafx.beans.Observable; -import javax.swing.SwingUtilities; -import org.joda.time.format.DateTimeFormatter; -import org.openide.nodes.Node; -import org.openide.util.NbBundle; -import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; -import org.sleuthkit.autopsy.corecomponents.DataResultPanel; -import org.sleuthkit.autopsy.timeline.TimeLineController; -import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; -import org.sleuthkit.autopsy.timeline.explorernodes.EventRootNode; - -/** - * Since it was too hard to derive from {@link DataResultPanel}, this class - * implements {@link TimeLineView}, listens to the events/state of a the - * assigned {@link FilteredEventsModel} and acts appropriately on its - * {@link DataResultPanel}. That is, this class acts as a sort of bridge/adapter - * between a FilteredEventsModel instance and a DataResultPanel instance. - */ -public class TimeLineResultView { - - /** - * the {@link DataResultPanel} that is the real view proxied by this class - */ - private final DataResultPanel dataResultPanel; - - private final TimeLineController controller; - - private final FilteredEventsModel filteredEvents; - - private Set selectedEventIDs = new HashSet<>(); - - public DataResultPanel getDataResultPanel() { - return dataResultPanel; - } - - public TimeLineResultView(TimeLineController controller, DataContent dataContent) { - - this.controller = controller; - this.filteredEvents = controller.getEventsModel(); - dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, dataContent); - - //set up listeners on relevant properties - TimeLineController.getTimeZone().addListener(timeZone -> dataResultPanel.setPath(getSummaryString())); - - controller.getSelectedEventIDs().addListener((Observable selectedIDs) -> refresh()); - refresh(); - } - - /** - * @return a String representation of all the Events displayed - */ - private String getSummaryString() { - if (controller.getSelectedTimeRange().get() != null) { - final DateTimeFormatter zonedFormatter = TimeLineController.getZonedFormatter(); - return NbBundle.getMessage(this.getClass(), "TimeLineResultView.startDateToEndDate.text", - controller.getSelectedTimeRange().get().getStart() - .withZone(TimeLineController.getJodaTimeZone()) - .toString(zonedFormatter), - controller.getSelectedTimeRange().get().getEnd() - .withZone(TimeLineController.getJodaTimeZone()) - .toString(zonedFormatter)); - } - return ""; - } - - /** - * refresh this view with the events selected in the controller - */ - public final void refresh() { - - Set newSelectedEventIDs = new HashSet<>(controller.getSelectedEventIDs()); - if (selectedEventIDs.equals(newSelectedEventIDs) == false) { - selectedEventIDs = newSelectedEventIDs; - final EventRootNode root = new EventRootNode( - NbBundle.getMessage(this.getClass(), "Timeline.node.root"), selectedEventIDs, - filteredEvents); - - //this must be in edt or exception is thrown - SwingUtilities.invokeLater(() -> { - dataResultPanel.setPath(getSummaryString()); - dataResultPanel.setNode(root); - }); - } - } -} 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 1f96d9cd49..dd96f3c7a3 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -22,6 +22,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; +import java.util.Objects; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.binding.Bindings; @@ -289,10 +290,9 @@ class ListTimeline extends BorderPane { if (element == null) { menuItems.add(new SeparatorMenuItem()); } else { - String actionName = element.getValue(Action.NAME).toString(); + String actionName = Objects.toString(element.getValue(Action.NAME)); if (Arrays.asList("&Properties", "Tools").contains(actionName) == false) { - if (element instanceof Presenter.Popup) { JMenuItem submenu = ((Presenter.Popup) element).getPopupPresenter(); menuItems.add(SwingMenuItemAdapter.create(submenu)); From edb564214001da04e84fa2f8dd60a395ea67e58c Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 19 May 2016 17:05:59 -0400 Subject: [PATCH 19/92] rename SwingMenuItemAdapter.java -> SwingFXMenuUtils.java and cleanup --- .../timeline/SwingMenuItemAdapter.java | 79 ------------ .../timeline/ui/listvew/ListTimeline.java | 5 +- .../timeline/ui/listvew/SwingFXMenuUtils.java | 117 ++++++++++++++++++ 3 files changed, 119 insertions(+), 82 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java create mode 100644 Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java b/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java deleted file mode 100644 index c513c76830..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/SwingMenuItemAdapter.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2013-14 Basis Technology Corp. - * Contact: carrier sleuthkit org - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.sleuthkit.autopsy.timeline; - -import javafx.scene.control.Menu; -import javafx.scene.control.MenuItem; -import javafx.scene.control.SeparatorMenuItem; -import javax.swing.JMenu; -import javax.swing.JMenuItem; -import javax.swing.JPopupMenu; -import javax.swing.MenuElement; -import javax.swing.SwingUtilities; - -//TODO: move this into CoreUtils? -jm -public class SwingMenuItemAdapter extends MenuItem { - - SwingMenuItemAdapter(final JMenuItem jMenuItem) { - super(jMenuItem.getText()); - setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick)); - } - - public static MenuItem create(MenuElement jmenuItem) { - if (jmenuItem == null) { - return new SeparatorMenuItem(); - } else if (jmenuItem instanceof JMenu) { - return new SwingMenuAdapter((JMenu) jmenuItem); - } else if (jmenuItem instanceof JPopupMenu) { - return new SwingMenuAdapter((JPopupMenu) jmenuItem); - } else { - return new SwingMenuItemAdapter((JMenuItem) jmenuItem); - } - - } - - private static class SwingMenuAdapter extends Menu { - - SwingMenuAdapter(final JMenu jMenu) { - super(jMenu.getText()); - buildChildren(jMenu); - } - - SwingMenuAdapter(JPopupMenu jPopupMenu) { - super(jPopupMenu.getLabel()); - buildChildren(jPopupMenu); - } - - private void buildChildren(MenuElement jMenu) { - - for (MenuElement menuE : jMenu.getSubElements()) { - if (menuE == null) { - getItems().add(new SeparatorMenuItem()); - } else if (menuE instanceof JMenuItem) { - getItems().add(SwingMenuItemAdapter.create(menuE)); - } else if (menuE instanceof JPopupMenu) { - buildChildren(menuE); - } else { - throw new UnsupportedOperationException(); - } - } - } - } - -} 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 dd96f3c7a3..84dbc1de86 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -50,7 +50,6 @@ import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.timeline.FXMLConstructor; -import org.sleuthkit.autopsy.timeline.SwingMenuItemAdapter; import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.SingleEvent; import org.sleuthkit.autopsy.timeline.explorernodes.EventNode; @@ -295,9 +294,9 @@ class ListTimeline extends BorderPane { if (Arrays.asList("&Properties", "Tools").contains(actionName) == false) { if (element instanceof Presenter.Popup) { JMenuItem submenu = ((Presenter.Popup) element).getPopupPresenter(); - menuItems.add(SwingMenuItemAdapter.create(submenu)); + menuItems.add(SwingFXMenuUtils.createFXMenu(submenu)); } else { - menuItems.add(SwingMenuItemAdapter.create(new Actions.MenuItem(element, false))); + menuItems.add(SwingFXMenuUtils.createFXMenu(new Actions.MenuItem(element, false))); } } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java new file mode 100644 index 0000000000..8468e8644e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java @@ -0,0 +1,117 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2011-2016 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.timeline.ui.listvew; + +import javafx.scene.control.Menu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SeparatorMenuItem; +import javax.swing.JMenu; +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.MenuElement; +import javax.swing.SwingUtilities; + +/** + * Allows creation of JavaFX menus with the same structure as Swing menus and + * which invoke the same actions. + */ +public class SwingFXMenuUtils extends MenuItem { + + /** + * Factory method that creates a JavaFX MenuItem backed by a MenuElement + * + * @param jMenuElement The MenuElement to create a JavaFX menu for. + * + * @return a MenuItem for the given MenuElement + */ + public static MenuItem createFXMenu(MenuElement jMenuElement) { + if (jMenuElement == null) { + //Since null is sometime used to represenet a seperator, follow that convention. + return new SeparatorMenuItem(); + } else if (jMenuElement instanceof JMenu) { + return new MenuAdapter((JMenu) jMenuElement); + } else if (jMenuElement instanceof JPopupMenu) { + return new MenuAdapter((JPopupMenu) jMenuElement); + } else { + return new MenuItemAdapter((JMenuItem) jMenuElement); + } + } + + /** + * A JavaFX MenuItem that invokes the backing JMenuItem when clicked. + */ + private static class MenuItemAdapter extends MenuItem { + + private MenuItemAdapter(final JMenuItem jMenuItem) { + super(jMenuItem.getText()); + setOnAction(actionEvent -> SwingUtilities.invokeLater(jMenuItem::doClick)); + } + } + + /** + * A JavaFX Menu that has the same structure as a given Swing JMenu or + * JPopupMenu. + */ + private static class MenuAdapter extends Menu { + + /** + * Constructor for JMenu + * + * @param jMenu The JMenu to parallel in this Menu. + */ + MenuAdapter(final JMenu jMenu) { + super(jMenu.getText()); + populateSubMenus(jMenu); + } + + /** + * Constructor for JPopupMenu + * + * @param jPopupMenu The JPopupMenu to parallel in this Menu. + */ + MenuAdapter(JPopupMenu jPopupMenu) { + super(jPopupMenu.getLabel()); + populateSubMenus(jPopupMenu); + } + + /** + * Populate the sub menus of this menu. + * + * @param menu The MenuElement whose sub elements will be used to + * populate the sub menus of this menu. + */ + private void populateSubMenus(MenuElement menu) { + for (MenuElement menuElement : menu.getSubElements()) { + if (menuElement == null) { + //Since null is sometime used to represenet a seperator, follow that convention. + getItems().add(new SeparatorMenuItem()); + + } else if (menuElement instanceof JMenuItem) { + getItems().add(SwingFXMenuUtils.createFXMenu(menuElement)); + + } else if (menuElement instanceof JPopupMenu) { + populateSubMenus(menuElement); + + } else { + throw new UnsupportedOperationException(); + } + } + } + } +} From ffef4926a102c21d80d08b106be451a23512690b Mon Sep 17 00:00:00 2001 From: jmillman Date: Thu, 19 May 2016 17:46:10 -0400 Subject: [PATCH 20/92] cleanup EventNode and EventRootNode --- .../autopsy/timeline/Bundle.properties | 2 +- .../autopsy/timeline/Bundle_ja.properties | 1 - .../timeline/TimeLineTopComponent.java | 26 +++---- .../explorernodes/Bundle_ja.properties | 2 +- .../timeline/explorernodes/EventNode.java | 78 +++++++++++-------- .../timeline/explorernodes/EventRootNode.java | 34 +++----- 6 files changed, 68 insertions(+), 75 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties index b417dc2072..5b74bea711 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle.properties @@ -9,7 +9,7 @@ Timeline.zoomOutButton.text=Zoom Out Timeline.goToButton.text=Go To\: Timeline.yearBarChart.x.years=Years Timeline.resultPanel.loading=Loading... -Timeline.node.root=Root + TimelineFrame.title=Timeline TimelinePanel.jButton1.text=6m TimelinePanel.jButton13.text=all diff --git a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties index 3bd24f9ef1..17f945778a 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/Bundle_ja.properties @@ -5,7 +5,6 @@ HINT_TimeLineTopComponent=\u3053\u308c\u306f\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 OpenTimelineAction.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 Timeline.frameName.text={0} - Autopsy\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3 Timeline.goToButton.text=\u4e0b\u8a18\u3078\u79fb\u52d5\uff1a -Timeline.node.root=\u30eb\u30fc\u30c8 Timeline.pushDescrLOD.confdlg.msg={0}\u30a4\u30d9\u30f3\u30c8\u306e\u8a73\u7d30\u304c\u8868\u793a\u53ef\u80fd\u3067\u3059\u3002\u3053\u306e\u51e6\u7406\u306f\u9577\u6642\u9593\u304b\u304b\u308b\u3082\u3057\u304f\u306fAutopsy\u3092\u30af\u30e9\u30c3\u30b7\u30e5\u3059\u308b\u53ef\u80fd\u6027\u304c\u3042\u308a\u307e\u3059\u3002\n\n\u5b9f\u884c\u3057\u307e\u3059\u304b\uff1f Timeline.resultPanel.loading=\u30ed\u30fc\u30c9\u4e2d\u30fb\u30fb\u30fb Timeline.resultsPanel.title=\u30bf\u30a4\u30e0\u30e9\u30a4\u30f3\u7d50\u679c diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 1e616fb735..ab547147ab 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -21,9 +21,7 @@ package org.sleuthkit.autopsy.timeline; import com.google.common.collect.Iterables; import java.beans.PropertyVetoException; import java.util.Collections; -import java.util.HashSet; import java.util.List; -import java.util.Set; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.InvalidationListener; @@ -93,8 +91,6 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private final TimeLineController controller; - private Set selectedEventIDs = new HashSet<>(); - @NbBundle.Messages({ "TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."}) /** @@ -351,22 +347,18 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer } /** - * refresh this view with the events selected in the controller + * Update the DataResultPanel with the events selected in the controller. */ private void updateResultView() { - Set newSelectedEventIDs = new HashSet<>(controller.getSelectedEventIDs()); - if (selectedEventIDs.equals(newSelectedEventIDs) == false) { - selectedEventIDs = newSelectedEventIDs; - final EventRootNode root = new EventRootNode( - NbBundle.getMessage(this.getClass(), "Timeline.node.root"), selectedEventIDs, - controller.getEventsModel()); + final EventRootNode root = new EventRootNode( + controller.getSelectedEventIDs(), + controller.getEventsModel()); - //this must be in edt or exception is thrown - SwingUtilities.invokeLater(() -> { - dataResultPanel.setPath(getResultViewerSummaryString()); - dataResultPanel.setNode(root); - }); - } + //this must be in edt or exception is thrown + SwingUtilities.invokeLater(() -> { + dataResultPanel.setPath(getResultViewerSummaryString()); + dataResultPanel.setNode(root); + }); } private void updateContentViewer() { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties index d431985f98..7096881555 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/Bundle_ja.properties @@ -1 +1 @@ -EventRoodNode.tooManyNode.displayName=\u8868\u793A\u3059\u308B\u30A4\u30D9\u30F3\u30C8\u6570\u304C\u591A\u3059\u304E\u307E\u3059\u3002\u6700\u5927 \= {0}\u3002\u8868\u793A\u3059\u308B\u30A4\u30D9\u30F3\u30C8\u306F{1}\u3042\u308A\u307E\u3059\u3002 \ No newline at end of file +EventRoodNode.tooManyNode.displayName=\u8868\u793a\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u6570\u304c\u591a\u3059\u304e\u307e\u3059\u3002\u6700\u5927 \= {0}\u3002\u8868\u793a\u3059\u308b\u30a4\u30d9\u30f3\u30c8\u306f{1}\u3042\u308a\u307e\u3059\u3002 \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java index 09ffec0ff0..ee22cfd295 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2014-16 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -23,13 +23,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.logging.Level; -import javafx.beans.Observable; import javax.swing.Action; import org.joda.time.DateTime; import org.joda.time.DateTimeZone; import org.openide.nodes.Children; import org.openide.nodes.PropertySupport; import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; @@ -47,27 +47,36 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * * Explorer Node for SingleEvents. + * * Explorer Node for a SingleEvent. */ public class EventNode extends DisplayableItemNode { + private static final long serialVersionUID = 1L; + private static final Logger LOGGER = Logger.getLogger(EventNode.class.getName()); - private final SingleEvent e; + private final SingleEvent event; - EventNode(SingleEvent eventById, AbstractFile file, BlackboardArtifact artifact) { - super(Children.LEAF, Lookups.fixed(eventById, file, artifact)); - this.e = eventById; - this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + e.getEventType().getIconBase()); // NON-NLS + EventNode(SingleEvent event, AbstractFile file, BlackboardArtifact artifact) { + super(Children.LEAF, Lookups.fixed(event, file, artifact)); + this.event = event; + this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + event.getEventType().getIconBase()); // NON-NLS } - EventNode(SingleEvent eventById, AbstractFile file) { - super(Children.LEAF, Lookups.fixed(eventById, file)); - this.e = eventById; - this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + e.getEventType().getIconBase()); // NON-NLS + EventNode(SingleEvent event, AbstractFile file) { + super(Children.LEAF, Lookups.fixed(event, file)); + this.event = event; + this.setIconBaseWithExtension("org/sleuthkit/autopsy/timeline/images/" + event.getEventType().getIconBase()); // NON-NLS } @Override + @NbBundle.Messages({ + "NodeProperty.displayName.icon=Icon", + "NodeProperty.displayName.description=Description", + "NodeProperty.displayName.baseType=Base Type", + "NodeProperty.displayName.subType=Sub Type", + "NodeProperty.displayName.known=Known", + "NodeProperty.displayName.dateTime=Date/Time"}) protected Sheet createSheet() { Sheet s = super.createSheet(); Sheet.Set properties = s.get(Sheet.PROPERTIES); @@ -76,28 +85,25 @@ public class EventNode extends DisplayableItemNode { s.put(properties); } - final TimeProperty timePropery = new TimeProperty("time", "Date/Time", "time ", getDateTimeString()); // NON-NLS - - TimeLineController.getTimeZone().addListener((Observable observable) -> { - try { - timePropery.setValue(getDateTimeString()); - } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { - LOGGER.log(Level.SEVERE, "unexpected error setting date/time property on EventNode explorer node", ex); //NON-NLS - } - }); - - properties.put(new NodeProperty<>("icon", "Icon", "icon", true)); // NON-NLS //gets overridden with icon - properties.put(timePropery); - properties.put(new NodeProperty<>("description", "Description", "description", e.getFullDescription())); // NON-NLS - properties.put(new NodeProperty<>("eventBaseType", "Base Type", "base type", e.getEventType().getSuperType().getDisplayName())); // NON-NLS - properties.put(new NodeProperty<>("eventSubType", "Sub Type", "sub type", e.getEventType().getDisplayName())); // NON-NLS - properties.put(new NodeProperty<>("Known", "Known", "known", e.getKnown().toString())); // NON-NLS + properties.put(new NodeProperty<>("icon", Bundle.NodeProperty_displayName_icon(), "icon", true)); // NON-NLS //gets overridden with icon + properties.put(new TimeProperty("time", Bundle.NodeProperty_displayName_dateTime(), "time ", getDateTimeString()));// NON-NLS + properties.put(new NodeProperty<>("description", Bundle.NodeProperty_displayName_description(), "description", event.getFullDescription())); // NON-NLS + properties.put(new NodeProperty<>("eventBaseType", Bundle.NodeProperty_displayName_baseType(), "base type", event.getEventType().getSuperType().getDisplayName())); // NON-NLS + properties.put(new NodeProperty<>("eventSubType", Bundle.NodeProperty_displayName_subType(), "sub type", event.getEventType().getDisplayName())); // NON-NLS + properties.put(new NodeProperty<>("Known", Bundle.NodeProperty_displayName_known(), "known", event.getKnown().toString())); // NON-NLS return s; } + /** + * Get the time of this event as a String formated according to the + * controller's time zone setting. + * + * @return The time of this event as a String formated according to the + * controller's time zone setting. + */ private String getDateTimeString() { - return new DateTime(e.getStartMillis(), DateTimeZone.UTC).toString(TimeLineController.getZonedFormatter()); + return new DateTime(event.getStartMillis(), DateTimeZone.UTC).toString(TimeLineController.getZonedFormatter()); } @Override @@ -122,7 +128,7 @@ public class EventNode extends DisplayableItemNode { @Override public T accept(DisplayableItemNodeVisitor dinv) { - throw new UnsupportedOperationException("Not supported yet."); // NON-NLS //To change body of generated methods, choose Tools | Templates. + throw new UnsupportedOperationException("Not supported yet."); // NON-NLS } /* @@ -138,7 +144,7 @@ public class EventNode extends DisplayableItemNode { * We use TimeProperty instead of a normal NodeProperty to correctly display * the date/time when the user changes the timezone setting. */ - private class TimeProperty extends PropertySupport.ReadWrite { + final private class TimeProperty extends PropertySupport.ReadWrite { private String value; @@ -151,6 +157,14 @@ public class EventNode extends DisplayableItemNode { super(name, String.class, displayName, shortDescription); setValue("suppressCustomEditor", Boolean.TRUE); // remove the "..." (editing) button NON-NLS this.value = value; + TimeLineController.getTimeZone().addListener(timeZone -> { + try { + setValue(getDateTimeString()); + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) { + LOGGER.log(Level.SEVERE, "Unexpected error setting date/time property on EventNode explorer node", ex); //NON-NLS + } + }); + } @Override @@ -159,7 +173,7 @@ public class EventNode extends DisplayableItemNode { } @Override - final public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { + public void setValue(String t) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { String oldValue = getValue(); value = t; firePropertyChange("time", oldValue, t); // NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java index eb84883808..3d1d44132e 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/EventRootNode.java @@ -34,10 +34,12 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.datamodel.TskCoreException; /** - * Root Explorer node to represent events. + * Root Explorer Node to represent events. */ public class EventRootNode extends DisplayableItemNode { + private static final long serialVersionUID = 1L; + /** * Since the lazy loading seems to be broken if there are more than this * many child events, we don't show them and just show a message showing the @@ -45,18 +47,8 @@ public class EventRootNode extends DisplayableItemNode { */ public static final int MAX_EVENTS_TO_DISPLAY = 5000; - /** - * the number of child events - */ - private final int childCount; - - public EventRootNode(String NAME, Collection fileIds, FilteredEventsModel filteredEvents) { + public EventRootNode(Collection fileIds, FilteredEventsModel filteredEvents) { super(Children.create(new EventNodeChildFactory(fileIds, filteredEvents), true), Lookups.singleton(fileIds)); - - super.setName(NAME); - super.setDisplayName(NAME); - - childCount = fileIds.size(); } @Override @@ -69,9 +61,6 @@ public class EventRootNode extends DisplayableItemNode { return null; } - public int getChildCount() { - return childCount; - } /* * TODO (AUT-1849): Correct or remove peristent column reordering code @@ -90,12 +79,12 @@ public class EventRootNode extends DisplayableItemNode { private static final Logger LOGGER = Logger.getLogger(EventNodeChildFactory.class.getName()); /** - * list of event ids that act as keys for the child nodes. + * List of event IDs that act as keys for the child nodes. */ private final Collection eventIDs; /** - * filteredEvents is used to lookup the events from their ids + * filteredEvents is used to lookup the events from their IDs */ private final FilteredEventsModel filteredEvents; @@ -107,8 +96,8 @@ public class EventRootNode extends DisplayableItemNode { @Override protected boolean createKeys(List toPopulate) { /** - * if there are too many events, just add one id (-1) to indicate - * this. + * If there are too many events, just add one dummy ID (-1) to + * indicate this. */ if (eventIDs.size() < MAX_EVENTS_TO_DISPLAY) { toPopulate.addAll(eventIDs); @@ -122,8 +111,8 @@ public class EventRootNode extends DisplayableItemNode { protected Node createNodeForKey(Long eventID) { if (eventID < 0) { /* - * if the eventId is a the special value, return a node with a - * warning that their are too many evens + * If the eventId is a the special value ( -1 ), return a node + * with a warning that their are too many evens */ return new TooManyNode(eventIDs.size()); } else { @@ -147,8 +136,7 @@ public class EventRootNode extends DisplayableItemNode { } /** - * A Node that just shows a warning message that their are too many events - * to show + * A Node with a warning message that their are too many events to show. */ private static class TooManyNode extends AbstractNode { From fbb0023b9931a722bee4ec96cc436429cd728d8a Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 20 May 2016 15:12:01 -0400 Subject: [PATCH 21/92] default location for saving custom hash database --- .../modules/hashdatabase/HashDbCreateDatabaseDialog.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java index 96f0c1b16e..1b60250a6b 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java +++ b/Core/src/org/sleuthkit/autopsy/modules/hashdatabase/HashDbCreateDatabaseDialog.java @@ -22,6 +22,7 @@ import java.awt.Dimension; import java.awt.Toolkit; import java.io.File; import java.io.IOException; +import java.nio.file.Paths; import java.util.logging.Level; import javax.swing.JFileChooser; import javax.swing.JFrame; @@ -30,6 +31,7 @@ import org.apache.commons.io.FilenameUtils; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ModuleSettings; +import org.sleuthkit.autopsy.coreutils.PlatformUtil; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType; import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException; @@ -273,12 +275,17 @@ final class HashDbCreateDatabaseDialog extends javax.swing.JDialog { private void saveAsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveAsButtonActionPerformed try { - String lastBaseDirectory = ""; + String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString(); if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) { lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY); } StringBuilder path = new StringBuilder(); path.append(lastBaseDirectory); + File hashDbFolder = new File(path.toString()); + // create the folder if it doesn't exist + if (!hashDbFolder.exists()){ + hashDbFolder.mkdir(); + } if (!hashSetNameTextField.getText().isEmpty()) { path.append(File.separator).append(hashSetNameTextField.getText()); } else { From 7a0bfb49802464b59f4e5752a83cfded4ffb0b3b Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 20 May 2016 15:20:25 -0400 Subject: [PATCH 22/92] cleanup and refactor TimeLineTopComponent --- .../autopsy/timeline/TimeLineController.java | 37 +- .../timeline/TimeLineTopComponent.form | 2 +- .../timeline/TimeLineTopComponent.java | 322 ++++++++---------- .../timeline/explorernodes/ExplorerUtils.java | 31 -- 4 files changed, 170 insertions(+), 222 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java index 5ac1eb68aa..30e0e6bd5f 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java @@ -188,7 +188,7 @@ public class TimeLineController { } @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - private TimeLineTopComponent mainFrame; + private TimeLineTopComponent topComponent; //are the listeners currently attached @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @@ -227,19 +227,32 @@ public class TimeLineController { private final PromptDialogManager promptDialogManager = new PromptDialogManager(this); /** - * @return A list of the selected event ids + * Get an ObservableList of selected event IDs + * + * @return A list of the selected event IDs */ synchronized public ObservableList getSelectedEventIDs() { return selectedEventIDs; } /** - * @return a read only view of the selected interval. + * Get a read only observable view of the selected time range. + * + * @return A read only view of the selected time range. */ - synchronized public ReadOnlyObjectProperty getSelectedTimeRange() { + synchronized public ReadOnlyObjectProperty selectedTimeRangeProperty() { return selectedTimeRange.getReadOnlyProperty(); } + /** + * Get the selected time range. + * + * @return The selected time range. + */ + synchronized public Interval getSelectedTimeRange() { + return selectedTimeRange.get(); + } + public ReadOnlyBooleanProperty eventsDBStaleProperty() { return eventsDBStale.getReadOnlyProperty(); } @@ -470,9 +483,9 @@ public class TimeLineController { IngestManager.getInstance().removeIngestModuleEventListener(ingestModuleListener); IngestManager.getInstance().removeIngestJobEventListener(ingestJobListener); Case.removePropertyChangeListener(caseListener); - if (mainFrame != null) { - mainFrame.close(); - mainFrame = null; + if (topComponent != null) { + topComponent.close(); + topComponent = null; } OpenTimelineAction.invalidateController(); } @@ -634,16 +647,16 @@ public class TimeLineController { */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) synchronized private void showWindow() { - if (mainFrame == null) { - mainFrame = new TimeLineTopComponent(this); + if (topComponent == null) { + topComponent = new TimeLineTopComponent(this); } - mainFrame.open(); - mainFrame.toFront(); + topComponent.open(); + topComponent.toFront(); /* * Make this top component active so its ExplorerManager's lookup gets * proxied in Utilities.actionsGlobalContext() */ - mainFrame.requestActive(); + topComponent.requestActive(); } synchronized public void pushEventTypeZoom(EventTypeZoomLevel typeZoomeLevel) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form index a0ca021ff5..ea5a68ccdc 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.form @@ -65,7 +65,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index ab547147ab..1b0032d290 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -18,14 +18,13 @@ */ package org.sleuthkit.autopsy.timeline; -import com.google.common.collect.Iterables; -import java.beans.PropertyVetoException; import java.util.Collections; import java.util.List; import java.util.logging.Level; import javafx.application.Platform; import javafx.beans.InvalidationListener; import javafx.beans.Observable; +import javafx.collections.ObservableList; import javafx.scene.Scene; import javafx.scene.control.SplitPane; import javafx.scene.control.Tab; @@ -38,14 +37,12 @@ import javafx.scene.layout.Priority; import javafx.scene.layout.VBox; import javax.swing.SwingUtilities; import org.controlsfx.control.Notifications; +import org.joda.time.Interval; import org.joda.time.format.DateTimeFormatter; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; -import org.openide.util.Utilities; import org.openide.windows.Mode; import org.openide.windows.TopComponent; import static org.openide.windows.TopComponent.PROP_UNDOCKING_DISABLED; @@ -65,7 +62,6 @@ import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel; import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane; -import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; /** @@ -91,43 +87,85 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer private final TimeLineController controller; - @NbBundle.Messages({ - "TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."}) /** - * Listener that drives the ContentViewer when in List ViewMode. + * Listener that drives the result viewer or content viewer (depending on + * view mode) according to the controller's selected event IDs */ + @NbBundle.Messages({"TimelineTopComponent.selectedEventListener.errorMsg=There was a problem getting the content for the selected event."}) private final InvalidationListener selectedEventsListener = new InvalidationListener() { @Override public void invalidated(Observable observable) { - if (controller.getViewMode() == ViewMode.LIST) { - updateContentViewer(); - } else { - updateResultView(); + ObservableList selectedEventIDs = controller.getSelectedEventIDs(); + + //depending on the active view mode, we either update the dataResultPanel, or update the contentViewerPanel directly. + switch (controller.getViewMode()) { + case LIST: + if (selectedEventIDs.size() == 1) { + //if there is only one event selected, make a explorer node for it and push it to the content viewer. + try { + EventNode eventNode = EventNode.createEventNode(selectedEventIDs.get(0), controller.getEventsModel()); + SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(eventNode)); + } catch (IllegalStateException ex) { + //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. + LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + } catch (TskCoreException ex) { + LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS + Platform.runLater(() -> { + Notifications.create() + .owner(jFXVizPanel.getScene().getWindow()) + .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg()) + .showError(); + }); + } + } else { + //There is more than one or no event selected, so clear the content viewer. + SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(null)); + } + break; + case COUNTS: + case DETAIL: + //make a root node with nodes for the selected events as children and push it to the result viewer. + EventRootNode rootNode = new EventRootNode(selectedEventIDs, controller.getEventsModel()); + SwingUtilities.invokeLater(() -> { + dataResultPanel.setPath(getResultViewerSummaryString()); + dataResultPanel.setNode(rootNode); + }); + break; + default: + throw new UnsupportedOperationException("Unknown view mode: " + controller.getViewMode()); } } }; + /** + * Constructor + * + * @param controller The TimeLineController for this topcomponent. + */ public TimeLineTopComponent(TimeLineController controller) { initComponents(); - this.controller = controller; associateLookup(ExplorerUtils.createLookup(em, getActionMap())); setName(NbBundle.getMessage(TimeLineTopComponent.class, "CTL_TimeLineTopComponent")); setToolTipText(NbBundle.getMessage(TimeLineTopComponent.class, "HINT_TimeLineTopComponent")); setIcon(WindowManager.getDefault().getMainWindow().getIconImage()); //use the same icon as main application + this.controller = controller; + + //create linked result and content views contentViewerPanel = DataContentPanel.createInstance(); dataResultPanel = DataResultPanel.createInstanceUninitialized("", "", Node.EMPTY, 0, contentViewerPanel); - lowerSplitXPane.setLeftComponent(dataResultPanel); - dataResultPanel.open(); - lowerSplitXPane.setRightComponent(contentViewerPanel); + //add them to bottom splitpane + horizontalSplitPane.setLeftComponent(dataResultPanel); + horizontalSplitPane.setRightComponent(contentViewerPanel); - //set up listeners on relevant properties + dataResultPanel.open(); //get the explorermanager + + Platform.runLater(this::initFXComponents); + + //set up listeners TimeLineController.getTimeZone().addListener(timeZone -> dataResultPanel.setPath(getResultViewerSummaryString())); - controller.getSelectedEventIDs().addListener(selectedEventsListener); - updateResultView(); - customizeFXComponents(); //Listen to ViewMode and adjust GUI componenets as needed. controller.viewModeProperty().addListener(viewMode -> { @@ -138,16 +176,22 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer * For counts and details mode, restore the result table at * the bottom left. */ - controller.getSelectedEventIDs().removeListener(selectedEventsListener); - SwingUtilities.invokeLater(this::showResultTable); + SwingUtilities.invokeLater(() -> { + splitYPane.remove(contentViewerPanel); + if ((horizontalSplitPane.getParent() == splitYPane) == false) { + splitYPane.setBottomComponent(horizontalSplitPane); + horizontalSplitPane.setRightComponent(contentViewerPanel); + } + }); break; case LIST: /* * For list mode, remove the result table, and let the * content viewer expand across the bottom. */ - controller.getSelectedEventIDs().addListener(selectedEventsListener); - SwingUtilities.invokeLater(this::hideResultTable); + SwingUtilities.invokeLater(() -> { + splitYPane.setBottomComponent(contentViewerPanel); + }); break; default: throw new UnsupportedOperationException("Unknown ViewMode: " + controller.getViewMode()); @@ -155,62 +199,65 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer }); } - @NbBundle.Messages({"TimeLineTopComponent.eventsTab.name=Events", + /** + * Create and wire up JavaFX components of the interface + */ + @NbBundle.Messages({ + "TimeLineTopComponent.eventsTab.name=Events", "TimeLineTopComponent.filterTab.name=Filters"}) - void customizeFXComponents() { - Platform.runLater(() -> { + @ThreadConfined(type = ThreadConfined.ThreadType.JFX) + void initFXComponents() { + /////init componenets of left most column from top to bottom + final TimeZonePanel timeZonePanel = new TimeZonePanel(); + VBox.setVgrow(timeZonePanel, Priority.SOMETIMES); + HistoryToolBar historyToolBar = new HistoryToolBar(controller); + final ZoomSettingsPane zoomSettingsPane = new ZoomSettingsPane(controller); - //create and wire up jfx componenets that make up the interface - final Tab filterTab = new Tab(Bundle.TimeLineTopComponent_filterTab_name(), new FilterSetPanel(controller)); - filterTab.setClosable(false); - filterTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/funnel.png")); // NON-NLS + //set up filter tab + final Tab filterTab = new Tab(Bundle.TimeLineTopComponent_filterTab_name(), new FilterSetPanel(controller)); + filterTab.setClosable(false); + filterTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/funnel.png")); // NON-NLS - final EventsTree eventsTree = new EventsTree(controller); - final VisualizationPanel visualizationPanel = new VisualizationPanel(controller, eventsTree); - final Tab eventsTreeTab = new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree); - eventsTreeTab.setClosable(false); - eventsTreeTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/timeline_marker.png")); // NON-NLS - eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL)); + //set up events tab + final EventsTree eventsTree = new EventsTree(controller); + final Tab eventsTreeTab = new Tab(Bundle.TimeLineTopComponent_eventsTab_name(), eventsTree); + eventsTreeTab.setClosable(false); + eventsTreeTab.setGraphic(new ImageView("org/sleuthkit/autopsy/timeline/images/timeline_marker.png")); // NON-NLS + eventsTreeTab.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL)); - final TabPane leftTabPane = new TabPane(filterTab, eventsTreeTab); - VBox.setVgrow(leftTabPane, Priority.ALWAYS); - controller.viewModeProperty().addListener(viewMode -> { - if (controller.getViewMode().equals(ViewMode.DETAIL) == false) { - //if view mode is counts, make sure events tab is not active - leftTabPane.getSelectionModel().select(filterTab); - } - }); - - HistoryToolBar historyToolBar = new HistoryToolBar(controller); - final TimeZonePanel timeZonePanel = new TimeZonePanel(); - VBox.setVgrow(timeZonePanel, Priority.SOMETIMES); - - final ZoomSettingsPane zoomSettingsPane = new ZoomSettingsPane(controller); - - final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane); - SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE); - - final SplitPane mainSplitPane = new SplitPane(leftVBox, visualizationPanel); - mainSplitPane.setDividerPositions(0); - - final Scene scene = new Scene(mainSplitPane); - scene.addEventFilter(KeyEvent.KEY_PRESSED, - (KeyEvent event) -> { - if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(event)) { - new Back(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(event)) { - new Back(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(event)) { - new Forward(controller).handle(null); - } else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(event)) { - new Forward(controller).handle(null); - } - }); - - //add ui componenets to JFXPanels - jFXVizPanel.setScene(scene); - jFXstatusPanel.setScene(new Scene(new StatusBar(controller))); + final TabPane leftTabPane = new TabPane(filterTab, eventsTreeTab); + VBox.setVgrow(leftTabPane, Priority.ALWAYS); + controller.viewModeProperty().addListener(viewMode -> { + if (controller.getViewMode().equals(ViewMode.DETAIL) == false) { + //if view mode is not details, switch back to the filter tab + leftTabPane.getSelectionModel().select(filterTab); + } }); + + //assemble left column + final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane); + SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE); + + final VisualizationPanel visualizationPanel = new VisualizationPanel(controller, eventsTree); + final SplitPane mainSplitPane = new SplitPane(leftVBox, visualizationPanel); + mainSplitPane.setDividerPositions(0); + + final Scene scene = new Scene(mainSplitPane); + scene.addEventFilter(KeyEvent.KEY_PRESSED, keyEvent -> { + if (new KeyCodeCombination(KeyCode.LEFT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) { + new Back(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.BACK_SPACE).match(keyEvent)) { + new Back(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.RIGHT, KeyCodeCombination.ALT_DOWN).match(keyEvent)) { + new Forward(controller).handle(null); + } else if (new KeyCodeCombination(KeyCode.BACK_SPACE, KeyCodeCombination.SHIFT_DOWN).match(keyEvent)) { + new Forward(controller).handle(null); + } + }); + + //add ui componenets to JFXPanels + jFXVizPanel.setScene(scene); + jFXstatusPanel.setScene(new Scene(new StatusBar(controller))); } @Override @@ -229,7 +276,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer jFXstatusPanel = new javafx.embed.swing.JFXPanel(); splitYPane = new javax.swing.JSplitPane(); jFXVizPanel = new javafx.embed.swing.JFXPanel(); - lowerSplitXPane = new javax.swing.JSplitPane(); + horizontalSplitPane = new javax.swing.JSplitPane(); leftFillerPanel = new javax.swing.JPanel(); rightfillerPanel = new javax.swing.JPanel(); @@ -241,10 +288,10 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer splitYPane.setPreferredSize(new java.awt.Dimension(1024, 400)); splitYPane.setLeftComponent(jFXVizPanel); - lowerSplitXPane.setDividerLocation(600); - lowerSplitXPane.setResizeWeight(0.5); - lowerSplitXPane.setPreferredSize(new java.awt.Dimension(1200, 300)); - lowerSplitXPane.setRequestFocusEnabled(false); + horizontalSplitPane.setDividerLocation(600); + horizontalSplitPane.setResizeWeight(0.5); + horizontalSplitPane.setPreferredSize(new java.awt.Dimension(1200, 300)); + horizontalSplitPane.setRequestFocusEnabled(false); javax.swing.GroupLayout leftFillerPanelLayout = new javax.swing.GroupLayout(leftFillerPanel); leftFillerPanel.setLayout(leftFillerPanelLayout); @@ -257,7 +304,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer .addGap(0, 54, Short.MAX_VALUE) ); - lowerSplitXPane.setLeftComponent(leftFillerPanel); + horizontalSplitPane.setLeftComponent(leftFillerPanel); javax.swing.GroupLayout rightfillerPanelLayout = new javax.swing.GroupLayout(rightfillerPanel); rightfillerPanel.setLayout(rightfillerPanelLayout); @@ -270,9 +317,9 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer .addGap(0, 54, Short.MAX_VALUE) ); - lowerSplitXPane.setRightComponent(rightfillerPanel); + horizontalSplitPane.setRightComponent(rightfillerPanel); - splitYPane.setRightComponent(lowerSplitXPane); + splitYPane.setRightComponent(horizontalSplitPane); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); @@ -291,10 +338,10 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JSplitPane horizontalSplitPane; private javafx.embed.swing.JFXPanel jFXVizPanel; private javafx.embed.swing.JFXPanel jFXstatusPanel; private javax.swing.JPanel leftFillerPanel; - private javax.swing.JSplitPane lowerSplitXPane; private javax.swing.JPanel rightfillerPanel; private javax.swing.JSplitPane splitYPane; // End of variables declaration//GEN-END:variables @@ -305,115 +352,34 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer putClientProperty(PROP_UNDOCKING_DISABLED, true); } - @Override - public void componentClosed() { - // TODO add custom code on component closing - } - - void writeProperties(java.util.Properties p) { - // better to version settings since initial version as advocated at - // http://wiki.apidesign.org/wiki/PropertyFiles - p.setProperty("version", "1.0"); - // TODO store your settings - } - - void readProperties(java.util.Properties p) { - String version = p.getProperty("version"); - // TODO read your settings according to their version - } - @Override public ExplorerManager getExplorerManager() { return em; } /** - * @return a String representation of all the Events displayed + * Get the string that should be used as the label above the result table. + * It displays the time range spanned by the selected events. + * + * @return A String representation of all the events displayed. */ @NbBundle.Messages({ + "# {0} - start of date range", + "# {1} - end of date range", "TimeLineResultView.startDateToEndDate.text={0} to {1}"}) private String getResultViewerSummaryString() { - if (controller.getSelectedTimeRange().get() != null) { + Interval selectedTimeRange = controller.getSelectedTimeRange(); + if (selectedTimeRange == null) { + return ""; + } else { final DateTimeFormatter zonedFormatter = TimeLineController.getZonedFormatter(); - String start = controller.getSelectedTimeRange().get().getStart() + String start = selectedTimeRange.getStart() .withZone(TimeLineController.getJodaTimeZone()) .toString(zonedFormatter); - String end = controller.getSelectedTimeRange().get().getEnd() + String end = selectedTimeRange.getEnd() .withZone(TimeLineController.getJodaTimeZone()) .toString(zonedFormatter); return Bundle.TimeLineResultView_startDateToEndDate_text(start, end); } - return ""; - } - - /** - * Update the DataResultPanel with the events selected in the controller. - */ - private void updateResultView() { - final EventRootNode root = new EventRootNode( - controller.getSelectedEventIDs(), - controller.getEventsModel()); - - //this must be in edt or exception is thrown - SwingUtilities.invokeLater(() -> { - dataResultPanel.setPath(getResultViewerSummaryString()); - dataResultPanel.setNode(root); - }); - } - - private void updateContentViewer() { - if (controller.getSelectedEventIDs().size() == 1) { - try { - EventNode eventNode = EventNode.createEventNode(Iterables.getOnlyElement(controller.getSelectedEventIDs()), controller.getEventsModel()); - SwingUtilities.invokeLater(() -> { - - Node[] eventNodes = new Node[]{eventNode}; - Children.Array children = new Children.Array(); - children.add(eventNodes); - - em.setRootContext(new AbstractNode(children)); - try { - em.setSelectedNodes(eventNodes); - System.out.println(Utilities.actionsGlobalContext().lookupAll(AbstractFile.class)); - - } catch (PropertyVetoException ex) { - LOGGER.log(Level.SEVERE, "Explorer manager selection was vetoed.", ex); //NON-NLS - } - contentViewerPanel.setNode(eventNode); - }); - - } catch (IllegalStateException ex) { - //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. - LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS - } catch (TskCoreException ex) { - LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS - Platform.runLater(() -> { - Notifications.create() - .owner(jFXVizPanel.getScene().getWindow()) - .text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg()) - .showError(); - }); - } - } else { - SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(null)); - } - } - - private void showResultTable() { - splitYPane.remove(contentViewerPanel); - - controller.getSelectedEventIDs().addListener(selectedEventsListener); - if ((lowerSplitXPane.getParent() == splitYPane) == false) { - splitYPane.setBottomComponent(lowerSplitXPane); - lowerSplitXPane.setRightComponent(contentViewerPanel); - } - lowerSplitXPane.setOneTouchExpandable(true); - lowerSplitXPane.setContinuousLayout(true); - lowerSplitXPane.resetToPreferredSizes(); - } - - private void hideResultTable() { - controller.getSelectedEventIDs().removeListener(selectedEventsListener); - splitYPane.setBottomComponent(contentViewerPanel); } } diff --git a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java deleted file mode 100644 index 1245f2d642..0000000000 --- a/Core/src/org/sleuthkit/autopsy/timeline/explorernodes/ExplorerUtils.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.timeline.explorernodes; - -import javafx.scene.Node; -import javafx.scene.Parent; -import org.openide.explorer.ExplorerManager; - -/** - * - */ -public class ExplorerUtils { - - static public ExplorerManager find(Node node) { - - if (node instanceof ExplorerManager.Provider) { - return ((ExplorerManager.Provider) node).getExplorerManager(); - } else { - Parent parent = node.getParent(); - if (parent == null) { - System.out.println(node.getScene().getWindow().getClass()); - return null; - } else { - return find(parent); - } - } - } -} From 835df96a78d2890ad6d22ab11b7d0343ccc90430 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 20 May 2016 15:54:23 -0400 Subject: [PATCH 23/92] cleanup ListTimeline --- .../timeline/ui/listvew/ListTimeline.java | 154 ++++++++++++------ 1 file changed, 102 insertions(+), 52 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 84dbc1de86..6cd573d478 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2016 Basis Technology Corp. + * Copyright 2011-2016 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -44,7 +44,7 @@ import javafx.util.Callback; import javax.swing.Action; import javax.swing.JMenuItem; import org.controlsfx.control.Notifications; -import org.openide.awt.Actions; +import org.controlsfx.control.action.ActionUtils; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.coreutils.Logger; @@ -57,43 +57,43 @@ import org.sleuthkit.autopsy.timeline.zooming.DescriptionLoD; import org.sleuthkit.datamodel.TskCoreException; /** - * + * The inner component that makes up the Lsit view. Manages the table. */ class ListTimeline extends BorderPane { private static final Logger LOGGER = Logger.getLogger(ListTimeline.class.getName()); - @FXML - private Label eventCountLabel; - - @FXML - private TableView table; - + /** + * call-back used to wrap the event ID inn a ObservableValue + */ private static final Callback, ObservableValue> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue()); - private final TimeLineController controller; - + @FXML + private Label eventCountLabel; + @FXML + private TableView table; @FXML private TableColumn idColumn; - @FXML private TableColumn millisColumn; - @FXML private TableColumn iconColumn; - @FXML private TableColumn descriptionColumn; - @FXML private TableColumn baseTypeColumn; - @FXML private TableColumn subTypeColumn; - @FXML private TableColumn knownColumn; + private final TimeLineController controller; + + /** + * Constructor + * + * @param controller The controller for this timeline + */ ListTimeline(TimeLineController controller) { this.controller = controller; FXMLConstructor.construct(this, ListTimeline.class, "ListTimeline.fxml"); @@ -111,9 +111,14 @@ class ListTimeline extends BorderPane { assert subTypeColumn != null : "fx:id=\"subTypeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; + //override default row with one that provides context menu.S table.setRowFactory(tableView -> new EventRow()); - idColumn.setCellValueFactory(CELL_VALUE_FACTORY); + //remove idColumn (can be used for debugging). + table.getColumns().remove(idColumn); + + // set up cell and cell-value factories for columns + // millisColumn.setCellValueFactory(CELL_VALUE_FACTORY); millisColumn.setCellFactory(col -> new EpochMillisCell()); @@ -132,32 +137,41 @@ class ListTimeline extends BorderPane { knownColumn.setCellValueFactory(CELL_VALUE_FACTORY); knownColumn.setCellFactory(col -> new KnownCell()); + //bind event count lable no number of items in table eventCountLabel.textProperty().bind(Bindings.size(table.getItems()).asString().concat(" events")); } - public TimeLineController getController() { - return controller; - } - + /** + * Clear all the events out of the table. + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void clear() { table.getItems().clear(); } + /** + * Set the Collection of events (by ID) to show in the table. + * + * @param eventIDs The Collection of event IDs to sho in the table. + */ @ThreadConfined(type = ThreadConfined.ThreadType.JFX) void setEventIDs(Collection eventIDs) { table.getItems().setAll(eventIDs); } /** - * Get the List of IDs of events that are selected in this list. + * Get an ObservableList of IDs of events that are selected in this table. * - * @return The List of IDs of events that are selected in this list. + * @return An ObservableList of IDs of events that are selected in this + * table. */ ObservableList getSelectedEventIDs() { return table.getSelectionModel().getSelectedItems(); } + /** + * TableCell to show the icon for the type of an event. + */ private class ImageCell extends EventTableCell { @Override @@ -172,6 +186,9 @@ class ListTimeline extends BorderPane { } } + /** + * TableCell to show the full description for an event. + */ private class DescriptionCell extends EventTableCell { @Override @@ -186,6 +203,9 @@ class ListTimeline extends BorderPane { } } + /** + * TableCell to show the base type of an event. + */ private class BaseTypeCell extends EventTableCell { @Override @@ -200,6 +220,9 @@ class ListTimeline extends BorderPane { } } + /** + * TableCell to show the sub type of an event. + */ private class EventTypeCell extends EventTableCell { @Override @@ -214,6 +237,9 @@ class ListTimeline extends BorderPane { } } + /** + * TableCell to show the known state of the file backing an event. + */ private class KnownCell extends EventTableCell { @Override @@ -228,26 +254,9 @@ class ListTimeline extends BorderPane { } } - private class EventTableCell extends TableCell { - - 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); - } - } - } - + /** + * TableCell to show the (start) time of an event. + */ private class EpochMillisCell extends EventTableCell { @Override @@ -262,6 +271,38 @@ class ListTimeline extends BorderPane { } } + /** + * Base class for TableCells that represent a SingleEvent by its ID + */ + private abstract class EventTableCell extends TableCell { + + private SingleEvent event; + + /** + * Get the SingleEvent this cell represents. + * + * @return The SingleEvent this cell represents. + */ + SingleEvent getEvent() { + return event; + } + + @Override + protected void updateItem(Long item, boolean empty) { + super.updateItem(item, empty); + + if (empty || item == null) { + event = null; + } else { + //stash the event in the cell for derived classed to use. + event = controller.getEventsModel().getEventById(item); + } + } + } + + /** + * TableRow that adds a right-click context menu. + */ private class EventRow extends TableRow { private SingleEvent event; @@ -280,23 +321,32 @@ class ListTimeline extends BorderPane { event = null; } else { event = controller.getEventsModel().getEventById(item); - + //make context menu try { EventNode node = EventNode.createEventNode(item, controller.getEventsModel()); List menuItems = new ArrayList<>(); - for (Action element : node.getActions(false)) { - if (element == null) { + //for each actions avaialable on node, make a menu item. + for (Action action : node.getActions(false)) { + if (action == null) { + // swing/netbeans uses null action to represent separator in menu menuItems.add(new SeparatorMenuItem()); } else { - String actionName = Objects.toString(element.getValue(Action.NAME)); - + String actionName = Objects.toString(action.getValue(Action.NAME)); + //for now, suppress properties and tools actions, by ignoring them if (Arrays.asList("&Properties", "Tools").contains(actionName) == false) { - if (element instanceof Presenter.Popup) { - JMenuItem submenu = ((Presenter.Popup) element).getPopupPresenter(); + if (action instanceof Presenter.Popup) { + /* + * If the action is really the root of a set + * of actions (eg, tagging). Make a menu + * that parallels the action's menu. + */ + JMenuItem submenu = ((Presenter.Popup) action).getPopupPresenter(); menuItems.add(SwingFXMenuUtils.createFXMenu(submenu)); } else { - menuItems.add(SwingFXMenuUtils.createFXMenu(new Actions.MenuItem(element, false))); + //make a JavaFX menu item that invokes the action (wrapped in a ControlsFX Action + menuItems.add(ActionUtils.createMenuItem( + new org.controlsfx.control.action.Action(actionName, actionEvent -> action.actionPerformed(null)))); } } } From 4d018f45d5f1cf350eba01f3f45d0ac43e88d093 Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 20 May 2016 16:16:39 -0400 Subject: [PATCH 24/92] fix actions which were broken by flawed refactoring --- .../autopsy/timeline/TimeLineTopComponent.java | 14 +++++++++++++- .../autopsy/timeline/ui/listvew/ListTimeline.java | 6 ++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 1b0032d290..4d1802bcd5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.timeline; +import java.beans.PropertyVetoException; import java.util.Collections; import java.util.List; import java.util.logging.Level; @@ -104,7 +105,18 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer //if there is only one event selected, make a explorer node for it and push it to the content viewer. try { EventNode eventNode = EventNode.createEventNode(selectedEventIDs.get(0), controller.getEventsModel()); - SwingUtilities.invokeLater(() -> contentViewerPanel.setNode(eventNode)); + SwingUtilities.invokeLater(() -> { + //set node as selected for actions + em.setRootContext(eventNode); + try { + em.setSelectedNodes(new Node[]{eventNode}); + } catch (PropertyVetoException ex) { + //I don't know why this would ever happen. + LOGGER.log(Level.SEVERE, "Selecting the event node was vetoed.", ex); // NON-NLS + } + //push into content viewer. + contentViewerPanel.setNode(eventNode); + }); } catch (IllegalStateException ex) { //Since the case is closed, the user probably doesn't care about this, just log it as a precaution. LOGGER.log(Level.SEVERE, "There was no case open to lookup the Sleuthkit object backing a SingleEvent.", ex); // NON-NLS 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 6cd573d478..77fc831b29 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/ListTimeline.java @@ -44,7 +44,7 @@ import javafx.util.Callback; import javax.swing.Action; import javax.swing.JMenuItem; import org.controlsfx.control.Notifications; -import org.controlsfx.control.action.ActionUtils; +import org.openide.awt.Actions; import org.openide.util.NbBundle; import org.openide.util.actions.Presenter; import org.sleuthkit.autopsy.coreutils.Logger; @@ -344,9 +344,7 @@ class ListTimeline extends BorderPane { JMenuItem submenu = ((Presenter.Popup) action).getPopupPresenter(); menuItems.add(SwingFXMenuUtils.createFXMenu(submenu)); } else { - //make a JavaFX menu item that invokes the action (wrapped in a ControlsFX Action - menuItems.add(ActionUtils.createMenuItem( - new org.controlsfx.control.action.Action(actionName, actionEvent -> action.actionPerformed(null)))); + menuItems.add(SwingFXMenuUtils.createFXMenu(new Actions.MenuItem(action, false))); } } } From 0b03435b2347e276f5ee66fb5368bb1c83f44b3c Mon Sep 17 00:00:00 2001 From: jmillman Date: Fri, 20 May 2016 16:18:56 -0400 Subject: [PATCH 25/92] add message to UnsupportedOperationException --- .../sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java index 8468e8644e..17241f2a36 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/listvew/SwingFXMenuUtils.java @@ -109,7 +109,7 @@ public class SwingFXMenuUtils extends MenuItem { populateSubMenus(menuElement); } else { - throw new UnsupportedOperationException(); + throw new UnsupportedOperationException("Unown MenuElement subclass: " + menuElement.getClass().getName()); } } } From 9f3b3539ba399030dde0246d80c9015aad3212a0 Mon Sep 17 00:00:00 2001 From: Oliver Spohngellert Date: Fri, 20 May 2016 16:20:06 -0400 Subject: [PATCH 26/92] Fixed image gallery help link. --- .../sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java index 265412146f..360ee70bb1 100644 --- a/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java +++ b/ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenHelpAction.java @@ -44,7 +44,7 @@ public final class OpenHelpAction implements ActionListener { @Override public void actionPerformed(ActionEvent e) { try { - Desktop.getDesktop().browse(URI.create("http://sleuthkit.org/autopsy/docs/user-docs/4.0/image_gallery_page.html")); //NON-NLS + Desktop.getDesktop().browse(URI.create("http://sleuthkit.org/autopsy/docs/user-docs/4.1/image_gallery_page.html")); //NON-NLS } catch (IOException ex) { Logger.getLogger(OpenHelpAction.class.getName()).log(Level.SEVERE, "failed to open help page", ex); //NON-NLS } From cb1f3c2d59f5630d7bc740046c8d077eee7390f5 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Fri, 20 May 2016 16:29:32 -0400 Subject: [PATCH 27/92] UTF-8 sanitizing all text before it is passed to Solr --- .../autopsy/keywordsearch/HtmlTextExtractor.java | 8 -------- .../org/sleuthkit/autopsy/keywordsearch/Ingester.java | 9 +++++++++ .../autopsy/keywordsearch/SolrSearchService.java | 8 -------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java index cdbead3e7f..4e03038b39 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java @@ -28,7 +28,6 @@ import java.util.Map; import java.util.logging.Level; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; -import org.sleuthkit.autopsy.coreutils.TextUtil; import org.sleuthkit.autopsy.keywordsearch.Ingester.IngesterException; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.ReadContentInputStream; @@ -163,13 +162,6 @@ class HtmlTextExtractor implements TextExtractor { } else { sb.append(textChunkBuf); } - - // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index - for (int i = 0; i < sb.length(); i++) { - if (!TextUtil.isValidSolrUTF8(sb.charAt(i))) { - sb.setCharAt(i, '^'); - } - } //reset for next chunk totalRead = 0; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index 77568a8916..1f54a3a976 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -31,6 +31,7 @@ import org.apache.solr.common.util.ContentStream; import org.apache.solr.common.SolrInputDocument; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.autopsy.coreutils.TextUtil; import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.AbstractContent; import org.sleuthkit.datamodel.AbstractFile; @@ -298,6 +299,14 @@ class Ingester { String s = ""; try { s = new String(docChunkContentBuf, 0, read, docContentEncoding); + // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index + char[] chars = s.toCharArray(); + for (int i = 0; i < s.length(); i++) { + if (!TextUtil.isValidSolrUTF8(s.charAt(i))) { + chars[i] = '^'; + } + } + s = new String(chars); } catch (UnsupportedEncodingException ex) { logger.log(Level.SEVERE, "Unsupported encoding", ex); //NON-NLS } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java index 589ddc4fa2..aa0bb5d830 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/SolrSearchService.java @@ -36,7 +36,6 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.openide.util.NbBundle; import java.net.InetAddress; import java.util.MissingResourceException; -import org.sleuthkit.autopsy.coreutils.TextUtil; import org.sleuthkit.autopsy.keywordsearchservice.KeywordSearchServiceException; /** @@ -156,13 +155,6 @@ public class SolrSearchService implements KeywordSearchService { documentId += "_" + Long.toString(chunkId); solrFields.replace(Server.Schema.ID.toString(), documentId); - - // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index - for (int i = 0; i < artifactContents.length(); i++) { - if (!TextUtil.isValidSolrUTF8(artifactContents.charAt(i))) { - artifactContents.setCharAt(i, '^'); - } - } StringStream contentStream = new StringStream(artifactContents.toString()); From 781e9c8bc919f1cc4097d46e82189bb23d01b019 Mon Sep 17 00:00:00 2001 From: Eugene Livis Date: Mon, 23 May 2016 10:29:51 -0400 Subject: [PATCH 28/92] Optimized performance by only converting string to char[] if there is a non-UTF8 character --- .../sleuthkit/autopsy/keywordsearch/Ingester.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java index 1f54a3a976..e7325f776c 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/Ingester.java @@ -300,13 +300,20 @@ class Ingester { try { s = new String(docChunkContentBuf, 0, read, docContentEncoding); // Sanitize by replacing non-UTF-8 characters with caret '^' before adding to index - char[] chars = s.toCharArray(); + char[] chars = null; for (int i = 0; i < s.length(); i++) { if (!TextUtil.isValidSolrUTF8(s.charAt(i))) { + // only convert string to char[] if there is a non-UTF8 character + if (chars == null) { + chars = s.toCharArray(); + } chars[i] = '^'; } - } - s = new String(chars); + } + // check if the string was modified (i.e. there was a non-UTF8 character found) + if (chars != null) { + s = new String(chars); + } } catch (UnsupportedEncodingException ex) { logger.log(Level.SEVERE, "Unsupported encoding", ex); //NON-NLS } From 4d6c4240c607c26c1f0e65db76d0657372a347b0 Mon Sep 17 00:00:00 2001 From: jmillman Date: Mon, 23 May 2016 16:37:28 -0400 Subject: [PATCH 29/92] change visualizaion -> view in code, ui, and comments --- .../timeline/TimeLineTopComponent.java | 6 +- .../actions/SaveSnapshotAsReport.java | 2 +- .../events/RefreshRequestedEvent.java | 2 +- .../snapshot/SnapShotReportWriter.java | 2 +- .../timeline/ui/AbstractTimeLineView.java | 6 +- .../timeline/ui/AbstractTimelineChart.java | 38 ++++---- .../autopsy/timeline/ui/Bundle.properties | 16 ++-- .../autopsy/timeline/ui/Bundle_ja.properties | 36 +++---- ...VisualizationPanel.fxml => ViewFrame.fxml} | 2 +- ...VisualizationPanel.java => ViewFrame.java} | 94 +++++++++---------- .../timeline/ui/listvew/ListViewPane.java | 14 +-- 11 files changed, 103 insertions(+), 115 deletions(-) rename Core/src/org/sleuthkit/autopsy/timeline/ui/{VisualizationPanel.fxml => ViewFrame.fxml} (98%) rename Core/src/org/sleuthkit/autopsy/timeline/ui/{VisualizationPanel.java => ViewFrame.java} (87%) diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java index 4d1802bcd5..32479bd0d5 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineTopComponent.java @@ -59,7 +59,7 @@ import org.sleuthkit.autopsy.timeline.explorernodes.EventRootNode; import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar; import org.sleuthkit.autopsy.timeline.ui.StatusBar; import org.sleuthkit.autopsy.timeline.ui.TimeZonePanel; -import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel; +import org.sleuthkit.autopsy.timeline.ui.ViewFrame; import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel; import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane; @@ -250,8 +250,8 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane); SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE); - final VisualizationPanel visualizationPanel = new VisualizationPanel(controller, eventsTree); - final SplitPane mainSplitPane = new SplitPane(leftVBox, visualizationPanel); + final ViewFrame viewFrame = new ViewFrame(controller, eventsTree); + final SplitPane mainSplitPane = new SplitPane(leftVBox, viewFrame); mainSplitPane.setDividerPositions(0); final Scene scene = new Scene(mainSplitPane); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java index e50e1909a8..7649b35705 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/actions/SaveSnapshotAsReport.java @@ -71,7 +71,7 @@ public class SaveSnapshotAsReport extends Action { "Timeline.ModuleName=Timeline", "SaveSnapShotAsReport.action.dialogs.title=Timeline", "SaveSnapShotAsReport.action.name.text=Snapshot Report", - "SaveSnapShotAsReport.action.longText=Save a screen capture of the visualization as a report.", + "SaveSnapShotAsReport.action.longText=Save a screen capture of the current view of the timeline as a report.", "# {0} - report file path", "SaveSnapShotAsReport.ReportSavedAt=Report saved at [{0}]", "SaveSnapShotAsReport.Success=Success", diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/RefreshRequestedEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/events/RefreshRequestedEvent.java index 47b04eb479..903cb55178 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/events/RefreshRequestedEvent.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/events/RefreshRequestedEvent.java @@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.timeline.events; /** * A "local" event published by filteredEventsModel to indicate that the user - * requested that the current visualization be refreshed with out changing any + * requested that the current view be refreshed with out changing any * of the parameters ( to include more up to date tag data for example.) * * This event is not intended for use out side of the Timeline module. diff --git a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java index 75377ff5bb..917d2a0881 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/snapshot/SnapShotReportWriter.java @@ -70,7 +70,7 @@ public class SnapShotReportWriter { * @param zoomParams The ZoomParams in effect when the snapshot was * taken. * @param generationDate The generation Date of the report. - * @param snapshot A snapshot of the visualization to include in the + * @param snapshot A snapshot of the view to include in the * report. */ public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) { diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java index faf55aef15..86fa7ec132 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimeLineView.java @@ -136,7 +136,7 @@ public abstract class AbstractTimeLineView extends BorderPane { /** * Refresh this view based on current state of zoom / filters. Primarily - * this invokes the background VisualizationUpdateTask returned by + * this invokes the background ViewRefreshTask returned by * getUpdateTask(), which derived classes must implement. * * TODO: replace this logic with a javafx Service ? -jm @@ -291,11 +291,11 @@ public abstract class AbstractTimeLineView extends BorderPane { * @throws Exception If there is an unhandled exception during the * background operation */ - @NbBundle.Messages(value = {"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) + @NbBundle.Messages(value = {"ViewRefreshTask.preparing=Analyzing zoom and filter settings"}) @Override protected Boolean call() throws Exception { updateProgress(-1, 1); - updateMessage(Bundle.VisualizationUpdateTask_preparing()); + updateMessage(Bundle.ViewRefreshTask_preparing()); Platform.runLater(() -> { MaskerPane maskerPane = new MaskerPane(); maskerPane.textProperty().bind(messageProperty()); diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java index fc2ee5ca63..571cd3438c 100644 --- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java +++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractTimelineChart.java @@ -57,7 +57,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; /** - * Abstract base class for TimeLineChart based visualizations. + * Abstract base class for TimeLineChart based views. * * @param The type of data plotted along the x axis * @param The type of data plotted along the y axis @@ -74,12 +74,12 @@ public abstract class AbstractTimelineChart of the nodes that are selected in - * this visualization. + * this view. */ protected ObservableList getSelectedNodes() { return selectedNodes; @@ -127,18 +127,18 @@ public abstract class AbstractTimelineChart getXAxis(); /** - * Get the Y-Axis of this Visualization's chart + * Get the Y-Axis of this view's chart * - * @return The vertical axis used by this Visualization's chart + * @return The vertical axis used by this view's chart */ abstract protected Axis getYAxis(); @@ -252,7 +252,7 @@ public abstract class AbstractTimelineChart -