diff --git a/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml b/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml
index ef5da81277..cde1d1b91d 100644
--- a/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml
+++ b/Core/src/org/sleuthkit/autopsy/core/discoveryWsmode.xml
@@ -3,7 +3,7 @@
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties
index db3f094fc7..6adf34c316 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties
+++ b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties
@@ -48,14 +48,12 @@ ResultsPanel.currentPageLabel.text=Page: -
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page Size:
-ResultsPanel.instancesList.border.title=Instances
DiscoveryExtractAction.title.extractFiles.text=Extract File
FileSearchPanel.includeRadioButton.text=Include
FileSearchPanel.excludeRadioButton.text=Exclude
FileSearchPanel.knownFilesCheckbox.toolTipText=
FileSearchPanel.knownFilesCheckbox.text=Hide known files
GroupListPanel.groupKeyList.border.title=Groups
-ResultsPanel.resultsSplitPane.toolTipText=
FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
@@ -66,3 +64,4 @@ DiscoveryDialog.videosButton.text=Videos
DiscoveryDialog.imagesButton.text=Images
DiscoveryDialog.searchButton.text=Show
DiscoveryDialog.cancelButton.text=Cancel
+DetailsPanel.instancesList.border.title=Instances
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED
index f69686a0ea..b7dd8cf30a 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED
+++ b/Core/src/org/sleuthkit/autopsy/discovery/Bundle.properties-MERGED
@@ -200,14 +200,12 @@ ResultsPanel.openInExternalViewer.name=Open in External Viewer
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page Size:
-ResultsPanel.instancesList.border.title=Instances
DiscoveryExtractAction.title.extractFiles.text=Extract File
FileSearchPanel.includeRadioButton.text=Include
FileSearchPanel.excludeRadioButton.text=Exclude
FileSearchPanel.knownFilesCheckbox.toolTipText=
FileSearchPanel.knownFilesCheckbox.text=Hide known files
GroupListPanel.groupKeyList.border.title=Groups
-ResultsPanel.resultsSplitPane.toolTipText=
FileSearchPanel.stepThreeLabel.text=Step 3: Choose display settings
DocumentPanel.fileSizeLabel.toolTipText=
DocumentPanel.isDeletedLabel.toolTipText=
@@ -218,6 +216,7 @@ DiscoveryDialog.videosButton.text=Videos
DiscoveryDialog.imagesButton.text=Images
DiscoveryDialog.searchButton.text=Show
DiscoveryDialog.cancelButton.text=Cancel
+DetailsPanel.instancesList.border.title=Instances
ResultsPanel.unableToCreate.text=Unable to create summary.
ResultsPanel.viewFileInDir.name=View File in Directory
VideoThumbnailPanel.bytes.text=bytes
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.form
index 058ed2dd31..32f78a9645 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.form
@@ -16,9 +16,9 @@
-
+
-
+
@@ -26,7 +26,7 @@
-
+
@@ -34,10 +34,100 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.java
index c49cbe0edc..46be9cca8c 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DetailsPanel.java
@@ -1,11 +1,48 @@
/*
- * 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
+ *
+ * Copyright 2020 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.discovery;
+import com.google.common.eventbus.Subscribe;
+import java.awt.Component;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import javax.swing.DefaultListCellRenderer;
+import javax.swing.DefaultListModel;
+import javax.swing.JList;
+import javax.swing.JPopupMenu;
+import javax.swing.SwingUtilities;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.sleuthkit.autopsy.actions.AddContentTagAction;
+import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
+import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
+import org.sleuthkit.autopsy.datamodel.FileNode;
+import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
+import org.sleuthkit.autopsy.directorytree.ViewContextAction;
+import org.sleuthkit.autopsy.modules.hashdatabase.AddContentToHashDbAction;
+import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
+import org.sleuthkit.datamodel.AbstractFile;
+import org.sleuthkit.datamodel.TskCoreException;
/**
*
@@ -13,7 +50,11 @@ import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
*/
public class DetailsPanel extends javax.swing.JPanel {
+ private static final long serialVersionUID = 1L;
+
private final DataContentPanel dataContentPanel;
+ private final DefaultListModel instancesListModel = new DefaultListModel<>();
+ private ListSelectionListener listener = null;
/**
* Creates new form DetailsPanel
@@ -21,7 +62,95 @@ public class DetailsPanel extends javax.swing.JPanel {
public DetailsPanel() {
initComponents();
dataContentPanel = DataContentPanel.createInstance();
- detailsSplitPane.setBottomComponent(dataContentPanel);
+
+ detailsSplitPane.setBottomComponent(dataContentPanel);
+ //Add the context menu when right clicking
+ instancesList.addMouseListener(new MouseAdapter() {
+ @Override
+ public void mousePressed(MouseEvent e) {
+ if (SwingUtilities.isRightMouseButton(e)) {
+ SwingUtilities.invokeLater(() -> {
+ instancesList.setSelectedIndex(instancesList.locationToIndex(e.getPoint()));
+ Set files = new HashSet<>();
+ files.add(instancesList.getSelectedValue());
+ JPopupMenu menu = new JPopupMenu();
+ menu.add(new ViewContextAction(Bundle.ResultsPanel_viewFileInDir_name(), instancesList.getSelectedValue()));
+ menu.add(new ExternalViewerAction(Bundle.ResultsPanel_openInExternalViewer_name(), new FileNode(instancesList.getSelectedValue())));
+ menu.add(ViewFileInTimelineAction.createViewFileAction(instancesList.getSelectedValue()));
+ menu.add(new DiscoveryExtractAction(files));
+ menu.add(AddContentTagAction.getInstance().getMenuForContent(files));
+ menu.add(DeleteFileContentTagAction.getInstance().getMenuForFiles(files));
+ menu.add(AddContentToHashDbAction.getInstance().getMenuForFiles(files));
+ menu.show(instancesList, e.getPoint().x, e.getPoint().y);
+ });
+ }
+ }
+ });
+ listener = new ListSelectionListener() {
+ @Override
+ public void valueChanged(ListSelectionEvent e) {
+ if (!e.getValueIsAdjusting()) {
+ SwingUtilities.invokeLater(() -> {
+ AbstractFile file = getSelectedFile();
+ if (file != null) {
+ dataContentPanel.setNode(new TableFilterNode(new FileNode(file), false));
+ } else {
+ dataContentPanel.setNode(null);
+ }
+ });
+ }
+ }
+ };
+ instancesList.addListSelectionListener(listener);
+ }
+
+ @Subscribe
+ void handleClearSelectionListener(DiscoveryEventUtils.ClearInstanceSelectionEvent clearEvent) {
+ instancesList.clearSelection();
+ }
+
+ /**
+ * Populate the instances list.
+ */
+ @Subscribe
+ synchronized void handlePopulateInstancesListEvent(DiscoveryEventUtils.PopulateInstancesListEvent populateEvent) {
+ SwingUtilities.invokeLater(() -> {
+ List files = populateEvent.getInstances();
+ if (files.isEmpty()) {
+ //if there are no files currently remove the current items without removing listener to cause content viewer to reset
+ instancesListModel.removeAllElements();
+ //send fade out event
+ DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false));
+ } else {
+ //remove listener so content viewer node is not set multiple times
+ instancesList.removeListSelectionListener(listener);
+ instancesListModel.removeAllElements();
+ for (AbstractFile file : files) {
+ instancesListModel.addElement(file);
+ }
+ //add listener back to allow selection of first index to cause content viewer node to be set
+ instancesList.addListSelectionListener(listener);
+ if (!instancesListModel.isEmpty()) {
+ instancesList.setSelectedIndex(0);
+ }
+ //send fade in event
+ DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true));
+ }
+ });
+ }
+
+ /**
+ * Get the AbstractFile for the item currently selected in the instances
+ * list.
+ *
+ * @return The AbstractFile which is currently selected.
+ */
+ synchronized AbstractFile getSelectedFile() {
+ if (instancesList.getSelectedIndex() == -1) {
+ return null;
+ } else {
+ return instancesListModel.getElementAt(instancesList.getSelectedIndex());
+ }
}
/**
@@ -34,27 +163,91 @@ public class DetailsPanel extends javax.swing.JPanel {
private void initComponents() {
detailsSplitPane = new javax.swing.JSplitPane();
+ javax.swing.JPanel instancesPanel = new javax.swing.JPanel();
+ javax.swing.JScrollPane instancesScrollPane = new javax.swing.JScrollPane();
+ instancesList = new javax.swing.JList<>();
+ detailsSplitPane.setDividerLocation(80);
detailsSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
+ detailsSplitPane.setMinimumSize(new java.awt.Dimension(200, 0));
+ detailsSplitPane.setPreferredSize(new java.awt.Dimension(700, 500));
+
+ instancesPanel.setMinimumSize(new java.awt.Dimension(0, 60));
+ instancesPanel.setPreferredSize(new java.awt.Dimension(700, 80));
+
+ instancesScrollPane.setPreferredSize(new java.awt.Dimension(775, 60));
+
+ instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(DetailsPanel.class, "DetailsPanel.instancesList.border.title"))); // NOI18N
+ instancesList.setModel(instancesListModel);
+ instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
+ instancesList.setCellRenderer(new InstancesCellRenderer());
+ instancesList.setVisibleRowCount(2);
+ instancesScrollPane.setViewportView(instancesList);
+
+ javax.swing.GroupLayout instancesPanelLayout = new javax.swing.GroupLayout(instancesPanel);
+ instancesPanel.setLayout(instancesPanelLayout);
+ instancesPanelLayout.setHorizontalGroup(
+ instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 775, Short.MAX_VALUE)
+ .addGroup(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ );
+ instancesPanelLayout.setVerticalGroup(
+ instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGap(0, 79, Short.MAX_VALUE)
+ .addGroup(instancesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, instancesPanelLayout.createSequentialGroup()
+ .addGap(0, 0, 0)
+ .addComponent(instancesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 79, Short.MAX_VALUE)))
+ );
+
+ detailsSplitPane.setTopComponent(instancesPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGap(0, 667, Short.MAX_VALUE)
+ .addGap(0, 777, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(detailsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 667, Short.MAX_VALUE))
+ .addComponent(detailsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 402, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(detailsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 402, Short.MAX_VALUE))
+ .addComponent(detailsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
}// //GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane detailsSplitPane;
+ private javax.swing.JList instancesList;
// End of variables declaration//GEN-END:variables
+
+ /**
+ * Cell renderer for the instances list.
+ */
+ private class InstancesCellRenderer extends DefaultListCellRenderer {
+
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public Component getListCellRendererComponent(JList> list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
+ super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
+ String name = "";
+ if (value instanceof AbstractFile) {
+ AbstractFile file = (AbstractFile) value;
+ try {
+ name = file.getUniquePath();
+ } catch (TskCoreException ingored) {
+ name = file.getParentPath() + "/" + file.getName();
+ }
+
+ }
+ setText(name);
+ return this;
+ }
+
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form
index 0f36d5ae95..4f74b9ff75 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.form
@@ -202,11 +202,14 @@
-
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java
index 9a1c45ccaa..70ca36cb2c 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryDialog.java
@@ -37,11 +37,19 @@ final class DiscoveryDialog extends javax.swing.JDialog {
private static final Color SELECTED_COLOR = new Color(216, 230, 242);
private static final Color UNSELECTED_COLOR = new Color(240, 240, 240);
private SearchWorker searchWorker = null;
+ private static DiscoveryDialog discoveryDialog;
- DiscoveryDialog() {
+ private DiscoveryDialog() {
this(null, true);
}
+ public static synchronized DiscoveryDialog getDiscoveryDialogInstance() {
+ if (discoveryDialog == null) {
+ discoveryDialog = new DiscoveryDialog();
+ }
+ return discoveryDialog;
+ }
+
/**
* Creates new form DiscoveryDialog
*/
@@ -100,7 +108,7 @@ final class DiscoveryDialog extends javax.swing.JDialog {
jPanel1 = new javax.swing.JPanel();
searchButton = new javax.swing.JButton();
errorLabel = new javax.swing.JLabel();
- cancelButton = new javax.swing.JButton();
+ javax.swing.JButton cancelButton = new javax.swing.JButton();
setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
@@ -180,7 +188,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
errorLabel.setForeground(new java.awt.Color(255, 0, 0));
org.openide.awt.Mnemonics.setLocalizedText(cancelButton, org.openide.util.NbBundle.getMessage(DiscoveryDialog.class, "DiscoveryDialog.cancelButton.text")); // NOI18N
- cancelButton.setEnabled(false);
cancelButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cancelButtonActionPerformed(evt);
@@ -270,12 +277,9 @@ final class DiscoveryDialog extends javax.swing.JDialog {
if (tc.isOpened() == false) {
tc.open();
-// tc.updateSearchSettings();
-// displayErrorMessage(tc);
}
tc.resetTopComponent();
List filters = filterPanel.getFilters();
-// enableSearch(false);
DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.SearchStartedEvent(filterPanel.getSelectedType()));
// Get the grouping attribute and group sorting method
@@ -300,10 +304,15 @@ final class DiscoveryDialog extends javax.swing.JDialog {
tc.requestActive();
}//GEN-LAST:event_searchButtonActionPerformed
+
private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
-// cancelSearch();
+ this.setVisible(false);
}//GEN-LAST:event_cancelButtonActionPerformed
+ void cancelSearch() {
+ filterPanel.cancelSearch();
+ }
+
/**
* The settings are valid so enable the Search button
*/
@@ -324,7 +333,6 @@ final class DiscoveryDialog extends javax.swing.JDialog {
}
// Variables declaration - do not modify//GEN-BEGIN:variables
- private javax.swing.JButton cancelButton;
private javax.swing.JButton documentsButton;
private javax.swing.JLabel errorLabel;
private javax.swing.JButton imagesButton;
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryEventUtils.java b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryEventUtils.java
index bc0c8e32df..4151a09c4b 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryEventUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryEventUtils.java
@@ -1,7 +1,7 @@
/*
* Autopsy
*
- * Copyright 2019 Basis Technology Corp.
+ * Copyright 2019-2020 Basis Technology Corp.
* Contact: carrier sleuthkit org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +24,7 @@ import java.util.List;
import java.util.Map;
import org.sleuthkit.autopsy.discovery.FileSearch.GroupKey;
import org.sleuthkit.autopsy.discovery.FileSearchData.FileType;
+import org.sleuthkit.datamodel.AbstractFile;
/**
* Class to handle event bus and events for file discovery tool.
@@ -70,6 +71,36 @@ final class DiscoveryEventUtils {
}
}
+ static final class ClearInstanceSelectionEvent {
+
+ /**
+ * Construct a new ClearInstanceSelectionEvent.
+ */
+ ClearInstanceSelectionEvent() {
+ //no arg constructor
+ }
+ }
+
+ static final class PopulateInstancesListEvent {
+
+ private final List instances;
+
+ /**
+ * Construct a new PopulateInstancesListEvent.
+ */
+ PopulateInstancesListEvent(List files) {
+ instances = files;
+ //no arg constructor
+ }
+
+ /**
+ * @return the instances
+ */
+ List getInstances() {
+ return Collections.unmodifiableList(instances);
+ }
+ }
+
/**
* Event to signal the completion of a search being performed.
*/
@@ -332,4 +363,22 @@ final class DiscoveryEventUtils {
}
}
+
+ static class DetailsVisibleEvent {
+
+ private final boolean showDetailsArea;
+
+ DetailsVisibleEvent(boolean isVisible) {
+ showDetailsArea = isVisible;
+ //no arg constructor
+ }
+
+ /**
+ * @return the showDetailsArea
+ */
+ boolean isShowDetailsArea() {
+ return showDetailsArea;
+ }
+ }
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form
index df7946a5c2..b5cffa4a96 100644
--- a/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form
+++ b/Core/src/org/sleuthkit/autopsy/discovery/DiscoveryTopComponent.form
@@ -2,8 +2,11 @@