5369 add panel to view files with same MD5

This commit is contained in:
William Schaefer 2019-08-22 15:33:35 -04:00
parent ac2a84f0d1
commit ae0235b422
7 changed files with 180 additions and 33 deletions

View File

@ -56,3 +56,4 @@ ResultsPanel.currentPageLabel.text=Page: -
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page size:
ResultsPanel.duplicatesList.border.title=Duplicate Files

View File

@ -165,4 +165,5 @@ ResultsPanel.invalidPageNumber.title=Invalid Page Number
ResultsPanel.pageControlsLabel.text=Pages:
ResultsPanel.gotoPageLabel.text=Go to Page:
ResultsPanel.pageSizeLabel.text=Page size:
ResultsPanel.duplicatesList.border.title=Duplicate Files
SearchNode.getName.text=Search Result

View File

@ -345,7 +345,8 @@ class FileSearch {
*/
@NbBundle.Messages({"# {0} - file name",
"FileSearch.genVideoThumb.progress.text=extracting temporary file {0}"})
static VideoThumbnailsWrapper getVideoThumbnails(AbstractFile file) {
static VideoThumbnailsWrapper getVideoThumbnails(ResultFile resultFile) {
AbstractFile file = resultFile.getAbstractFile();
//Currently this method always creates the thumbnails
java.io.File tempFile;
try {
@ -357,7 +358,7 @@ class FileSearch {
0,
0,
0};
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, file);
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, resultFile);
}
if (tempFile.exists() == false || tempFile.length() < file.getSize()) {
ProgressHandle progress = ProgressHandle.createHandle(Bundle.FileSearch_genVideoThumb_progress_text(file.getName()));
@ -370,7 +371,7 @@ class FileSearch {
0,
0,
0};
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, file);
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, resultFile);
}
ContentUtils.writeToFile(file, tempFile, progress, null, true);
} catch (IOException ex) {
@ -390,7 +391,7 @@ class FileSearch {
0,
0,
0};
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, file);
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, resultFile);
}
double fps = videoFile.get(5); // gets frame per second
double totalFrames = videoFile.get(7); // gets total frames
@ -401,7 +402,7 @@ class FileSearch {
0,
0,
0};
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, file);
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, resultFile);
}
if (Thread.interrupted()) {
int[] framePositions = new int[]{
@ -409,7 +410,7 @@ class FileSearch {
0,
0,
0};
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, file);
return new VideoThumbnailsWrapper(createDefaultThumbnailList(), framePositions, resultFile);
}
double duration = 1000 * (totalFrames / fps); //total milliseconds
@ -464,11 +465,11 @@ class FileSearch {
bufferedImage.getRaster().setDataElements(0, 0, matrixColumns, matrixRows, data);
if (Thread.interrupted()) {
return new VideoThumbnailsWrapper(videoThumbnails, framePositions, file);
return new VideoThumbnailsWrapper(videoThumbnails, framePositions, resultFile);
}
videoThumbnails.add(ScalrWrapper.resizeFast(bufferedImage, ImageUtils.ICON_SIZE_LARGE));
}
return new VideoThumbnailsWrapper(videoThumbnails, framePositions, file);
return new VideoThumbnailsWrapper(videoThumbnails, framePositions, resultFile);
} finally {
videoFile.release(); // close the file}
}

View File

