Merge pull request #5155 from wschaeferB/5423-DiscoveryThumbnailViewer

5423 discovery thumbnail viewer
This commit is contained in:
Richard Cordovano 2019-09-17 13:53:10 -04:00 committed by GitHub
commit 11296bbbd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 593 additions and 64 deletions

View File

@ -117,6 +117,10 @@ FileSorter.SortingMethod.filetype.displayName=By file type
FileSorter.SortingMethod.frequency.displayName=By central repo frequency
FileSorter.SortingMethod.keywordlist.displayName=By keyword list names
FileSorter.SortingMethod.parent.displayName=By parent path
# {0} - numberOfInstances
ImageThumbnailPanel.countLabel.text=Number of Instances: {0}
# {0} - fileSize
ImageThumbnailPanel.sizeLabel.text=Size: {0} bytes
ResultsDialog.dialogTitle.text=File search results
ResultsDialog.exitButton.text=Exit
ResultsDialog.searchButton.text=Run another search

View File

@ -116,7 +116,7 @@ class FileSearch {
// Sort and group the results
searchResults.sortGroupsAndFiles();
LinkedHashMap<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
Map<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
for (String groupName : resultHashMap.keySet()) {
groupCache.put(groupName, resultHashMap.get(groupName));
}
@ -251,7 +251,7 @@ class FileSearch {
// Collect everything in the search results
SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod);
searchResults.add(resultFiles);
LinkedHashMap<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
Map<String, List<ResultFile>> resultHashMap = searchResults.toLinkedHashMap();
for (String groupName : resultHashMap.keySet()) {
groupCache.put(groupName, resultHashMap.get(groupName));
}

View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="thumbnailPanel" max="32767" attributes="0"/>
<Component id="countLabel" alignment="0" pref="201" max="32767" attributes="0"/>
<Component id="fileSizeLabel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="thumbnailPanel" min="-2" pref="178" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="fileSizeLabel" min="-2" pref="16" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="countLabel" min="-2" pref="12" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="thumbnailPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="thumbnailLabel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="fileSizeLabel">
</Component>
<Component class="javax.swing.JLabel" name="countLabel">
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,105 @@
/*
* Autopsy
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.filequery;
import java.awt.Color;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
import org.openide.util.NbBundle;
/**
* Class which displays a thumbnail and information for an image file.
*/
public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer<ImageThumbnailWrapper> {
private static final long serialVersionUID = 1L;
private static final Color SELECTION_COLOR = new Color(0, 120, 215);
/**
* Creates new form ImageThumbnailPanel
*/
public ImageThumbnailPanel() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JPanel thumbnailPanel = new javax.swing.JPanel();
thumbnailLabel = new javax.swing.JLabel();
fileSizeLabel = new javax.swing.JLabel();
countLabel = new javax.swing.JLabel();
thumbnailPanel.setLayout(new java.awt.GridBagLayout());
thumbnailPanel.add(thumbnailLabel, new java.awt.GridBagConstraints());
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(thumbnailPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE)
.addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(thumbnailPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel countLabel;
private javax.swing.JLabel fileSizeLabel;
private javax.swing.JLabel thumbnailLabel;
// End of variables declaration//GEN-END:variables
@NbBundle.Messages({"# {0} - fileSize",
"ImageThumbnailPanel.sizeLabel.text=Size: {0} bytes",
"# {0} - numberOfInstances",
"ImageThumbnailPanel.countLabel.text=Number of Instances: {0}"})
@Override
public Component getListCellRendererComponent(JList<? extends ImageThumbnailWrapper> list, ImageThumbnailWrapper value, int index, boolean isSelected, boolean cellHasFocus) {
fileSizeLabel.setText(Bundle.ImageThumbnailPanel_sizeLabel_text(value.getResultFile().getFirstInstance().getSize()));
countLabel.setText(Bundle.ImageThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size()));
thumbnailLabel.setIcon(new ImageIcon(value.getThumbnail()));
setBackground(isSelected ? SELECTION_COLOR : list.getBackground());
return this;
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,0,-117,0,0,1,-22"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="thumbnailListScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="thumbnailList">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="thumbnailListModel" type="code"/>
</Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new ImageThumbnailPanel()" type="code"/>
</Property>
<Property name="layoutOrientation" type="int" value="2"/>
<Property name="visibleRowCount" type="int" value="0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;ImageThumbnailWrapper&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,119 @@
/*
* Autopsy
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.filequery;
import java.util.ArrayList;
import java.util.List;
import javax.swing.DefaultListModel;
import javax.swing.event.ListSelectionListener;
import org.sleuthkit.datamodel.AbstractFile;
/**
* A JPanel to display image thumbnails.
*
*/
public class ImageThumbnailViewer extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private final DefaultListModel<ImageThumbnailWrapper> thumbnailListModel = new DefaultListModel<>();
/**
* Creates new form ImageThumbnailViewer
*/
public ImageThumbnailViewer() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
javax.swing.JScrollPane thumbnailListScrollPane = new javax.swing.JScrollPane();
thumbnailList = new javax.swing.JList<>();
setLayout(new java.awt.BorderLayout());
thumbnailList.setModel(thumbnailListModel);
thumbnailList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
thumbnailList.setCellRenderer(new ImageThumbnailPanel());
thumbnailList.setLayoutOrientation(javax.swing.JList.HORIZONTAL_WRAP);
thumbnailList.setVisibleRowCount(0);
thumbnailListScrollPane.setViewportView(thumbnailList);
add(thumbnailListScrollPane, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JList<ImageThumbnailWrapper> thumbnailList;
// End of variables declaration//GEN-END:variables
/**
* Add a selection listener to the list of thumbnails being displayed.
*
* @param listener The ListSelectionListener to add to the selection model.
*/
void addListSelectionListener(ListSelectionListener listener) {
thumbnailList.getSelectionModel().addListSelectionListener(listener);
}
/**
* Get the list of AbstractFiles which are represented by the selected image
* thumbnail.
*
* @return The list of AbstractFiles which are represented by the selected image
* thumbnail.
*/
List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAllInstances();
}
}
}
/**
* Clear the list of thumbnails being displayed.
*/
void clearViewer() {
synchronized (this) {
thumbnailListModel.removeAllElements();
}
}
/**
* Add the thumbnail for an image to the panel.
*
* @param thumbnailWrapper The object which contains the thumbnail which
* will be displayed.
*/
void addImage(ImageThumbnailWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper);
}
}
}

