From ab6abbe29a7e9554bdc1a8f865079d970c1d4aed Mon Sep 17 00:00:00 2001 From: William Schaefer Date: Mon, 12 Aug 2019 13:07:30 -0400 Subject: [PATCH] 5372 initial video thumbnail viewer --- .../autopsy/filequery/Bundle.properties | 1 + .../filequery/Bundle.properties-MERGED | 1 + .../autopsy/filequery/DiscoveryEvents.java | 9 +- .../autopsy/filequery/FileGroup.java | 4 +- .../autopsy/filequery/FileSearch.java | 17 ++-- .../filequery/FileSearchTestAction.java | 14 ++- .../autopsy/filequery/PageWorker.java | 2 +- .../autopsy/filequery/ResultFile.java | 34 ++++++- .../autopsy/filequery/ResultsPanel.form | 34 +++---- .../autopsy/filequery/ResultsPanel.java | 51 +++++++--- .../autopsy/filequery/SearchResults.java | 6 +- .../autopsy/filequery/ThumbnailPanel.form | 61 ++++++++++++ .../autopsy/filequery/ThumbnailPanel.java | 92 +++++++++++++++++++ .../filequery/VideoThumbnailViewer.form | 18 ++++ .../filequery/VideoThumbnailViewer.java | 68 ++++++++++++++ 15 files changed, 344 insertions(+), 68 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.java create mode 100644 Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form create mode 100644 Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties index 7bd6347392..4d95cba42c 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties @@ -56,3 +56,4 @@ ResultsPanel.currentPageLabel.text=Page: - ResultsPanel.pageControlsLabel.text=Pages: ResultsPanel.gotoPageLabel.text=Go to Page: ResultsPanel.pageSizeLabel.text=Page size: +ThumbnailPanel.fileInfoLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index 1ab7f6cf88..373683b22c 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -164,3 +164,4 @@ ResultsPanel.pageControlsLabel.text=Pages: ResultsPanel.gotoPageLabel.text=Go to Page: ResultsPanel.pageSizeLabel.text=Page size: SearchNode.getName.text=Search Result +ThumbnailPanel.fileInfoLabel.text= diff --git a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEvents.java b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEvents.java index 3209918445..ae3bd479a2 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEvents.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/DiscoveryEvents.java @@ -24,10 +24,9 @@ import java.util.List; import java.util.LinkedHashMap; import java.util.Map; import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; -import org.sleuthkit.datamodel.AbstractFile; /** - * Class to handle envent bus and events for file discovery tool. + * Class to handle event bus and events for file discovery tool. */ final class DiscoveryEvents { @@ -156,7 +155,7 @@ final class DiscoveryEvents { */ static final class PageRetrievedEvent { - private final List results; + private final List results; private final int page; private final FileType resultType; @@ -167,7 +166,7 @@ final class DiscoveryEvents { * @param page The number of the page which was retrieved. * @param results The list of files in the page retrieved. */ - PageRetrievedEvent(FileType resultType, int page, List results) { + PageRetrievedEvent(FileType resultType, int page, List results) { this.results = results; this.page = page; this.resultType = resultType; @@ -178,7 +177,7 @@ final class DiscoveryEvents { * * @return The list of files in the page retrieved. */ - List getSearchResults() { + List getSearchResults() { return Collections.unmodifiableList(results); } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java index ae469c1846..e4800899ee 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileGroup.java @@ -77,8 +77,8 @@ class FileGroup implements Comparable { * * @return List of abstract files */ - List getAbstractFiles() { - return files.stream().map(file -> file.getAbstractFile()).collect(Collectors.toList()); + List getAbstractFiles() { + return Collections.unmodifiableList(files); } /** diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index 8678bcdcb0..b029421b95 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -57,7 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException; class FileSearch { private final static Logger logger = Logger.getLogger(FileSearch.class.getName()); - private static final Cache> groupCache = CacheBuilder.newBuilder().build(); + private static final Cache> groupCache = CacheBuilder.newBuilder().build(); /** * Run the file search and returns the SearchResults object for debugging. @@ -104,7 +104,7 @@ class FileSearch { // Sort and group the results searchResults.sortGroupsAndFiles(); - LinkedHashMap> resultHashMap = searchResults.toLinkedHashMap(); + LinkedHashMap> resultHashMap = searchResults.toLinkedHashMap(); for (String groupName : resultHashMap.keySet()) { groupCache.put(groupName, resultHashMap.get(groupName)); } @@ -134,7 +134,7 @@ class FileSearch { FileGroup.GroupSortingAlgorithm groupSortingType, FileSorter.SortingMethod fileSortingMethod, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { - LinkedHashMap> searchResults = runFileSearch(filters, + LinkedHashMap> searchResults = runFileSearch(filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); LinkedHashMap groupSizes = new LinkedHashMap<>(); for (String groupName : searchResults.keySet()) { @@ -163,7 +163,7 @@ class FileSearch { * * @throws FileSearchException */ - static synchronized List getFilesInGroup( + static synchronized List getFilesInGroup( List filters, AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, @@ -173,8 +173,8 @@ class FileSearch { int numberOfEntries, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { //the group should be in the cache at this point - List filesInGroup = groupCache.getIfPresent(groupName); - List page = new ArrayList<>(); + List filesInGroup = groupCache.getIfPresent(groupName); + List page = new ArrayList<>(); if (filesInGroup == null) { logger.log(Level.INFO, "Group {0} was not cached, performing search to cache all groups again", groupName); runFileSearch(filters, groupAttributeType, groupSortingType, fileSortingMethod, caseDb, centralRepoDb); @@ -214,7 +214,7 @@ class FileSearch { * * @throws FileSearchException */ - private synchronized static LinkedHashMap> runFileSearch( + private synchronized static LinkedHashMap> runFileSearch( List filters, AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, @@ -239,7 +239,7 @@ class FileSearch { // Collect everything in the search results SearchResults searchResults = new SearchResults(groupSortingType, groupAttributeType, fileSortingMethod); searchResults.add(resultFiles); - LinkedHashMap> resultHashMap = searchResults.toLinkedHashMap(); + LinkedHashMap> resultHashMap = searchResults.toLinkedHashMap(); for (String groupName : resultHashMap.keySet()) { groupCache.put(groupName, resultHashMap.get(groupName)); } @@ -361,6 +361,7 @@ class FileSearch { */ void addAttributeToResultFiles(List files, SleuthkitCase caseDb, EamDb centralRepoDb) throws FileSearchException { // Default is to do nothing + System.out.println("DEFAULT IMPLE "); } } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java index 17e6acbf8a..f666db6b24 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearchTestAction.java @@ -18,7 +18,6 @@ */ package org.sleuthkit.autopsy.filequery; -import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.logging.Level; @@ -33,7 +32,6 @@ import org.openide.util.NbBundle; import org.openide.util.actions.CallableSystemAction; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AbstractFile; /** * Class to test the file search API. Allows the user to run searches and see results. @@ -106,7 +104,7 @@ public final class FileSearchTestAction extends CallableSystemAction { if (! groups.isEmpty()) { String firstGroupName = groups.keySet().iterator().next(); - List entries0to5 = FileSearch.getFilesInGroup(filters, + List entries0to5 = FileSearch.getFilesInGroup(filters, groupingAttr, groupSortAlgorithm, fileSort, @@ -115,11 +113,11 @@ public final class FileSearchTestAction extends CallableSystemAction { 5, Case.getCurrentCase().getSleuthkitCase(), crDb); System.out.println("First five " + firstGroupName + " : "); - for (AbstractFile f : entries0to5) { - System.out.println(" " + f.getName()); + for (ResultFile f : entries0to5) { + System.out.println(" " + f.getAbstractFile().getName()); } - List entries6to106 = FileSearch.getFilesInGroup(filters, + List entries6to106 = FileSearch.getFilesInGroup(filters, groupingAttr, groupSortAlgorithm, fileSort, @@ -128,8 +126,8 @@ public final class FileSearchTestAction extends CallableSystemAction { 100, Case.getCurrentCase().getSleuthkitCase(), crDb); System.out.println(firstGroupName + " 6 to 106: "); - for (AbstractFile f : entries6to106) { - System.out.println(" " + f.getName()); + for (ResultFile f : entries6to106) { + System.out.println(" " + f.getAbstractFile().getName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java b/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java index 51e3cc8f88..d727709590 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/PageWorker.java @@ -76,7 +76,7 @@ final class PageWorker extends SwingWorker { try { // Run the search - List results = FileSearch.getFilesInGroup(searchfilters, + List results = FileSearch.getFilesInGroup(searchfilters, groupingAttribute, groupSort, fileSortMethod, groupName, startingEntry, pageSize, diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java index 677fbb0955..87f15803b3 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -18,12 +18,14 @@ */ package org.sleuthkit.autopsy.filequery; +import java.awt.Image; import org.sleuthkit.autopsy.filequery.FileSearchData.FileType; import org.sleuthkit.datamodel.AbstractFile; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.datamodel.HashUtility; /** @@ -38,6 +40,7 @@ class ResultFile { private final List tagNames; private final List interestingSetNames; private final List objectDetectedNames; + private final List thumbnails; private final List duplicates; private FileType fileType; @@ -54,6 +57,7 @@ class ResultFile { tagNames = new ArrayList<>(); interestingSetNames = new ArrayList<>(); objectDetectedNames = new ArrayList<>(); + thumbnails = new ArrayList<>(); duplicates = new ArrayList<>(); fileType = FileType.OTHER; } @@ -198,6 +202,30 @@ class ResultFile { Collections.sort(interestingSetNames); } + private void createThumbnails(FileSearchData.FileType resultType) { + if (resultType == FileType.IMAGE) { + System.out.println("create single image thumbnail"); + thumbnails.add(ImageUtils.getThumbnail(abstractFile, ImageUtils.ICON_SIZE_MEDIUM)); + } else if (resultType == FileType.VIDEO) { + thumbnails.add(ImageUtils.getThumbnail(abstractFile, ImageUtils.ICON_SIZE_LARGE)); + thumbnails.add(ImageUtils.getThumbnail(abstractFile, ImageUtils.ICON_SIZE_LARGE)); + thumbnails.add(ImageUtils.getThumbnail(abstractFile, ImageUtils.ICON_SIZE_LARGE)); + thumbnails.add(ImageUtils.getThumbnail(abstractFile, ImageUtils.ICON_SIZE_LARGE)); + } else { + System.out.println("NOT IMAGE OR VIDEO: " + fileType.name()); + } + + } + + List getThumbnails(FileSearchData.FileType resultType) { + if (thumbnails.isEmpty()) { + System.out.println("IS EMPTY"); + createThumbnails(resultType); + } + System.out.println("THUMBNAILS GOT"); + return Collections.unmodifiableList(thumbnails); + } + /** * Get the interesting item set names for this file * @@ -252,10 +280,10 @@ class ResultFile { if (this.getAbstractFile().getMd5Hash() == null || HashUtility.isNoDataMd5(this.getAbstractFile().getMd5Hash()) || !HashUtility.isValidMd5Hash(this.getAbstractFile().getMd5Hash())) { - return super.hashCode(); + return super.hashCode(); } else { //if the file has a valid MD5 use the hashcode of the MD5 for deduping files with the same MD5 - return this.getAbstractFile().getMd5Hash().hashCode(); + return this.getAbstractFile().getMd5Hash().hashCode(); } } @@ -268,7 +296,7 @@ class ResultFile { || !HashUtility.isValidMd5Hash(this.getAbstractFile().getMd5Hash())) { return super.equals(obj); } else { - //if the file has a valid MD5 compare use the MD5 for equality check + //if the file has a valid MD5 compare use the MD5 for equality check return this.getAbstractFile().getMd5Hash().equals(((ResultFile) obj).getAbstractFile().getMd5Hash()); } } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form index d3706721d5..0cb97a4022 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.form @@ -17,15 +17,15 @@ - + - - + + @@ -101,15 +101,6 @@ - - - - - - - - - @@ -147,15 +138,6 @@ - - - - - - - - - @@ -234,9 +216,15 @@ - + - + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java index f350cfc342..721e14f8ed 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultsPanel.java @@ -19,7 +19,9 @@ package org.sleuthkit.autopsy.filequery; import com.google.common.eventbus.Subscribe; +import java.awt.Image; import java.util.List; +import java.util.stream.Collectors; import javax.swing.JOptionPane; import javax.swing.JSpinner; import javax.swing.SwingUtilities; @@ -32,6 +34,7 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; +import org.sleuthkit.datamodel.AbstractFile; /** * Panel for displaying of file discovery results and handling the paging of @@ -42,6 +45,7 @@ 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 List searchFilters; private FileSearch.AttributeType groupingAttribute; private FileGroup.GroupSortingAlgorithm groupSort; @@ -62,6 +66,7 @@ public class ResultsPanel extends javax.swing.JPanel { this.centralRepo = centralRepo; thumbnailViewer = new DataResultViewerThumbnail(explorerManager); tableViewer = new DataResultViewerTable(explorerManager); + videoThumbnailViewer = new VideoThumbnailViewer(); // Disable manual editing of page size spinner ((JSpinner.DefaultEditor) pageSizeSpinner.getEditor()).getTextField().setEditable(false); } @@ -79,17 +84,23 @@ public class ResultsPanel extends javax.swing.JPanel { thumbnailViewer.resetComponent(); resultsViewerPanel.remove(thumbnailViewer); resultsViewerPanel.remove(tableViewer); - if (pageRetrievedEvent.getType() == FileSearchData.FileType.IMAGE || pageRetrievedEvent.getType() == FileSearchData.FileType.VIDEO) { + if (pageRetrievedEvent.getType() == FileSearchData.FileType.IMAGE) { resultsViewerPanel.add(thumbnailViewer); if (pageRetrievedEvent.getSearchResults().size() > 0) { - thumbnailViewer.setNode(new TableFilterNode(new DataResultFilterNode(new AbstractNode(new DiscoveryThumbnailChildren(pageRetrievedEvent.getSearchResults()))), true)); + List filesList = pageRetrievedEvent.getSearchResults().stream().map(file -> file.getAbstractFile()).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)); } + } else if (pageRetrievedEvent.getType() == FileSearchData.FileType.VIDEO) { + populateVideoViewer(pageRetrievedEvent.getSearchResults()); + resultsViewerPanel.add(videoThumbnailViewer); + } else { resultsViewerPanel.add(tableViewer); if (pageRetrievedEvent.getSearchResults().size() > 0) { - tableViewer.setNode(new TableFilterNode(new SearchNode(pageRetrievedEvent.getSearchResults()), true)); + List filesList = pageRetrievedEvent.getSearchResults().stream().map(file -> file.getAbstractFile()).collect(Collectors.toList()); + tableViewer.setNode(new TableFilterNode(new SearchNode(filesList), true)); } else { tableViewer.setNode(new TableFilterNode(new DataResultFilterNode(Node.EMPTY), true)); } @@ -98,6 +109,19 @@ public class ResultsPanel extends javax.swing.JPanel { }); } + void populateVideoViewer(List files) { + System.out.println("POPULATE VIEWER"); + videoThumbnailViewer.clearViewer(); + for (ResultFile file : files) { + System.out.println("NEW THREAD"); + new Thread(() -> { + System.out.println("ON NEW THREAD"); + videoThumbnailViewer.addRow(file.getThumbnails(resultType), file.getAbstractFile().getParentPath()); + System.out.println("END NEW THREAD"); + }).start(); + } + } + /** * Subscribe and respond to GroupSelectedEvents. * @@ -168,6 +192,7 @@ public class ResultsPanel extends javax.swing.JPanel { gotoPageLabel = new javax.swing.JLabel(); gotoPageField = new javax.swing.JTextField(); pageSizeLabel = new javax.swing.JLabel(); + jScrollPane1 = new javax.swing.JScrollPane(); resultsViewerPanel = new javax.swing.JPanel(); pagingPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder()); @@ -177,9 +202,6 @@ public class ResultsPanel extends javax.swing.JPanel { previousPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N previousPageButton.setEnabled(false); previousPageButton.setFocusable(false); - previousPageButton.setMaximumSize(new java.awt.Dimension(23, 23)); - previousPageButton.setMinimumSize(new java.awt.Dimension(23, 23)); - previousPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); previousPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N previousPageButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -197,9 +219,6 @@ public class ResultsPanel extends javax.swing.JPanel { nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N nextPageButton.setEnabled(false); nextPageButton.setFocusable(false); - nextPageButton.setMaximumSize(new java.awt.Dimension(23, 23)); - nextPageButton.setMinimumSize(new java.awt.Dimension(23, 23)); - nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N nextPageButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { @@ -244,9 +263,9 @@ public class ResultsPanel extends javax.swing.JPanel { .addGap(18, 18, 18) .addComponent(pageControlsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(previousPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(previousPageButton) .addGap(0, 0, 0) - .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(nextPageButton) .addGap(18, 18, 18) .addComponent(gotoPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) @@ -262,9 +281,9 @@ public class ResultsPanel extends javax.swing.JPanel { .addGroup(pagingPanelLayout.createSequentialGroup() .addGap(4, 4, 4) .addGroup(pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(nextPageButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(nextPageButton, javax.swing.GroupLayout.Alignment.TRAILING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(previousPageButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(previousPageButton, javax.swing.GroupLayout.Alignment.TRAILING) .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(pageControlsLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, pagingPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) @@ -276,20 +295,21 @@ public class ResultsPanel extends javax.swing.JPanel { ); resultsViewerPanel.setLayout(new java.awt.BorderLayout()); + jScrollPane1.setViewportView(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(jScrollPane1) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(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, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addComponent(jScrollPane1)) ); }// //GEN-END:initComponents @@ -368,6 +388,7 @@ public class ResultsPanel extends javax.swing.JPanel { private javax.swing.JLabel currentPageLabel; private javax.swing.JTextField gotoPageField; private javax.swing.JLabel gotoPageLabel; + private javax.swing.JScrollPane jScrollPane1; private javax.swing.JButton nextPageButton; private javax.swing.JLabel pageControlsLabel; private javax.swing.JLabel pageSizeLabel; diff --git a/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java b/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java index 7bdcf4b722..bd06752caf 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/SearchResults.java @@ -135,7 +135,7 @@ class SearchResults { * @param groupName The name of the group. Can have the size appended. * @return the list of abstract files */ - List getAbstractFilesInGroup(String groupName) { + List getAbstractFilesInGroup(String groupName) { if (groupName != null) { final String modifiedGroupName = groupName.replaceAll(" \\([0-9]+\\)$", ""); @@ -152,8 +152,8 @@ class SearchResults { * * @return the grouped and sorted results */ - LinkedHashMap> toLinkedHashMap() throws FileSearchException { - LinkedHashMap> map = new LinkedHashMap<>(); + LinkedHashMap> toLinkedHashMap() throws FileSearchException { + LinkedHashMap> map = new LinkedHashMap<>(); // Sort the groups and files sortGroupsAndFiles(); diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.form new file mode 100644 index 0000000000..c4b113fcff --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.form @@ -0,0 +1,61 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.java new file mode 100644 index 0000000000..3952cc9364 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/ThumbnailPanel.java @@ -0,0 +1,92 @@ +/* + * 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.filequery; + +import java.awt.GridBagConstraints; +import java.awt.Image; +import java.util.List; +import javax.swing.ImageIcon; +import javax.swing.JLabel; + +/** + * + * @author wschaefer + */ +public class ThumbnailPanel extends javax.swing.JPanel { + + private static final int GAP_SIZE = 2; + + /** + * Creates new form ThumbnailPanel + */ + public ThumbnailPanel(List thumbnails, String fileInfo) { + initComponents(); + fileInfoLabel.setText(fileInfo); + addThumbnails(thumbnails); + this.setFocusable(true); + } + + private void addThumbnails(List thumbnails) { + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 32767))); + gridBagConstraints.gridx++; + for (Image image : thumbnails) { + imagePanel.add(new JLabel(new ImageIcon(image)), gridBagConstraints); + gridBagConstraints.gridx++; + imagePanel.add(new javax.swing.Box.Filler(new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 0), new java.awt.Dimension(2, 32767))); + gridBagConstraints.gridx++; + } + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + imagePanel = new javax.swing.JPanel(); + fileInfoLabel = new javax.swing.JLabel(); + + setBorder(javax.swing.BorderFactory.createEtchedBorder()); + + imagePanel.setLayout(new java.awt.GridBagLayout()); + + org.openide.awt.Mnemonics.setLocalizedText(fileInfoLabel, org.openide.util.NbBundle.getMessage(ThumbnailPanel.class, "ThumbnailPanel.fileInfoLabel.text")); // NOI18N + + 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) + .addComponent(imagePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 756, Short.MAX_VALUE) + .addComponent(fileInfoLabel, 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(imagePanel, javax.swing.GroupLayout.DEFAULT_SIZE, 125, Short.MAX_VALUE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(fileInfoLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 8, Short.MAX_VALUE) + .addContainerGap()) + ); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel fileInfoLabel; + private javax.swing.JPanel imagePanel; + // End of variables declaration//GEN-END:variables +} diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form new file mode 100644 index 0000000000..8458b286d7 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.form @@ -0,0 +1,18 @@ + + +
+ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java new file mode 100644 index 0000000000..03b0a18eb3 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailViewer.java @@ -0,0 +1,68 @@ +/* + * 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.filequery; + +import java.awt.GridBagConstraints; +import java.awt.Image; +import java.util.List; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.SwingUtilities; + +/** + * + * @author wschaefer + */ +public class VideoThumbnailViewer extends javax.swing.JPanel { + + private int nextRow = 0; + + /** + * Creates new form VideoThumbnailViewer + */ + public VideoThumbnailViewer() { + initComponents(); + } + + void clearViewer(){ + nextRow = 0; + this.removeAll(); + } + + void addRow(List thumbnails, String fileInfo) { + SwingUtilities.invokeLater(() -> { + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.anchor = GridBagConstraints.LINE_START; + synchronized (this) { + gridBagConstraints.gridy = nextRow; + nextRow++; + } + this.add(new ThumbnailPanel(thumbnails, fileInfo), gridBagConstraints); + System.out.println("ROW ADDED"); + revalidate(); + repaint(); + }); + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + setLayout(new java.awt.GridBagLayout()); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + // End of variables declaration//GEN-END:variables +}