@ -17,15 +17,16 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="pagingPanel" max="32767" attributes="0"/>
<Component id="resultsViewerPanel" alignment="0" max="32767" attributes="0"/>
<Component id="resultsSplitPane" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="pagingPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="resultsViewerPanel" pref="62" max="32767" attributes="0"/>
<Component id="resultsSplitPane" pref="199" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -60,7 +61,7 @@
<Component id="pageSizeLabel" min="-2" pref="52" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pageSizeSpinner" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="81" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -228,9 +229,82 @@
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="resultsViewerPanel">
<Container class="javax.swing.JSplitPane" name="resultsSplitPane">
<Properties>
<Property name="dividerLocation" type="int" value="250"/>
<Property name="orientation" type="int" value="0"/>
<Property name="resizeWeight" type="double" value="0.9"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[777, 125]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="duplicatesPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="775" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="duplicatesScrollPane" alignment="0" pref="775" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="52" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="duplicatesScrollPane" alignment="0" pref="52" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="duplicatesScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="duplicatesList">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
<TitledBorder title="Duplicate Files">
<ResourceString PropertyName="titleX" bundle="org/sleuthkit/autopsy/filequery/Bundle.properties" key="ResultsPanel.duplicatesList.border.title" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</TitledBorder>
</Border>
</Property>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="duplicatesListModel" type="code"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="resultsViewerPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -22,6 +22,8 @@ import com.google.common.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.DefaultListModel;
import javax.swing.DefaultListSelectionModel;
import javax.swing.JOptionPane;
import javax.swing.JSpinner;
import javax.swing.SwingUtilities;
@ -37,6 +39,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.directorytree.DataResultFilterNode;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Panel for displaying of file discovery results and handling the paging of
@ -60,6 +63,7 @@ public class ResultsPanel extends javax.swing.JPanel {
private int groupSize = 0;
private PageWorker pageWorker;
private final List<SwingWorker> thumbnailWorkers = new ArrayList<>();
private final DefaultListModel<String> duplicatesListModel = new DefaultListModel<>();
/**
* Creates new form ResultsPanel.
@ -71,6 +75,20 @@ public class ResultsPanel extends javax.swing.JPanel {
tableViewer = new DataResultViewerTable(explorerManager);
videoThumbnailViewer = new VideoThumbnailViewer();
// Disable manual editing of page size spinner
videoThumbnailViewer.addListSelectionListener((e) -> {
SwingUtilities.invokeLater(() -> {
duplicatesListModel.removeAllElements();
for (AbstractFile file : getSelectedDuplicates()) {
String name;
try {
name = file.getUniquePath();
} catch (TskCoreException ingored) {
name = file.getParentPath() + "/" + file.getName();
}
duplicatesListModel.addElement(name);
}
});
});
((JSpinner.DefaultEditor) pageSizeSpinner.getEditor()).getTextField().setEditable(false);
}
@ -82,6 +100,13 @@ public class ResultsPanel extends javax.swing.JPanel {
return videoThumbnailViewer.getSelectedFile();
}
private List<AbstractFile> getSelectedDuplicates() {
if (resultType == FileSearchData.FileType.VIDEO) {
return videoThumbnailViewer.getDuplicatesForSelected();
}
return new ArrayList<>();
}
/**
* Subscribe and respond to PageRetrievedEvents.
*
@ -226,6 +251,10 @@ 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();
duplicatesPanel = new javax.swing.JPanel();
duplicatesScrollPane = new javax.swing.JScrollPane();
duplicatesList = new javax.swing.JList<>();
resultsViewerPanel = new javax.swing.JPanel();
pagingPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
@ -307,7 +336,7 @@ public class ResultsPanel extends javax.swing.JPanel {
.addComponent(pageSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 52, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pageSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(81, Short.MAX_VALUE))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
pagingPanelLayout.setVerticalGroup(
pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -327,21 +356,49 @@ public class ResultsPanel extends javax.swing.JPanel {
.addGap(4, 4, 4))
);
resultsSplitPane.setDividerLocation(250);
resultsSplitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
resultsSplitPane.setResizeWeight(0.9);
resultsSplitPane.setPreferredSize(new java.awt.Dimension(777, 125));
duplicatesList.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(ResultsPanel.class, "ResultsPanel.duplicatesList.border.title"))); // NOI18N
duplicatesList.setModel(duplicatesListModel);
duplicatesScrollPane.setViewportView(duplicatesList);
javax.swing.GroupLayout duplicatesPanelLayout = new javax.swing.GroupLayout(duplicatesPanel);
duplicatesPanel.setLayout(duplicatesPanelLayout);
duplicatesPanelLayout.setHorizontalGroup(
duplicatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 775, Short.MAX_VALUE)
.addGroup(duplicatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(duplicatesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 775, Short.MAX_VALUE))
);
duplicatesPanelLayout.setVerticalGroup(
duplicatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 52, Short.MAX_VALUE)
.addGroup(duplicatesPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(duplicatesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 52, Short.MAX_VALUE))
);
resultsSplitPane.setRightComponent(duplicatesPanel);
resultsViewerPanel.setLayout(new java.awt.BorderLayout());
resultsSplitPane.setLeftComponent(resultsViewerPanel);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(pagingPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(resultsViewerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(resultsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(pagingPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(resultsViewerPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 62, Short.MAX_VALUE))
.addComponent(resultsSplitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 199, Short.MAX_VALUE)
.addGap(0, 0, 0))
);
}// </editor-fold>//GEN-END:initComponents
@ -418,11 +475,15 @@ public class ResultsPanel extends javax.swing.JPanel {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel currentPageLabel;
private javax.swing.JList<String> duplicatesList;
private javax.swing.JPanel duplicatesPanel;
private javax.swing.JScrollPane duplicatesScrollPane;
private javax.swing.JTextField gotoPageField;
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
@ -437,7 +498,7 @@ public class ResultsPanel extends javax.swing.JPanel {
@Override
protected Void doInBackground() throws Exception {
thumbnailWrapper = FileSearch.getVideoThumbnails(file.getAbstractFile());
thumbnailWrapper = FileSearch.getVideoThumbnails(file);
return null;
}

View File

@ -18,6 +18,8 @@
*/
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;
@ -51,13 +53,21 @@ public class VideoThumbnailViewer extends javax.swing.JPanel {
* Get the AbstractFile associated with the selected thumbnails.
*
* @return The AbstractFile associated with the selected thumbnails, or null
* if no thumnails are selected.
* if no thumbnails are selected.
*/
AbstractFile getSelectedFile() {
if (thumbnailList.getSelectedIndex() == -1) {
return null;
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getAbstractFile();
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getAbstractFile();
}
}
List<AbstractFile> getDuplicatesForSelected() {
if (thumbnailList.getSelectedIndex() == -1) {
return new ArrayList<>();
} else {
return thumbnailListModel.getElementAt(thumbnailList.getSelectedIndex()).getResultFile().getDuplicates();
}
}

View File

@ -21,17 +21,16 @@ package org.sleuthkit.autopsy.filequery;
import java.awt.Image;
import java.util.Collections;
import java.util.List;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Class to wrap all the information necessary for video thumbnails to be
* displayed.
*/
public class VideoThumbnailsWrapper {
final class VideoThumbnailsWrapper {
private final List<Image> thumbnails;
private final AbstractFile abstractFile;
private final ResultFile resultFile;
private final int[] timeStamps;
/**
@ -41,24 +40,24 @@ public class VideoThumbnailsWrapper {
* video.
* @param timeStamps An array containing the time in milliseconds into the
* video that each thumbnail created for.
* @param file The AbstractFile which represents the video file which
* @param file The ResultFile which represents the video file which
* the thumbnails were created for.
*/
public VideoThumbnailsWrapper(List<Image> thumbnails, int[] timeStamps, AbstractFile file) {
VideoThumbnailsWrapper(List<Image> thumbnails, int[] timeStamps, ResultFile file) {
this.thumbnails = thumbnails;
this.timeStamps = timeStamps;
this.abstractFile = file;
this.resultFile = file;
}
/**
* Get the AbstractFile which represents the video file which the thumbnails
* Get the ResultFile which represents the video file which the thumbnails
* were created for.
*
* @return The AbstractFile which represents the video file which the
* @return The ResultFile which represents the video file which the
* thumbnails were created for.
*/
AbstractFile getAbstractFile() {
return abstractFile;
ResultFile getResultFile() {
return resultFile;
}
/**
@ -79,9 +78,9 @@ public class VideoThumbnailsWrapper {
*/
String getFilePath() {
try {
return abstractFile.getUniquePath();
return resultFile.getAbstractFile().getUniquePath();
} catch (TskCoreException ingored) {
return abstractFile.getParentPath() + "/" + abstractFile.getName();
return resultFile.getAbstractFile().getParentPath() + "/" + resultFile.getAbstractFile().getName();
}
}