diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java index 89afa87707..3894fed46c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Case.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Case.java @@ -1113,7 +1113,10 @@ public class Case { CallableSystemAction.get(OpenCommVisualizationToolAction.class).setEnabled(false); CallableSystemAction.get(OpenOutputFolderAction.class).setEnabled(false); CallableSystemAction.get(CommonAttributeSearchAction.class).setEnabled(false); - DiscoveryTopComponent.getTopComponent().resetTopComponent(); + DiscoveryTopComponent discoveryTopComp = DiscoveryTopComponent.getTopComponent(); + if (discoveryTopComp != null){ + discoveryTopComp.resetTopComponent(); + } /* * Clear the notifications in the notfier component in the lower * right hand corner of the main application window. diff --git a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED index bd8502abd8..dc5dd875f8 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/filequery/Bundle.properties-MERGED @@ -119,8 +119,13 @@ FileSorter.SortingMethod.keywordlist.displayName=By keyword list names FileSorter.SortingMethod.parent.displayName=By parent path # {0} - numberOfInstances ImageThumbnailPanel.countLabel.text=Number of Instances: {0} +ImageThumbnailPanel.isDeleted.text=All instances of file are deleted. # {0} - fileSize ImageThumbnailPanel.sizeLabel.text=Size: {0} bytes +ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it. +ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable. +ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag. +ResultFile.score.taggedFile.description=At least one instance of the file has been tagged. ResultsDialog.dialogTitle.text=File search results ResultsDialog.exitButton.text=Exit ResultsDialog.searchButton.text=Run another search @@ -177,5 +182,6 @@ ResultsPanel.viewFileInDir.name=View File in Directory SearchNode.getName.text=Search Result # {0} - numberOfInstances VideoThumbnailPanel.countLabel.text=Number of Instances: {0} +VideoThumbnailPanel.deleted.text=All instances of file are deleted. # {0} - fileSize VideoThumbnailPanel.sizeLabel.text=Size: {0} bytes diff --git a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java index 106bbb90b7..dd5f62b8c3 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/FileSearch.java @@ -144,7 +144,7 @@ class FileSearch { * * @throws FileSearchException */ - static LinkedHashMap getGroupSizes(String userName, + static Map getGroupSizes(String userName, List filters, AttributeType groupAttributeType, FileGroup.GroupSortingAlgorithm groupSortingType, diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form index bc2f036c23..e75effab93 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.form @@ -1,6 +1,9 @@ -
+ + + + @@ -17,13 +20,21 @@ - - - - - + + + + + + + + + + + + + - + @@ -35,7 +46,11 @@ - + + + + + @@ -43,6 +58,9 @@ + + + @@ -60,8 +78,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java index d73a6e4d0c..2c4a1f4e9b 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailPanel.java @@ -20,9 +20,14 @@ package org.sleuthkit.autopsy.filequery; import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.MouseEvent; import javax.swing.ImageIcon; +import javax.swing.JComponent; import javax.swing.JList; import javax.swing.ListCellRenderer; +import org.openide.util.ImageUtilities; import org.openide.util.NbBundle; /** @@ -32,6 +37,13 @@ public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellR private static final long serialVersionUID = 1L; private static final Color SELECTION_COLOR = new Color(0, 120, 215); + private static final int ICON_SIZE = 16; + private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; + private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; + private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png"; + private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); + private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); + private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false)); /** * Creates new form ImageThumbnailPanel @@ -53,20 +65,50 @@ public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellR thumbnailLabel = new javax.swing.JLabel(); fileSizeLabel = new javax.swing.JLabel(); countLabel = new javax.swing.JLabel(); + isDeletedLabel = new javax.swing.JLabel(); + scoreLabel = new javax.swing.JLabel(); + setToolTipText(""); + + thumbnailPanel.setToolTipText(""); thumbnailPanel.setLayout(new java.awt.GridBagLayout()); thumbnailPanel.add(thumbnailLabel, new java.awt.GridBagConstraints()); + fileSizeLabel.setToolTipText(""); + + countLabel.setToolTipText(""); + countLabel.setMaximumSize(new java.awt.Dimension(159, 12)); + countLabel.setMinimumSize(new java.awt.Dimension(159, 12)); + countLabel.setPreferredSize(new java.awt.Dimension(159, 12)); + + isDeletedLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/file-icon-deleted.png"))); // NOI18N + isDeletedLabel.setToolTipText(""); + isDeletedLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + isDeletedLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + isDeletedLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + scoreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/red-circle-exclamation.png"))); // NOI18N + scoreLabel.setToolTipText(""); + scoreLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + 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)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(thumbnailPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE) + .addComponent(fileSizeLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) + .addGroup(layout.createSequentialGroup() + .addComponent(countLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(isDeletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); layout.setVerticalGroup( @@ -77,7 +119,10 @@ public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellR .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) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(isDeletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(countLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) .addContainerGap()) ); }// //GEN-END:initComponents @@ -86,20 +131,75 @@ public class ImageThumbnailPanel extends javax.swing.JPanel implements ListCellR // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel countLabel; private javax.swing.JLabel fileSizeLabel; + private javax.swing.JLabel isDeletedLabel; + private javax.swing.JLabel scoreLabel; 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}"}) + "ImageThumbnailPanel.countLabel.text=Number of Instances: {0}", + "ImageThumbnailPanel.isDeleted.text=All instances of file are deleted."}) @Override public Component getListCellRendererComponent(JList 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())); + if (value.getResultFile().isDeleted()) { + isDeletedLabel.setIcon(DELETED_ICON); + isDeletedLabel.setToolTipText(Bundle.ImageThumbnailPanel_isDeleted_text()); + } else { + isDeletedLabel.setIcon(null); + isDeletedLabel.setToolTipText(null); + } + switch (value.getResultFile().getScore()) { + case NOTABLE_SCORE: + scoreLabel.setIcon(NOTABLE_SCORE_ICON); + break; + case INTERESTING_SCORE: + scoreLabel.setIcon(INTERESTING_SCORE_ICON); + break; + case NO_SCORE: + default: + scoreLabel.setIcon(null); + break; + } + scoreLabel.setToolTipText(value.getResultFile().getScoreDescription()); setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); + return this; } + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + //gets tooltip of internal panel item mouse is over + Point point = event.getPoint(); + for (Component comp : getComponents()) { + if (isPointOnIcon(comp, point)) { + String toolTip = ((JComponent) comp).getToolTipText(); + if (toolTip == null || toolTip.isEmpty()) { + return null; + } else { + return toolTip; + } + } + } + } + return null; + } + + /** + * Helper method to see if point is on the icon. + * + * @param comp The component to check if the cursor is over the icon of + * @param point The point the cursor is at. + * + * @return True if the point is over the icon, false otherwise. + */ + private boolean isPointOnIcon(Component comp, Point point) { + return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java index 1524f60820..542a7480a8 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ImageThumbnailViewer.java @@ -39,6 +39,7 @@ public class ImageThumbnailViewer extends javax.swing.JPanel { */ public ImageThumbnailViewer() { initComponents(); + } /** @@ -78,13 +79,13 @@ public class ImageThumbnailViewer extends javax.swing.JPanel { 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. + * @return The list of AbstractFiles which are represented by the selected + * image thumbnail. */ List getInstancesForSelected() { synchronized (this) { diff --git a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java index 1e124b0e2c..3b28a5e97d 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/ResultFile.java @@ -23,13 +23,25 @@ import org.sleuthkit.datamodel.AbstractFile; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; +import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.HashUtility; +import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.datamodel.TskData; /** * Container for files that holds all necessary data for grouping and sorting */ class ResultFile { + private final static Logger logger = Logger.getLogger(ResultFile.class.getName()); private FileSearchData.Frequency frequency; private final List keywordListNames; private final List hashSetNames; @@ -37,7 +49,9 @@ class ResultFile { private final List interestingSetNames; private final List objectDetectedNames; private final List instances = new ArrayList<>(); - ; + private DataResultViewerTable.Score currentScore = DataResultViewerTable.Score.NO_SCORE; + private String scoreDescription = null; + private boolean deleted = false; private FileType fileType; /** @@ -48,6 +62,10 @@ class ResultFile { ResultFile(AbstractFile abstractFile) { //store the file the ResultFile was created for as the first value in the instances list instances.add(abstractFile); + if (abstractFile.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + deleted = true; + } + updateScoreAndDescription(abstractFile); this.frequency = FileSearchData.Frequency.UNKNOWN; keywordListNames = new ArrayList<>(); hashSetNames = new ArrayList<>(); @@ -82,9 +100,42 @@ class ResultFile { * @param duplicate The abstract file to add as a duplicate. */ void addDuplicate(AbstractFile duplicate) { + if (deleted && !duplicate.isDirNameFlagSet(TskData.TSK_FS_NAME_FLAG_ENUM.UNALLOC)) { + deleted = false; + } + updateScoreAndDescription(duplicate); instances.add(duplicate); } + /** + * Get the aggregate score of this ResultFile. Calculated as the highest + * score among all instances it represents. + * + * @return The score of this ResultFile. + */ + DataResultViewerTable.Score getScore() { + return currentScore; + } + + /** + * Get the description for the score assigned to this item. + * + * @return The score description of this ResultFile. + */ + String getScoreDescription() { + return scoreDescription; + } + + /** + * Get the aggregate deleted status of this ResultFile. A file is identified + * as deleted if all instances of it are deleted. + * + * @return The deleted status of this ResultFile. + */ + boolean isDeleted() { + return deleted; + } + /** * Get the list of AbstractFiles which have been identified as instances of * this file. @@ -271,4 +322,56 @@ class ResultFile { return this.getFirstInstance().getMd5Hash().equals(((ResultFile) obj).getFirstInstance().getMd5Hash()); } } + + /** + * Get all tags from the case database that are associated with the file + * + * @return a list of tags that are associated with the file + */ + private List getContentTagsFromDatabase(AbstractFile file) { + List tags = new ArrayList<>(); + try { + tags.addAll(Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByContent(file)); + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.SEVERE, "Failed to get tags for file " + file.getName(), ex); + } + return tags; + } + + @NbBundle.Messages({ + "ResultFile.score.notableFile.description=At least one instance of the file was recognized as notable.", + "ResultFile.score.interestingResult.description=At least one instance of the file has an interesting result associated with it.", + "ResultFile.score.taggedFile.description=At least one instance of the file has been tagged.", + "ResultFile.score.notableTaggedFile.description=At least one instance of the file is tagged with a notable tag."}) + private void updateScoreAndDescription(AbstractFile file) { + if (currentScore == DataResultViewerTable.Score.NOTABLE_SCORE) { + //already notable can return + return; + } + if (file.getKnown() == TskData.FileKnown.BAD) { + currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; + scoreDescription = Bundle.ResultFile_score_notableFile_description(); + return; + } + try { + if (currentScore == DataResultViewerTable.Score.NO_SCORE && !file.getArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT).isEmpty()) { + currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; + scoreDescription = Bundle.ResultFile_score_interestingResult_description(); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Error getting artifacts for file: " + file.getName(), ex); + } + List tags = getContentTagsFromDatabase(file); + if (!tags.isEmpty()) { + currentScore = DataResultViewerTable.Score.INTERESTING_SCORE; + scoreDescription = Bundle.ResultFile_score_taggedFile_description(); + for (Tag tag : tags) { + if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) { + currentScore = DataResultViewerTable.Score.NOTABLE_SCORE; + scoreDescription = Bundle.ResultFile_score_notableTaggedFile_description(); + return; + } + } + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form index 5f41c66123..86a30442f6 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.form @@ -24,16 +24,20 @@ - + - - + + - + + + + + - + @@ -41,13 +45,17 @@ - - - - - + + + + + + + + + - + @@ -61,5 +69,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java index 5dbe46cd3d..048fe1b8d5 100644 --- a/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java +++ b/Core/src/org/sleuthkit/autopsy/filequery/VideoThumbnailPanel.java @@ -20,13 +20,18 @@ package org.sleuthkit.autopsy.filequery; import java.awt.Color; import java.awt.Component; +import java.awt.Dimension; import java.awt.Image; import java.awt.GridBagConstraints; +import java.awt.Point; +import java.awt.event.MouseEvent; import java.util.concurrent.TimeUnit; import javax.swing.ImageIcon; +import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; +import org.openide.util.ImageUtilities; import org.openide.util.NbBundle.Messages; /** @@ -35,7 +40,14 @@ import org.openide.util.NbBundle.Messages; final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRenderer { private static final int GAP_SIZE = 4; - private static final Color SELECTION_COLOR = new Color(0,120,215); + private static final Color SELECTION_COLOR = new Color(0, 120, 215); + private static final int ICON_SIZE = 16; + private static final String RED_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/red-circle-exclamation.png"; + private static final String YELLOW_CIRCLE_ICON_PATH = "org/sleuthkit/autopsy/images/yellow-circle-yield.png"; + private static final String DELETE_ICON_PATH = "/org/sleuthkit/autopsy/images/file-icon-deleted.png"; + private static final ImageIcon INTERESTING_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(YELLOW_CIRCLE_ICON_PATH, false)); + private static final ImageIcon NOTABLE_SCORE_ICON = new ImageIcon(ImageUtilities.loadImage(RED_CIRCLE_ICON_PATH, false)); + private static final ImageIcon DELETED_ICON = new ImageIcon(ImageUtilities.loadImage(DELETE_ICON_PATH, false)); private static final long serialVersionUID = 1L; /** @@ -96,11 +108,23 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe imagePanel = new javax.swing.JPanel(); fileSizeLabel = new javax.swing.JLabel(); countLabel = new javax.swing.JLabel(); + scoreLabel = new javax.swing.JLabel(); + deletedLabel = new javax.swing.JLabel(); setBorder(javax.swing.BorderFactory.createEtchedBorder()); imagePanel.setLayout(new java.awt.GridBagLayout()); + scoreLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/red-circle-exclamation.png"))); // NOI18N + scoreLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + scoreLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + + deletedLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/file-icon-deleted.png"))); // NOI18N + deletedLabel.setMaximumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + deletedLabel.setMinimumSize(new Dimension(ICON_SIZE,ICON_SIZE)); + deletedLabel.setPreferredSize(new Dimension(ICON_SIZE,ICON_SIZE)); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( @@ -112,40 +136,101 @@ final class VideoThumbnailPanel extends javax.swing.JPanel implements ListCellRe .addGroup(layout.createSequentialGroup() .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 248, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 204, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 124, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))) .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() - .addComponent(imagePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 220, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(imagePanel, javax.swing.GroupLayout.PREFERRED_SIZE, 140, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .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()) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(fileSizeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) + .addComponent(deletedLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(scoreLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(countLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JLabel countLabel; + private javax.swing.JLabel deletedLabel; private javax.swing.JLabel fileSizeLabel; private javax.swing.JPanel imagePanel; + private javax.swing.JLabel scoreLabel; // End of variables declaration//GEN-END:variables @Messages({"# {0} - fileSize", "VideoThumbnailPanel.sizeLabel.text=Size: {0} bytes", "# {0} - numberOfInstances", - "VideoThumbnailPanel.countLabel.text=Number of Instances: {0}"}) + "VideoThumbnailPanel.countLabel.text=Number of Instances: {0}", + "VideoThumbnailPanel.deleted.text=All instances of file are deleted."}) @Override public Component getListCellRendererComponent(JList list, VideoThumbnailsWrapper value, int index, boolean isSelected, boolean cellHasFocus) { fileSizeLabel.setText(Bundle.VideoThumbnailPanel_sizeLabel_text(value.getResultFile().getFirstInstance().getSize())); countLabel.setText(Bundle.VideoThumbnailPanel_countLabel_text(value.getResultFile().getAllInstances().size())); addThumbnails(value); imagePanel.setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); + if (value.getResultFile().isDeleted()) { + deletedLabel.setIcon(DELETED_ICON); + deletedLabel.setToolTipText(Bundle.VideoThumbnailPanel_deleted_text()); + } else { + deletedLabel.setIcon(null); + deletedLabel.setToolTipText(""); + } + switch (value.getResultFile().getScore()) { + case NOTABLE_SCORE: + scoreLabel.setIcon(NOTABLE_SCORE_ICON); + break; + case INTERESTING_SCORE: + scoreLabel.setIcon(INTERESTING_SCORE_ICON); + break; + case NO_SCORE: + default: + scoreLabel.setIcon(null); + break; + } + scoreLabel.setToolTipText(value.getResultFile().getScoreDescription()); setBackground(isSelected ? SELECTION_COLOR : list.getBackground()); return this; } + + @Override + public String getToolTipText(MouseEvent event) { + if (event != null) { + //gets tooltip of internal panel item mouse is over + Point point = event.getPoint(); + for (Component comp : getComponents()) { + if (isPointOnIcon(comp, point)) { + String toolTip = ((JComponent) comp).getToolTipText(); + if (toolTip == null || toolTip.isEmpty()) { + return null; + } else { + return toolTip; + } + } + } + } + return null; + } + + /** + * Helper method to see if point is on the icon. + * + * @param comp The component to check if the cursor is over the icon of + * @param point The point the cursor is at. + * + * @return True if the point is over the icon, false otherwise. + */ + private boolean isPointOnIcon(Component comp, Point point) { + return comp instanceof JComponent && point.x >= comp.getX() && point.x <= comp.getX() + ICON_SIZE && point.y >= comp.getY() && point.y <= comp.getY() + ICON_SIZE; + } }