View File

@ -0,0 +1,72 @@
/*
* Autopsy
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> 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.filequery;
import java.awt.Image;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
/**
* Class to wrap all the information necessary for an image thumbnail to be
* displayed.
*/
public class ImageThumbnailWrapper {
private Image thumbnail;
private final ResultFile resultFile;
/**
* Construct a new ImageThumbnailsWrapper.
*
* @param file The ResultFile which represents the image file which the
* thumbnails were created for.
*/
ImageThumbnailWrapper(ResultFile file) {
this.thumbnail = ImageUtils.getDefaultThumbnail();
this.resultFile = file;
}
/**
* Set the image thumbnail which exists.
*
* @param thumbnail The thumbnail which exists for this file.
*/
void setImageThumbnail(Image thumbnail) {
this.thumbnail = thumbnail;
}
/**
* Get the ResultFile which represents the image file which the thumbnail
* was created for.
*
* @return The ResultFile which represents the image file which the
* thumbnail was created for.
*/
ResultFile getResultFile() {
return resultFile;
}
/**
* Get the thumbnail for the image.
*
* @return The Image which is the thumbnail for the image.
*/
Image getThumbnail() {
return thumbnail;
}
}

View File

@ -238,10 +238,18 @@
<Dimension value="[777, 125]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="instancesPanel">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
@ -269,6 +277,8 @@
<SubComponents>
<Container class="javax.swing.JScrollPane" name="instancesScrollPane">
<AuxValues>
<AuxValue name="JavaCodeGenerator_VariableLocal" type="java.lang.Boolean" value="true"/>
<AuxValue name="JavaCodeGenerator_VariableModifier" type="java.lang.Integer" value="0"/>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
@ -286,6 +296,7 @@
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="instancesListModel" type="code"/>
</Property>
<Property name="selectionMode" type="int" value="0"/>
<Property name="cellRenderer" type="javax.swing.ListCellRenderer" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new InstancesCellRenderer()" type="code"/>
</Property>

View File

@ -33,13 +33,12 @@ import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import javax.swing.event.ListSelectionListener;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.AbstractNode;
import org.openide.nodes.Node;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
@ -51,9 +50,9 @@ import org.sleuthkit.datamodel.TskCoreException;
public class ResultsPanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L;
private final DataResultViewerThumbnail thumbnailViewer;
private final DataResultViewerTable tableViewer;
private final VideoThumbnailViewer videoThumbnailViewer;
private final ImageThumbnailViewer imageThumbnailViewer;
private List<FileSearchFiltering.FileFilter> searchFilters;
private FileSearch.AttributeType groupingAttribute;
private FileGroup.GroupSortingAlgorithm groupSort;
@ -74,22 +73,35 @@ public class ResultsPanel extends javax.swing.JPanel {
public ResultsPanel(ExplorerManager explorerManager, EamDb centralRepo) {
initComponents();
this.centralRepo = centralRepo;
thumbnailViewer = new DataResultViewerThumbnail(explorerManager);
tableViewer = new DataResultViewerTable(explorerManager);
imageThumbnailViewer = new ImageThumbnailViewer();
videoThumbnailViewer = new VideoThumbnailViewer();
// Disable manual editing of page size spinner
videoThumbnailViewer.addListSelectionListener((e) -> {
if (!e.getValueIsAdjusting()) {
populateInstancesList();
}
});
imageThumbnailViewer.addListSelectionListener((e) -> {
if (!e.getValueIsAdjusting()) {
populateInstancesList();
}
});
// Disable manual editing of page size spinner
((JSpinner.DefaultEditor) pageSizeSpinner.getEditor()).getTextField().setEditable(false);
}
/**
* Add a list selection listener to the instances list.
*
* @param listener The ListSelectionListener to add to the instances list.
*/
void addListSelectionListener(ListSelectionListener listener) {
instancesList.addListSelectionListener(listener);
}
/**
* Populate the instances list.
*/
synchronized void populateInstancesList() {
SwingUtilities.invokeLater(() -> {
instancesListModel.removeAllElements();
@ -102,6 +114,12 @@ public class ResultsPanel extends javax.swing.JPanel {
});
}
/**
* 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;
@ -110,9 +128,18 @@ public class ResultsPanel extends javax.swing.JPanel {
}
}
/**
* Get the list of all instances for the the currently selected item in the
* results viewer area.
*
* @return The list of AbstractFiles which are represented by the item
* selected in the results viewer area.
*/
private List<AbstractFile> getInstancesForSelected() {
if (resultType == FileSearchData.FileType.VIDEO) {
return videoThumbnailViewer.getInstancesForSelected();
} else if (resultType == FileSearchData.FileType.IMAGE) {
return imageThumbnailViewer.getInstancesForSelected();
}
return new ArrayList<>();
}
@ -128,23 +155,13 @@ public class ResultsPanel extends javax.swing.JPanel {
populateInstancesList();
currentPage = pageRetrievedEvent.getPageNumber();
updateControls();
thumbnailViewer.resetComponent();
tableViewer.resetComponent();
resultsViewerPanel.remove(thumbnailViewer);
resultsViewerPanel.remove(tableViewer);
resultsViewerPanel.remove(videoThumbnailViewer);
resetResultViewer();
if (pageRetrievedEvent.getType() == FileSearchData.FileType.IMAGE) {
resultsViewerPanel.add(thumbnailViewer);
if (pageRetrievedEvent.getSearchResults().size() > 0) {
List<AbstractFile> filesList = pageRetrievedEvent.getSearchResults().stream().map(file -> file.getFirstInstance()).collect(Collectors.toList());
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new DiscoveryThumbnailChildren(filesList))), true));
} else {
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
}
populateImageViewer(pageRetrievedEvent.getSearchResults());
resultsViewerPanel.add(imageThumbnailViewer);
} else if (pageRetrievedEvent.getType() == FileSearchData.FileType.VIDEO) {
populateVideoViewer(pageRetrievedEvent.getSearchResults());
resultsViewerPanel.add(videoThumbnailViewer);
} else {
resultsViewerPanel.add(tableViewer);
if (pageRetrievedEvent.getSearchResults().size() > 0) {
@ -154,10 +171,22 @@ public class ResultsPanel extends javax.swing.JPanel {
tableViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
}
}
});
resultsViewerPanel.revalidate();
resultsViewerPanel.repaint();
}
);
}
synchronized void populateVideoViewer(List<ResultFile> files) {
/**
* Reset the result viewer and any associate workers to a default empty
* state.
*/
private synchronized void resetResultViewer() {
resultsViewerPanel.remove(imageThumbnailViewer);
tableViewer.resetComponent();
resultsViewerPanel.remove(tableViewer);
resultsViewerPanel.remove(videoThumbnailViewer);
//cancel any unfished thumb workers
for (SwingWorker<Void, Void> thumbWorker : thumbnailWorkers) {
if (!thumbWorker.isDone()) {
@ -167,6 +196,16 @@ public class ResultsPanel extends javax.swing.JPanel {
//clear old thumbnails
thumbnailWorkers.clear();
videoThumbnailViewer.clearViewer();
imageThumbnailViewer.clearViewer();
}
/**
* Populate the video thumbnail viewer, cancelling any thumbnails which are
* currently being created first.
*
* @param files The list of ResultFiles to populate the video viewer with.
*/
synchronized void populateVideoViewer(List<ResultFile> files) {
for (ResultFile file : files) {
VideoThumbnailWorker thumbWorker = new VideoThumbnailWorker(file);
thumbWorker.execute();
@ -175,6 +214,21 @@ public class ResultsPanel extends javax.swing.JPanel {
}
}
/**
* Populate the image thumbnail viewer, cancelling any thumbnails which are
* currently being created first.
*
* @param files The list of ResultFiles to populate the image viewer with.
*/
synchronized void populateImageViewer(List<ResultFile> files) {
for (ResultFile file : files) {
ImageThumbnailWorker thumbWorker = new ImageThumbnailWorker(file);
thumbWorker.execute();
//keep track of thumb worker for possible cancelation
thumbnailWorkers.add(thumbWorker);
}
}
/**
* Subscribe and respond to GroupSelectedEvents.
*
@ -201,8 +255,7 @@ public class ResultsPanel extends javax.swing.JPanel {
currentPage = 0;
updateControls();
videoThumbnailViewer.clearViewer();
thumbnailViewer.resetComponent();
thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
imageThumbnailViewer.clearViewer();
tableViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true));
resultsViewerPanel.revalidate();
resultsViewerPanel.repaint();
@ -262,9 +315,9 @@ public class ResultsPanel extends javax.swing.JPanel {
javax.swing.JLabel gotoPageLabel = new javax.swing.JLabel();
gotoPageField = new javax.swing.JTextField();
javax.swing.JLabel pageSizeLabel = new javax.swing.JLabel();
resultsSplitPane = new javax.swing.JSplitPane();
instancesPanel = new javax.swing.JPanel();
instancesScrollPane = new javax.swing.JScrollPane();
javax.swing.JSplitPane resultsSplitPane = 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<>();
resultsViewerPanel = new javax.swing.JPanel();
@ -374,6 +427,7 @@ public class ResultsPanel extends javax.swing.JPanel {
instancesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.instancesList.border.title"))); // NOI18N
instancesList.setModel(instancesListModel);
instancesList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
instancesList.setCellRenderer(new InstancesCellRenderer());
instancesScrollPane.setViewportView(instancesList);
@ -489,33 +543,82 @@ public class ResultsPanel extends javax.swing.JPanel {
private javax.swing.JLabel currentPageLabel;
private javax.swing.JTextField gotoPageField;
private javax.swing.JList<AbstractFile> instancesList;
private javax.swing.JPanel instancesPanel;
private javax.swing.JScrollPane instancesScrollPane;
private javax.swing.JButton nextPageButton;
private javax.swing.JSpinner pageSizeSpinner;
private javax.swing.JPanel pagingPanel;
private javax.swing.JButton previousPageButton;
private javax.swing.JSplitPane resultsSplitPane;
private javax.swing.JPanel resultsViewerPanel;
// End of variables declaration//GEN-END:variables
/**
* Swing worker to handle the retrieval of video thumbnails and population
* of the Video Thumbnail Viewer.
*/
private class VideoThumbnailWorker extends SwingWorker<Void, Void> {
private final VideoThumbnailsWrapper thumbnailWrapper;
/**
* Construct a new VideoThumbnailWorker.
*
* @param file The ResultFile which represents the video file thumbnails
* are being retrieved for.
*/
VideoThumbnailWorker(ResultFile file) {
thumbnailWrapper = new VideoThumbnailsWrapper(new ArrayList<Image>(), new int[4], file);
videoThumbnailViewer.addRow(thumbnailWrapper);
thumbnailWrapper = new VideoThumbnailsWrapper(file);
videoThumbnailViewer.addVideo(thumbnailWrapper);
}
@Override
protected Void doInBackground() throws Exception {
FileSearch.getVideoThumbnails(thumbnailWrapper);
videoThumbnailViewer.repaint();
return null;
}
@Override
protected void done() {
videoThumbnailViewer.repaint();
}
}
/**
* Swing worker to handle the retrieval of image thumbnails and population
* of the Image Thumbnail Viewer.
*/
private class ImageThumbnailWorker extends SwingWorker<Void, Void> {
private final ImageThumbnailWrapper thumbnailWrapper;
/**
* Construct a new ImageThumbnailWorker.
*
* @param file The ResultFile which represents the image file thumbnails
* are being retrieved for.
*/
ImageThumbnailWorker(ResultFile file) {
thumbnailWrapper = new ImageThumbnailWrapper(file);
imageThumbnailViewer.addImage(thumbnailWrapper);
}
@Override
protected Void doInBackground() throws Exception {
Image thumbnail = ImageUtils.getThumbnail(thumbnailWrapper.getResultFile().getFirstInstance(), ImageUtils.ICON_SIZE_LARGE);
if (thumbnail != null) {
thumbnailWrapper.setImageThumbnail(thumbnail);
}
return null;
}
@Override
protected void done() {
imageThumbnailViewer.repaint();
}
}
/**
* Cell renderer for the instances list.
*/
private class InstancesCellRenderer extends DefaultListCellRenderer {
private static final long serialVersionUID = 1L;

View File

@ -150,8 +150,8 @@ class SearchResults {
*
* @return the grouped and sorted results
*/
LinkedHashMap<String, List<ResultFile>> toLinkedHashMap() throws FileSearchException {
LinkedHashMap<String, List<ResultFile>> map = new LinkedHashMap<>();
Map<String, List<ResultFile>> toLinkedHashMap() throws FileSearchException {
Map<String, List<ResultFile>> map = new LinkedHashMap<>();
// Sort the groups and files
sortGroupsAndFiles();

View File

@ -47,7 +47,7 @@
<Component id="fileSizeLabel" pref="19" max="32767" attributes="0"/>
<Component id="countLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>

View File

@ -35,7 +35,7 @@ import org.openide.util.NbBundle.Messages;
final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer<VideoThumbnailsWrapper> {
private static final int GAP_SIZE = 4;
private static final Color SELECTION_COLOR = new Color(100, 200, 255);
private static final Color SELECTION_COLOR = new Color(0,120,215);
private static final long serialVersionUID = 1L;
/**
@ -124,7 +124,7 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 19, Short.MAX_VALUE)
.addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents

View File

@ -50,21 +50,12 @@ public class VideoThumbnailViewer extends javax.swing.JPanel {
}
/**
* Get the AbstractFile associated with the selected thumbnails.
* Get the list of AbstractFiles which are represented by the selected Video
* thumbnails.
*
* @return The AbstractFile associated with the selected thumbnails, or null
* if no thumbnails are selected.
* @return The list of AbstractFiles which are represented by the selected Video
* thumbnails.
*/
AbstractFile getSelectedFile() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) {
return null;
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getFirstInstance();
}
}
}
List<AbstractFile> getInstancesForSelected() {
synchronized (this) {
if (thumbnailList.getSelectedIndex() == -1) {
@ -90,7 +81,7 @@ public class VideoThumbnailViewer extends javax.swing.JPanel {
* @param thumbnailWrapper The object which contains the thumbnails which
* will be displayed.
*/
void addRow(VideoThumbnailsWrapper thumbnailWrapper) {
void addVideo(VideoThumbnailsWrapper thumbnailWrapper) {
synchronized (this) {
thumbnailListModel.addElement(thumbnailWrapper);
}

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.filequery;
import java.awt.Image;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -28,23 +29,19 @@ import java.util.List;
*/
final class VideoThumbnailsWrapper {
private List<Image> thumbnails;
private final List<Image> thumbnails;
private final ResultFile resultFile;
private int[] timeStamps;
/**
* Construct a new VideoThumbnailsWrapper.
*
* @param thumbnails The list of Images which are the thumbnails for the
* video.
* @param timeStamps An array containing the time in milliseconds into the
* video that each thumbnail created for.
* @param file The ResultFile which represents the video file which
* the thumbnails were created for.
* @param file The ResultFile which represents the video file which the
* thumbnails were created for.
*/
VideoThumbnailsWrapper(List<Image> thumbnails, int[] timeStamps, ResultFile file) {
this.thumbnails = thumbnails;
this.timeStamps = timeStamps;
VideoThumbnailsWrapper(ResultFile file) {
this.thumbnails = new ArrayList<>();
this.timeStamps = new int[4];
this.resultFile = file;
}
@ -79,9 +76,17 @@ final class VideoThumbnailsWrapper {
return Collections.unmodifiableList(thumbnails);
}
/**
* Set the thumbnails and their associated time stamps.
*
* @param videoThumbnails The list of Images which are the thumbnails for
* the video.
* @param framePositions An array containing the time in milliseconds into
* the video that each thumbnail created for.
*/
void setThumbnails(List<Image> videoThumbnails, int[] framePositions) {
this.thumbnails = videoThumbnails;
this.timeStamps = framePositions;
this.thumbnails.addAll(videoThumbnails);
this.timeStamps = framePositions.clone();
}
}