diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties index 2683f71d6d..85bd858a97 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties @@ -238,13 +238,7 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case... OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name -LogicalImagerPanel.jLabel1.text=Insert external drive -LogicalImagerPanel.scanButton.text=Scan -LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder LogicalImagerPanel.browseButton.text=Browse -LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results -LogicalImagerPanel.selectDriveLabel.text=Select Drive -LogicalImagerPanel.messageLabel.text=Error/Status message UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module. UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the UnpackagePortableCaseDialog.exitButton.text=Exit @@ -259,4 +253,13 @@ UnpackagePortableCaseProgressDialog.cancelButton.text=Cancel UnpackagePortableCaseProgressDialog.okButton.text=OK UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to: -UnpackagePortableCaseDialog.caseLabel.text=Portable Case: \ No newline at end of file +UnpackagePortableCaseDialog.caseLabel.text=Portable Case: +LogicalImagerPanel.importRadioButton.text=Import From External Drive +LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder +LogicalImagerPanel.importRadioButton.toolTipText= +LogicalImagerPanel.pathTextField.text= +LogicalImagerPanel.selectFolderLabel.text=Selected Folder: +LogicalImagerPanel.refreshButton.text=Refresh +LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive +LogicalImagerPanel.selectDriveLabel.text=Select Drive +LogicalImagerPanel.messageTextArea.text= diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED index 5ad9042e9f..1a55b465df 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/casemodule/Bundle.properties-MERGED @@ -191,7 +191,6 @@ LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0} LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected. LogicalImagerPanel.imageTable.columnModel.title0=Hostname LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date -LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images # {0} - sparseImageDirectory # {1} - image LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1} @@ -201,7 +200,6 @@ LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found LogicalImagerPanel.messageLabel.noImageSelected=No image selected LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ... -LogicalImagerPanel.messageLabel.selectedImage=Selected folder LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive Menu/Case/OpenRecentCase=Open Recent Case CTL_CaseDeleteAction=Delete Case @@ -475,13 +473,7 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case... OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name -LogicalImagerPanel.jLabel1.text=Insert external drive -LogicalImagerPanel.scanButton.text=Scan -LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder LogicalImagerPanel.browseButton.text=Browse -LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results -LogicalImagerPanel.selectDriveLabel.text=Select Drive -LogicalImagerPanel.messageLabel.text=Error/Status message UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module. UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the UnpackagePortableCaseDialog.exitButton.text=Exit @@ -497,3 +489,12 @@ UnpackagePortableCaseProgressDialog.okButton.text=OK UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to: UnpackagePortableCaseDialog.caseLabel.text=Portable Case: +LogicalImagerPanel.importRadioButton.text=Import From External Drive +LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder +LogicalImagerPanel.importRadioButton.toolTipText= +LogicalImagerPanel.pathTextField.text= +LogicalImagerPanel.selectFolderLabel.text=Selected Folder: +LogicalImagerPanel.refreshButton.text=Refresh +LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive +LogicalImagerPanel.selectDriveLabel.text=Select Drive +LogicalImagerPanel.messageTextArea.text= diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form index 79e6241551..f5a370ae4c 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.form @@ -1,6 +1,10 @@
+ + + + @@ -25,118 +29,146 @@ - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + + + + + - - - - - - - - - + + + + + - - - - - + + + + + + - - - - - - - - - - - - - - + + - + - - - + + + + + + + + + + + + + + + + + - + - + + + + + - + - - + + - - - - + - + + + + - + - + + + + + + + + + + + + + + - + + + + + + + + + + + + @@ -147,6 +179,13 @@ + + + + + + + @@ -162,7 +201,7 @@ - + @@ -171,28 +210,14 @@ - + - - - - - - - - - - - - - - - + - + @@ -224,13 +249,56 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java index cfc0f87eb1..b54d468d34 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/LogicalImagerPanel.java @@ -19,6 +19,7 @@ package org.sleuthkit.autopsy.casemodule; import java.awt.Color; +import java.awt.Component; import java.io.File; import java.io.IOException; import java.nio.file.FileStore; @@ -46,7 +47,6 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor; * select a file. */ @Messages({ - "LogicalImagerPanel.messageLabel.selectedImage=Selected folder", "LogicalImagerPanel.messageLabel.noImageSelected=No image selected", "LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images", "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",}) @@ -55,7 +55,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { private static final long serialVersionUID = 1L; private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS - private static final String SELECTED_IMAGE = Bundle.LogicalImagerPanel_messageLabel_selectedImage(); private static final String NO_IMAGE_SELECTED = Bundle.LogicalImagerPanel_messageLabel_noImageSelected(); private static final String DRIVE_HAS_NO_IMAGES = Bundle.LogicalImagerPanel_messageLabel_driveHasNoImages(); private static final String[] EMPTY_LIST_DATA = {}; @@ -75,6 +74,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { private LogicalImagerPanel(String context) { this.contextName = context; initComponents(); + jScrollPane1.setBorder(null); clearImageTable(); } @@ -86,13 +86,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { * * @return instance of the LogicalImagerPanel */ - @Messages({ - "LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images" - }) public static synchronized LogicalImagerPanel createInstance(String context) { LogicalImagerPanel instance = new LogicalImagerPanel(context); // post-constructor initialization of listener support without leaking references of uninitialized objects - instance.messageLabel.setText(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse()); instance.imageTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); return instance; } @@ -104,43 +100,68 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { */ // //GEN-BEGIN:initComponents private void initComponents() { + bindingGroup = new org.jdesktop.beansbinding.BindingGroup(); - topLabel = new javax.swing.JLabel(); - jLabel1 = new javax.swing.JLabel(); - scanButton = new javax.swing.JButton(); - messageLabel = new javax.swing.JLabel(); + buttonGroup1 = new javax.swing.ButtonGroup(); + browseButton = new javax.swing.JButton(); + importRadioButton = new javax.swing.JRadioButton(); + manualRadioButton = new javax.swing.JRadioButton(); + pathTextField = new javax.swing.JTextField(); + selectFolderLabel = new javax.swing.JLabel(); selectDriveLabel = new javax.swing.JLabel(); + selectFromDriveLabel = new javax.swing.JLabel(); driveListScrollPane = new javax.swing.JScrollPane(); driveList = new javax.swing.JList<>(); - selectAcquisitionFromDriveLabel = new javax.swing.JLabel(); - jLabel6 = new javax.swing.JLabel(); - browseButton = new javax.swing.JButton(); + refreshButton = new javax.swing.JButton(); imageScrollPane = new javax.swing.JScrollPane(); imageTable = new javax.swing.JTable(); - jSeparator1 = new javax.swing.JSeparator(); + jSeparator2 = new javax.swing.JSeparator(); + jScrollPane1 = new javax.swing.JScrollPane(); + messageTextArea = new javax.swing.JTextArea(); setMinimumSize(new java.awt.Dimension(0, 65)); setPreferredSize(new java.awt.Dimension(403, 65)); - org.openide.awt.Mnemonics.setLocalizedText(topLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.topLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel1.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(scanButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.scanButton.text")); // NOI18N - scanButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.browseButton.text")); // NOI18N + browseButton.setEnabled(false); + browseButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - scanButtonActionPerformed(evt); + browseButtonActionPerformed(evt); } }); - org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageLabel.text")); // NOI18N + buttonGroup1.add(importRadioButton); + importRadioButton.setSelected(true); + org.openide.awt.Mnemonics.setLocalizedText(importRadioButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.text")); // NOI18N + importRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.toolTipText")); // NOI18N + importRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + importRadioButtonActionPerformed(evt); + } + }); + + buttonGroup1.add(manualRadioButton); + org.openide.awt.Mnemonics.setLocalizedText(manualRadioButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.manualRadioButton.text")); // NOI18N + manualRadioButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + manualRadioButtonActionPerformed(evt); + } + }); + + pathTextField.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.pathTextField.text")); // NOI18N + pathTextField.setDisabledTextColor(java.awt.Color.black); + pathTextField.setEnabled(false); + + org.openide.awt.Mnemonics.setLocalizedText(selectFolderLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectFolderLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(selectDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectDriveLabel.text")); // NOI18N + org.openide.awt.Mnemonics.setLocalizedText(selectFromDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectFromDriveLabel.text")); // NOI18N + driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); driveList.addMouseListener(new java.awt.event.MouseAdapter() { - public void mouseClicked(java.awt.event.MouseEvent evt) { - driveListMouseClicked(evt); + public void mouseReleased(java.awt.event.MouseEvent evt) { + driveListMouseReleased(evt); } }); driveList.addKeyListener(new java.awt.event.KeyAdapter() { @@ -150,14 +171,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { }); driveListScrollPane.setViewportView(driveList); - org.openide.awt.Mnemonics.setLocalizedText(selectAcquisitionFromDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel6.text")); // NOI18N - - org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.browseButton.text")); // NOI18N - browseButton.addActionListener(new java.awt.event.ActionListener() { + org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.refreshButton.text")); // NOI18N + refreshButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - browseButtonActionPerformed(evt); + refreshButtonActionPerformed(evt); } }); @@ -178,8 +195,8 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { imageTable.getTableHeader().setReorderingAllowed(false); imageTable.setUpdateSelectionOnSort(false); imageTable.addMouseListener(new java.awt.event.MouseAdapter() { - public void mouseClicked(java.awt.event.MouseEvent evt) { - imageTableMouseClicked(evt); + public void mouseReleased(java.awt.event.MouseEvent evt) { + imageTableMouseReleased(evt); } }); imageTable.addKeyListener(new java.awt.event.KeyAdapter() { @@ -190,72 +207,94 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { imageScrollPane.setViewportView(imageTable); imageTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); + jScrollPane1.setBorder(null); + + messageTextArea.setBackground(new java.awt.Color(240, 240, 240)); + messageTextArea.setColumns(20); + messageTextArea.setForeground(java.awt.Color.red); + messageTextArea.setLineWrap(true); + messageTextArea.setRows(3); + messageTextArea.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageTextArea.text")); // NOI18N + messageTextArea.setBorder(null); + messageTextArea.setDisabledTextColor(java.awt.Color.red); + messageTextArea.setEnabled(false); + messageTextArea.setMargin(new java.awt.Insets(0, 0, 0, 0)); + + org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, messageTextArea, org.jdesktop.beansbinding.ELProperty.create("false"), messageTextArea, org.jdesktop.beansbinding.BeanProperty.create("editable")); + bindingGroup.addBinding(binding); + + jScrollPane1.setViewportView(messageTextArea); + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addGap(238, 238, 238) - .addComponent(topLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 163, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, Short.MAX_VALUE)) - .addGroup(layout.createSequentialGroup() - .addGap(28, 28, 28) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(messageLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) - .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(layout.createSequentialGroup() - .addComponent(selectDriveLabel) - .addGap(289, 289, 289)) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addComponent(scanButton) - .addGap(126, 126, 126))) - .addGap(36, 36, 36) - .addComponent(browseButton)) - .addGroup(layout.createSequentialGroup() - .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(28, 28, 28) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(selectAcquisitionFromDriveLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE))) - .addGroup(layout.createSequentialGroup() - .addGap(346, 346, 346) - .addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGroup(layout.createSequentialGroup() - .addGap(144, 144, 144) - .addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE)))) - .addContainerGap(48, Short.MAX_VALUE)))) + .addGap(10, 10, 10) + .addComponent(selectFolderLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(13, 13, 13) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 474, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false) + .addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addGap(41, 41, 41) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(refreshButton)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 377, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addGroup(layout.createSequentialGroup() + .addGap(20, 20, 20) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(manualRadioButton) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(browseButton)) + .addComponent(importRadioButton) + .addGroup(layout.createSequentialGroup() + .addGap(21, 21, 21) + .addComponent(selectDriveLabel) + .addGap(113, 113, 113) + .addComponent(selectFromDriveLabel)))))) + .addGroup(layout.createSequentialGroup() + .addContainerGap() + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 568, javax.swing.GroupLayout.PREFERRED_SIZE))) + .addContainerGap(14, Short.MAX_VALUE)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() - .addComponent(topLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(jLabel1) - .addComponent(jLabel6)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(scanButton) - .addComponent(browseButton)) + .addGap(16, 16, 16) + .addComponent(importRadioButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) .addComponent(selectDriveLabel) - .addComponent(selectAcquisitionFromDriveLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false) + .addComponent(selectFromDriveLabel)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) - .addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE)) - .addGap(26, 26, 26) - .addComponent(messageLabel) - .addGap(154, 154, 154)) + .addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(refreshButton) + .addGap(18, 18, 18) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(browseButton) + .addComponent(manualRadioButton)) + .addGap(18, 18, 18) + .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(selectFolderLabel) + .addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(6, 6, 6)) ); + + bindingGroup.bind(); }// //GEN-END:initComponents public static String humanReadableByteCount(long bytes, boolean si) { @@ -268,47 +307,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); //NON-NLS } - @Messages({ - "LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...", - "LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found" - }) - private void scanButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scanButtonActionPerformed - // Scan external drives for sparse_image.vhd - clearImageTable(); - setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives()); - List listData = new ArrayList<>(); - File[] roots = File.listRoots(); - int firstRemovableDrive = -1; - int i = 0; - for (File root : roots) { - String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root); - long spaceInBytes = root.getTotalSpace(); - String sizeWithUnit = humanReadableByteCount(spaceInBytes, false); - listData.add(root + " (" + description + ") (" + sizeWithUnit + ")"); - if (firstRemovableDrive == -1) { - try { - FileStore fileStore = Files.getFileStore(root.toPath()); - if ((boolean) fileStore.getAttribute("volume:isRemovable")) { //NON-NLS - firstRemovableDrive = i; - } - } catch (IOException ex) { - ; // skip - } - } - i++; - } - driveList.setListData(listData.toArray(new String[0])); - if (!listData.isEmpty()) { - // auto-select the first external drive, if any - driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive); - driveListMouseClicked(null); - driveList.requestFocusInWindow(); - } else { - setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_noExternalDriveFound()); - } - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); - }//GEN-LAST:event_scanButtonActionPerformed - @Messages({ "# {0} - sparseImageDirectory", "# {1} - image", @@ -333,7 +331,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { return; } choosenImageDirPath = Paths.get(path); - setNormalMessage(SELECTED_IMAGE + " " + path); + setNormalMessage(path); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryFormatInvalid(path)); @@ -348,7 +346,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { int index = imageTable.getSelectedRow(); if (index != -1) { choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(index, 2)); - setNormalMessage(SELECTED_IMAGE + " " + choosenImageDirPath.toString()); + setNormalMessage(choosenImageDirPath.toString()); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true); } else { choosenImageDirPath = null; @@ -357,10 +355,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } } - private void imageTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseClicked - imageTableSelect(); - }//GEN-LAST:event_imageTableMouseClicked - private void driveListSelect() { String selectedStr = driveList.getSelectedValue(); if (selectedStr == null) { @@ -398,7 +392,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } } } - selectAcquisitionFromDriveLabel.setText(Bundle.LogicalImagerPanel_selectAcquisitionFromDriveLabel_text() + selectFromDriveLabel.setText(Bundle.LogicalImagerPanel_selectAcquisitionFromDriveLabel_text() + " " + driveLetter); imageTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN); imageTable.setModel(imageTableModel); @@ -411,6 +405,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { choosenImageDirPath = null; setErrorMessage(DRIVE_HAS_NO_IMAGES); } + } else { + clearImageTable(); + choosenImageDirPath = null; + setErrorMessage(DRIVE_HAS_NO_IMAGES); } } @@ -421,13 +419,14 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { } private void setErrorMessage(String msg) { - messageLabel.setForeground(Color.red); - messageLabel.setText(msg); + messageTextArea.setForeground(Color.red); + messageTextArea.setText(msg); + pathTextField.setText(""); } private void setNormalMessage(String msg) { - messageLabel.setForeground(Color.black); - messageLabel.setText(msg); + pathTextField.setText(msg); + messageTextArea.setText(""); } private void clearImageTable() { @@ -436,43 +435,138 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { fixImageTableColumnWidth(); } - private void driveListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseClicked - driveListSelect(); + private void toggleMouseAndKeyListeners(Component component, boolean isEnable) { + component.setEnabled(isEnable); + } + + private void manualRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manualRadioButtonActionPerformed + browseButton.setEnabled(true); + + // disable import panel + toggleMouseAndKeyListeners(driveList, false); + toggleMouseAndKeyListeners(driveListScrollPane, false); + toggleMouseAndKeyListeners(imageScrollPane, false); + toggleMouseAndKeyListeners(imageTable, false); + + refreshButton.setEnabled(false); + + choosenImageDirPath = null; + setNormalMessage(""); firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); - }//GEN-LAST:event_driveListMouseClicked + }//GEN-LAST:event_manualRadioButtonActionPerformed + + private void importRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importRadioButtonActionPerformed + browseButton.setEnabled(false); + + toggleMouseAndKeyListeners(driveList, true); + toggleMouseAndKeyListeners(driveListScrollPane, true); + toggleMouseAndKeyListeners(imageScrollPane, true); + toggleMouseAndKeyListeners(imageTable, true); + + refreshButton.setEnabled(true); + + choosenImageDirPath = null; + setNormalMessage(""); + refreshButton.doClick(); + }//GEN-LAST:event_importRadioButtonActionPerformed + + @Messages({ + "LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...", + "LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found" + }) + private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed + // Scan external drives for sparse_image.vhd + clearImageTable(); + setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives()); + List listData = new ArrayList<>(); + File[] roots = File.listRoots(); + int firstRemovableDrive = -1; + int i = 0; + for (File root : roots) { + String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root); + long spaceInBytes = root.getTotalSpace(); + String sizeWithUnit = humanReadableByteCount(spaceInBytes, false); + listData.add(root + " (" + description + ") (" + sizeWithUnit + ")"); + if (firstRemovableDrive == -1) { + try { + FileStore fileStore = Files.getFileStore(root.toPath()); + if ((boolean) fileStore.getAttribute("volume:isRemovable")) { //NON-NLS + firstRemovableDrive = i; + } + } catch (IOException ex) { + ; // skip + } + } + i++; + } + driveList.setListData(listData.toArray(new String[0])); + if (!listData.isEmpty()) { + // auto-select the first external drive, if any + driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive); + driveListMouseReleased(null); + driveList.requestFocusInWindow(); + } else { + setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_noExternalDriveFound()); + } + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); + }//GEN-LAST:event_refreshButtonActionPerformed private void driveListKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_driveListKeyReleased - driveListSelect(); - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); + if (importRadioButton.isSelected()) { + driveListSelect(); + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); + } }//GEN-LAST:event_driveListKeyReleased private void imageTableKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_imageTableKeyReleased - imageTableSelect(); - firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); + if (importRadioButton.isSelected()) { + imageTableSelect(); + } }//GEN-LAST:event_imageTableKeyReleased + private void imageTableMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseReleased + if (importRadioButton.isSelected()) { + imageTableSelect(); + } + }//GEN-LAST:event_imageTableMouseReleased + + private void driveListMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseReleased + if (importRadioButton.isSelected()) { + driveListSelect(); + firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false); + } + }//GEN-LAST:event_driveListMouseReleased + + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton browseButton; + private javax.swing.ButtonGroup buttonGroup1; private javax.swing.JList driveList; private javax.swing.JScrollPane driveListScrollPane; private javax.swing.JScrollPane imageScrollPane; private javax.swing.JTable imageTable; - private javax.swing.JLabel jLabel1; - private javax.swing.JLabel jLabel6; - private javax.swing.JSeparator jSeparator1; - private javax.swing.JLabel messageLabel; - private javax.swing.JButton scanButton; - private javax.swing.JLabel selectAcquisitionFromDriveLabel; + private javax.swing.JRadioButton importRadioButton; + private javax.swing.JScrollPane jScrollPane1; + private javax.swing.JSeparator jSeparator2; + private javax.swing.JRadioButton manualRadioButton; + private javax.swing.JTextArea messageTextArea; + private javax.swing.JTextField pathTextField; + private javax.swing.JButton refreshButton; private javax.swing.JLabel selectDriveLabel; - private javax.swing.JLabel topLabel; + private javax.swing.JLabel selectFolderLabel; + private javax.swing.JLabel selectFromDriveLabel; + private org.jdesktop.beansbinding.BindingGroup bindingGroup; // End of variables declaration//GEN-END:variables public void reset() { //reset the UI elements to default choosenImageDirPath = null; + setNormalMessage(""); driveList.setListData(EMPTY_LIST_DATA); clearImageTable(); - setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse()); + if (importRadioButton.isSelected()) { + refreshButton.doClick(); + } } /** @@ -488,10 +582,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener { return choosenImageDirPath; } - public void setMessageLabel(String message) { - messageLabel.setText(message); - } - @Override public void insertUpdate(DocumentEvent e) { } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java index 8b551ce4ec..aedbb3671f 100644 --- a/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/NewCaseVisualPanel1.java @@ -88,7 +88,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { * @return caseName the case name from the case name text field */ String getCaseName() { - return this.caseNameTextField.getText(); + return this.caseNameTextField.getText().trim(); } /** @@ -109,7 +109,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener { * @return baseDirectory the base directory from the case dir text field */ String getCaseParentDir() { - String parentDir = this.caseParentDirTextField.getText(); + String parentDir = this.caseParentDirTextField.getText().trim(); if (parentDir.endsWith(File.separator) == false) { parentDir = parentDir + File.separator; } diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java b/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java index 8d117e908b..db3424a696 100755 --- a/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java +++ b/Core/src/org/sleuthkit/autopsy/casemodule/services/contentviewertags/ContentViewerTagManager.java @@ -58,8 +58,6 @@ public class ContentViewerTagManager { * generic tag data instance T will be automatically serialized into a * storable format. * - * @param Generic class type that will be serialized into a storable - * format for persistence. * @param contentTag ContentTag that this ContentViewerTag is associated * with (1:1). * @param tagDataBean Data instance that contains the tag information to be @@ -95,8 +93,6 @@ public class ContentViewerTagManager { * Updates the ContentViewerTag instance with the new tag data T and * persists the changes to the case database. * - * @param Generic class type that will be serialized into a storable - * format. * @param oldTag ContentViewerTag instance to be updated * @param tagDataBean Data instance that contains the updated information to * be persisted. @@ -130,8 +126,6 @@ public class ContentViewerTagManager { * details of the tag should be passed so that automatic binding can take * place. * - * @param Generic class type that will be instantiated and filled in - * with data. * @param contentTag ContentTag that this ContentViewerTag is associated * with (1:1) * @param clazz Generic class that will be instantiated and filled in with diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 54ab3fba23..fe66b2fa6e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -122,7 +122,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro final int rows = Math.min(100, outline.getRowCount()); - for (int column = 0; column < outline.getModel().getColumnCount(); column++) { + for (int column = 0; column < outline.getColumnCount(); column++) { int columnWidthLimit = 500; int columnWidth = 0; diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 77fc276f83..bc16bb6da2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -11,6 +11,7 @@ + @@ -28,7 +29,7 @@ - + @@ -65,14 +66,9 @@ - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 8e8365436c..0032793584 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -112,18 +112,18 @@ public final class CVTTopComponent extends TopComponent { gridBagConstraints.gridy = 0; gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; - gridBagConstraints.weightx = 1.0; + gridBagConstraints.weightx = 0.75; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new Insets(15, 0, 15, 15); add(browseVisualizeTabPane, gridBagConstraints); browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N - filtersPane.setMinimumSize(new Dimension(256, 495)); gridBagConstraints = new GridBagConstraints(); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; gridBagConstraints.fill = GridBagConstraints.BOTH; gridBagConstraints.anchor = GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 0.25; gridBagConstraints.weighty = 1.0; gridBagConstraints.insets = new Insets(15, 15, 15, 5); add(filtersPane, gridBagConstraints); diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form index 8bd260ea4b..2a656008c2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.form @@ -11,493 +11,487 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - + - + - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + - + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + - - - - - - + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + - - - - + + - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index a6cdf63b63..5ba6b9b89a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -394,135 +394,123 @@ final public class FiltersPanel extends JPanel { @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; - applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N - applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N - applyFiltersButton.setPreferredSize(null); + setLayout(new java.awt.GridBagLayout()); + + topPane.setLayout(new java.awt.GridBagLayout()); filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + topPane.add(filtersTitleLabel, gridBagConstraints); - unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N - unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() { + refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N + refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + topPane.add(refreshButton, gridBagConstraints); + + applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N + applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + topPane.add(applyFiltersButton, gridBagConstraints); + + needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N + needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0)); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + topPane.add(needsRefreshLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END; + gridBagConstraints.weightx = 1.0; + add(topPane, gridBagConstraints); + + scrollPane.setBorder(null); + + mainPanel.setLayout(new java.awt.GridBagLayout()); + + limitPane.setLayout(new java.awt.GridBagLayout()); + + mostRecentLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.mostRecentLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 9, 0, 9); + limitPane.add(mostRecentLabel, gridBagConstraints); + + limitComboBox.setEditable(true); + limitComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "All", "10000", "5000", "1000", "500", "100" })); + limitComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - unCheckAllAccountTypesButtonActionPerformed(evt); + limitComboBoxActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 1; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + limitPane.add(limitComboBox, gridBagConstraints); - accountTypesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/accounts.png"))); // NOI18N - accountTypesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypesLabel.text")); // NOI18N + limitTitlePanel.setLayout(new java.awt.GridBagLayout()); - checkAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllAccountTypesButton.text")); // NOI18N - checkAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - checkAllAccountTypesButtonActionPerformed(evt); - } - }); + limitHeaderLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitHeaderLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + limitTitlePanel.add(limitHeaderLabel, gridBagConstraints); - accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200)); + limitErrorMsgLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N + limitErrorMsgLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitErrorMsgLabel.text")); // NOI18N + limitErrorMsgLabel.setForeground(new java.awt.Color(255, 0, 0)); + limitErrorMsgLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + limitTitlePanel.add(limitErrorMsgLabel, gridBagConstraints); - accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS)); - accountTypesScrollPane.setViewportView(accountTypeListPane); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + limitPane.add(limitTitlePanel, gridBagConstraints); - accountTypeRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N - accountTypeRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypeRequiredLabel.text")); // NOI18N - accountTypeRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); - accountTypeRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - - javax.swing.GroupLayout accountTypesPaneLayout = new javax.swing.GroupLayout(accountTypesPane); - accountTypesPane.setLayout(accountTypesPaneLayout); - accountTypesPaneLayout.setHorizontalGroup( - accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, accountTypesPaneLayout.createSequentialGroup() - .addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addGroup(accountTypesPaneLayout.createSequentialGroup() - .addComponent(accountTypesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(accountTypeRequiredLabel)) - .addGroup(accountTypesPaneLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(unCheckAllAccountTypesButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkAllAccountTypesButton)) - .addGroup(accountTypesPaneLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(accountTypesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))) - .addGap(0, 0, 0)) - ); - accountTypesPaneLayout.setVerticalGroup( - accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(accountTypesPaneLayout.createSequentialGroup() - .addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(accountTypesLabel) - .addComponent(accountTypeRequiredLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(accountTypesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(checkAllAccountTypesButton) - .addComponent(unCheckAllAccountTypesButton))) - ); - - unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N - unCheckAllDevicesButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - unCheckAllDevicesButtonActionPerformed(evt); - } - }); - - devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); // NOI18N - devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N - - checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N - checkAllDevicesButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - checkAllDevicesButtonActionPerformed(evt); - } - }); - - devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 75)); - - devicesListPane.setMinimumSize(new java.awt.Dimension(4, 100)); - devicesListPane.setLayout(new javax.swing.BoxLayout(devicesListPane, javax.swing.BoxLayout.Y_AXIS)); - devicesScrollPane.setViewportView(devicesListPane); - - deviceRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N - deviceRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.deviceRequiredLabel.text")); // NOI18N - deviceRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); - deviceRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); - - javax.swing.GroupLayout devicesPaneLayout = new javax.swing.GroupLayout(devicesPane); - devicesPane.setLayout(devicesPaneLayout); - devicesPaneLayout.setHorizontalGroup( - devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(devicesPaneLayout.createSequentialGroup() - .addComponent(devicesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(deviceRequiredLabel)) - .addGroup(devicesPaneLayout.createSequentialGroup() - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(unCheckAllDevicesButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(checkAllDevicesButton)) - .addGroup(devicesPaneLayout.createSequentialGroup() - .addGap(10, 10, 10) - .addComponent(devicesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - devicesPaneLayout.setVerticalGroup( - devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(devicesPaneLayout.createSequentialGroup() - .addGroup(devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(devicesLabel) - .addComponent(deviceRequiredLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(devicesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(checkAllDevicesButton) - .addComponent(unCheckAllDevicesButton)) - .addGap(5, 5, 5)) - ); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 0, 15, 0); + mainPanel.add(limitPane, gridBagConstraints); startDatePicker.setEnabled(false); @@ -582,97 +570,177 @@ final public class FiltersPanel extends JPanel { .addComponent(endCheckBox))) ); - refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N - refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0); + mainPanel.add(dateRangePane, gridBagConstraints); - needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N - needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0)); + devicesPane.setLayout(new java.awt.GridBagLayout()); - limitHeaderLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitHeaderLabel.text")); // NOI18N - - mostRecentLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.mostRecentLabel.text")); // NOI18N - - limitComboBox.setEditable(true); - limitComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "All", "10000", "5000", "1000", "500", "100" })); - limitComboBox.addActionListener(new java.awt.event.ActionListener() { + unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N + unCheckAllDevicesButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { - limitComboBoxActionPerformed(evt); + unCheckAllDevicesButtonActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 9); + devicesPane.add(unCheckAllDevicesButton, gridBagConstraints); - limitErrorMsgLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N - limitErrorMsgLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitErrorMsgLabel.text")); // NOI18N - limitErrorMsgLabel.setForeground(new java.awt.Color(255, 0, 0)); - limitErrorMsgLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING); + devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); // NOI18N + devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + devicesPane.add(devicesLabel, gridBagConstraints); - javax.swing.GroupLayout limitPaneLayout = new javax.swing.GroupLayout(limitPane); - limitPane.setLayout(limitPaneLayout); - limitPaneLayout.setHorizontalGroup( - limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(limitPaneLayout.createSequentialGroup() - .addComponent(limitHeaderLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(limitErrorMsgLabel) - .addContainerGap()) - .addGroup(limitPaneLayout.createSequentialGroup() - .addContainerGap() - .addComponent(mostRecentLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(limitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); - limitPaneLayout.setVerticalGroup( - limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(limitPaneLayout.createSequentialGroup() - .addContainerGap() - .addGroup(limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(limitHeaderLabel) - .addComponent(limitErrorMsgLabel)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addGroup(limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(mostRecentLabel) - .addComponent(limitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 32, Short.MAX_VALUE)) - ); + checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N + checkAllDevicesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + checkAllDevicesButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + devicesPane.add(checkAllDevicesButton, gridBagConstraints); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(devicesPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(accountTypesPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addComponent(filtersTitleLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(refreshButton)) - .addComponent(dateRangePane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(layout.createSequentialGroup() - .addContainerGap() - .addComponent(needsRefreshLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - .addComponent(limitPane, 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() - .addGap(0, 0, 0) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(filtersTitleLabel) - .addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(refreshButton)) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(needsRefreshLabel) - .addGap(4, 4, 4) - .addComponent(devicesPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(accountTypesPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(dateRangePane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(limitPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) - ); + devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 75)); + + devicesListPane.setMinimumSize(new java.awt.Dimension(4, 100)); + devicesListPane.setLayout(new javax.swing.BoxLayout(devicesListPane, javax.swing.BoxLayout.Y_AXIS)); + devicesScrollPane.setViewportView(devicesListPane); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + devicesPane.add(devicesScrollPane, gridBagConstraints); + + deviceRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N + deviceRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.deviceRequiredLabel.text")); // NOI18N + deviceRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); + deviceRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0); + devicesPane.add(deviceRequiredLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.ipady = 100; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0); + mainPanel.add(devicesPane, gridBagConstraints); + + accountTypesPane.setLayout(new java.awt.GridBagLayout()); + + unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N + unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + unCheckAllAccountTypesButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 9); + accountTypesPane.add(unCheckAllAccountTypesButton, gridBagConstraints); + + accountTypesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/accounts.png"))); // NOI18N + accountTypesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypesLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + accountTypesPane.add(accountTypesLabel, gridBagConstraints); + + checkAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllAccountTypesButton.text")); // NOI18N + checkAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + checkAllAccountTypesButtonActionPerformed(evt); + } + }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + accountTypesPane.add(checkAllAccountTypesButton, gridBagConstraints); + + accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200)); + + accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS)); + accountTypesScrollPane.setViewportView(accountTypeListPane); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.gridwidth = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0); + accountTypesPane.add(accountTypesScrollPane, gridBagConstraints); + + accountTypeRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N + accountTypeRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypeRequiredLabel.text")); // NOI18N + accountTypeRequiredLabel.setForeground(new java.awt.Color(255, 0, 0)); + accountTypeRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridwidth = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST; + accountTypesPane.add(accountTypeRequiredLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0); + mainPanel.add(accountTypesPane, gridBagConstraints); + + scrollPane.setViewportView(mainPanel); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + add(scrollPane, gridBagConstraints); }// //GEN-END:initComponents /** @@ -933,11 +1001,15 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JLabel limitErrorMsgLabel = new javax.swing.JLabel(); private final javax.swing.JLabel limitHeaderLabel = new javax.swing.JLabel(); private final javax.swing.JPanel limitPane = new javax.swing.JPanel(); + private final javax.swing.JPanel limitTitlePanel = new javax.swing.JPanel(); + private final javax.swing.JPanel mainPanel = new javax.swing.JPanel(); private final javax.swing.JLabel mostRecentLabel = new javax.swing.JLabel(); private final javax.swing.JLabel needsRefreshLabel = new javax.swing.JLabel(); private final javax.swing.JButton refreshButton = new javax.swing.JButton(); + private final javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane(); private final javax.swing.JCheckBox startCheckBox = new javax.swing.JCheckBox(); private final com.github.lgooddatepicker.components.DatePicker startDatePicker = new com.github.lgooddatepicker.components.DatePicker(); + private final javax.swing.JPanel topPane = new javax.swing.JPanel(); private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form index a08d9e9b31..bb3b76f532 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.form @@ -11,30 +11,8 @@ + - - - - - - - - - - - - - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java index a7e3e7fd11..fb88ed9312 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/HtmlViewer.java @@ -37,17 +37,18 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(HtmlViewer.class.getName()); - private static final String[] SUPPORTED_MIMETYPES = new String[]{ "text/html", "application/xhtml+xml" }; + private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel(); /** * Creates new form HtmlViewerPanel */ HtmlViewer() { initComponents(); + this.add(htmlPanel); } /** @@ -81,31 +82,12 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer { // //GEN-BEGIN:initComponents private void initComponents() { - htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel(); - - 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() - .addComponent(htmlPanel, 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(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addContainerGap()) - ); + setLayout(new java.awt.BorderLayout()); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel; // End of variables declaration//GEN-END:variables - @Override public List getSupportedMIMETypes() { return Arrays.asList(SUPPORTED_MIMETYPES); diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java index 7aeacc8f27..fbca1e3ab8 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MediaViewImagePanel.java @@ -20,12 +20,12 @@ package org.sleuthkit.autopsy.contentviewers; import java.awt.EventQueue; import java.awt.event.ActionEvent; +import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; @@ -866,15 +866,21 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan @Override protected Void doInBackground() { try { + //Retrieve content viewer tags List tags = Case.getCurrentCase().getServices() .getTagsManager().getContentTagsByContent(file); List> contentViewerTags = getContentViewerTags(tags); + + //Pull out image tag regions Collection regions = contentViewerTags.stream() .map(cvTag -> cvTag.getDetails()).collect(Collectors.toList()); - byte[] jpgImage = ImageTagsUtility.exportTags(file, regions, ".jpg"); + + //Apply tags to image and write to file + BufferedImage pngImage = ImageTagsUtility.writeTags(file, regions, "png"); Path output = Paths.get(exportChooser.getSelectedFile().getPath(), - FilenameUtils.getBaseName(file.getName()) + "-with_tags.jpg"); //NON-NLS - Files.write(output, jpgImage); + FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS + ImageIO.write(pngImage, "png", output.toFile()); + JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport()); } catch (TskCoreException | NoCurrentCaseException | IOException ex) { LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form index 1172483699..cf38160cd6 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.form @@ -271,22 +271,7 @@ - - - - - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java index 48f7e4e82e..0f36c84b9e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/MessageContentViewer.java @@ -87,7 +87,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont private static final int ATTM_TAB_INDEX = 4; private final List textAreas; - + private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel(); /** * Artifact currently being displayed */ @@ -101,6 +101,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont @NbBundle.Messages("MessageContentViewer.AtrachmentsPanel.title=Attachments") public MessageContentViewer() { initComponents(); + htmlPane.add(htmlPanel); envelopePanel.setBackground(new Color(0, 0, 0, 38)); drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new TableFilterNode(Node.EMPTY, false), 0, null); attachmentsScrollPane.setViewportView(drp); @@ -153,7 +154,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont textbodyScrollPane = new javax.swing.JScrollPane(); textbodyTextArea = new javax.swing.JTextArea(); htmlPane = new javax.swing.JPanel(); - htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel(); rtfbodyScrollPane = new javax.swing.JScrollPane(); rtfbodyTextPane = new javax.swing.JTextPane(); attachmentsPanel = new javax.swing.JPanel(); @@ -266,17 +266,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"), textbodyScrollPane); // NOI18N - javax.swing.GroupLayout htmlPaneLayout = new javax.swing.GroupLayout(htmlPane); - htmlPane.setLayout(htmlPaneLayout); - htmlPaneLayout.setHorizontalGroup( - htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE) - ); - htmlPaneLayout.setVerticalGroup( - htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE) - ); - + htmlPane.setLayout(new java.awt.BorderLayout()); msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N rtfbodyScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); @@ -357,7 +347,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont private javax.swing.JScrollPane headersScrollPane; private javax.swing.JTextArea headersTextArea; private javax.swing.JPanel htmlPane; - private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel; private javax.swing.JTabbedPane msgbodyTabbedPane; private javax.swing.JScrollPane rtfbodyScrollPane; private javax.swing.JTextPane rtfbodyTextPane; @@ -534,7 +523,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont textComponent.setCaretPosition(0); //make sure we start at the top } } - + final boolean hasText = attributeText.length() > 0; msgbodyTabbedPane.setEnabledAt(index, hasText); @@ -680,18 +669,18 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont protected Sheet createSheet() { Sheet sheet = super.createSheet(); Set keepProps = new HashSet<>(Arrays.asList( - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), - NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); - + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"), + NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl"))); + //Remove all other props except for the ones above Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES); - for(Property p : sheetSet.getProperties()) { - if(!keepProps.contains(p.getName())){ + for (Property p : sheetSet.getProperties()) { + if (!keepProps.contains(p.getName())) { sheetSet.remove(p.getName()); } } diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java index 885dd6c18f..24421d796c 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagCreator.java @@ -41,7 +41,7 @@ public final class ImageTagCreator extends Rectangle { //Rectangle lines should be 1.5% of the image. This level of thickness has //a good balance between visual acuity and loss of selection at the borders //of the image. - private final static double lineThicknessAsPercent = 1.5; + private final static double LINE_THICKNESS_PERCENT = 1.5; private final double minArea; //Used to update listeners of the new tag boundaries @@ -68,11 +68,11 @@ public final class ImageTagCreator extends Rectangle { //Calculate how many pixels the stroke width should be to guarentee //a consistent % of image consumed by the rectangle border. double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight()); - double lineThicknessPixels = min * lineThicknessAsPercent / 100.0; + double lineThicknessPixels = min * LINE_THICKNESS_PERCENT / 100.0; setStrokeWidth(lineThicknessPixels); minArea = lineThicknessPixels * lineThicknessPixels; setVisible(false); - + this.mousePressed = (MouseEvent event) -> { if (event.isSecondaryButtonDown()) { return; diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java index 1d1d164e2f..3c5fccc5e6 100755 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/imagetagging/ImageTagsUtility.java @@ -18,13 +18,21 @@ */ package org.sleuthkit.autopsy.contentviewers.imagetagging; +import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; import java.util.Collection; +import javax.imageio.ImageIO; import org.opencv.core.Core; import org.opencv.core.Mat; import org.opencv.core.MatOfByte; +import org.opencv.core.MatOfInt; import org.opencv.core.Point; import org.opencv.core.Scalar; +import org.opencv.core.Size; import org.opencv.highgui.Highgui; +import org.opencv.imgproc.Imgproc; import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.TskCoreException; @@ -34,19 +42,42 @@ import org.sleuthkit.datamodel.TskCoreException; public final class ImageTagsUtility { /** - * Embeds the tag regions into an image (represented as an AbstractFile). - * + * Sizes for thumbnails + */ + public enum IconSize { + SMALL(50), + MEDIUM(100), + LARGE(200); + + private final int SIZE; + + IconSize(int size) { + this.SIZE = size; + } + + public int getSize() { + return SIZE; + } + } + + /** + * Embeds the tag regions into an image. + * * @param file Base Image * @param tagRegions Tag regions to be saved into the image - * @param outputEncoding Output file type encoding (ex. .jpg, .png) - * @return output image in byte array - * @throws TskCoreException + * @param outputEncoding Format of image (jpg, png, etc). See OpenCV for + * supported formats. Do not include a "." + * @return Output image as a BufferedImage + * + * @throws TskCoreException Cannot read from abstract file + * @throws IOException Could not create buffered image from OpenCV result */ - public static byte[] exportTags(AbstractFile file, Collection tagRegions, String outputEncoding) throws TskCoreException { + public static BufferedImage writeTags(AbstractFile file, Collection tagRegions, + String outputEncoding) throws TskCoreException, IOException { byte[] imageInMemory = new byte[(int) file.getSize()]; file.read(imageInMemory, 0, file.getSize()); Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_UNCHANGED); - + tagRegions.forEach((region) -> { Core.rectangle( originalImage, //Matrix obj of the image @@ -56,17 +87,55 @@ public final class ImageTagsUtility { (int) Math.rint(region.getStrokeThickness()) ); }); - + MatOfByte matOfByte = new MatOfByte(); - Highgui.imencode(outputEncoding, originalImage, matOfByte); - - originalImage.release(); - byte[] output = matOfByte.toArray(); - matOfByte.release(); - - return output; + MatOfInt params = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 100); + Highgui.imencode("." + outputEncoding, originalImage, matOfByte, params); + + try (ByteArrayInputStream imageStream = new ByteArrayInputStream(matOfByte.toArray())) { + BufferedImage result = ImageIO.read(imageStream); + originalImage.release(); + matOfByte.release(); + return result; + } } - - private ImageTagsUtility(){ + + /** + * Creates a thumbnail version of the image with tags applied. + * + * @param file Input file to apply tags & produce thumbnail from + * @param tagRegions Tags to apply + * @param iconSize Size of the output thumbnail + * @param outputEncoding Format of thumbnail (jpg, png, etc). See OpenCV for + * supported formats. Do not include a "." + * @return BufferedImage representing the thumbnail + * + * @throws TskCoreException Could not read from file + * @throws IOException Could not create buffered image from OpenCV result + */ + public static BufferedImage makeThumbnail(AbstractFile file, Collection tagRegions, + IconSize iconSize, String outputEncoding) throws TskCoreException, IOException { + try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) { + BufferedImage result = writeTags(file, tagRegions, outputEncoding); + ImageIO.write(result, outputEncoding, baos); + Mat markedUpImage = Highgui.imdecode(new MatOfByte(baos.toByteArray()), Highgui.IMREAD_UNCHANGED); + Mat thumbnail = new Mat(); + Size resize = new Size(iconSize.getSize(), iconSize.getSize()); + + Imgproc.resize(markedUpImage, thumbnail, resize); + MatOfByte matOfByte = new MatOfByte(); + Highgui.imencode("." + outputEncoding, thumbnail, matOfByte); + + try (ByteArrayInputStream thumbnailStream = new ByteArrayInputStream(matOfByte.toArray())) { + BufferedImage thumbnailImage = ImageIO.read(thumbnailStream); + thumbnail.release(); + matOfByte.release(); + markedUpImage.release(); + return thumbnailImage; + } + } + } + + private ImageTagsUtility() { } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java index e6c1110038..b192942e71 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerTable.java @@ -301,20 +301,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer { this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); try { - /* - * If the given node is not null and has children, set it as the - * root context of the child OutlineView, otherwise make an - * "empty"node the root context. - * - * IMPORTANT NOTE: This is the first of many times where a - * getChildren call on the current root node causes all of the - * children of the root node to be created and defeats lazy child - * node creation, if it is enabled. It also likely leads to many - * case database round trips. - */ - if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { - this.rootNode = rootNode; - + if (rootNode != null) { /** * Check to see if we have previously created a paging support * class for this node. @@ -363,6 +350,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer { // No-op } }); + } + + /* + * If the given node is not null and has children, set it as the + * root context of the child OutlineView, otherwise make an + * "empty"node the root context. + * + * IMPORTANT NOTE: This is the first of many times where a + * getChildren call on the current root node causes all of the + * children of the root node to be created and defeats lazy child + * node creation, if it is enabled. It also likely leads to many + * case database round trips. + */ + if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) { + this.rootNode = rootNode; this.getExplorerManager().setRootContext(this.rootNode); setupTable(); diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form index a042fd7de6..798b0b51bd 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.form @@ -11,217 +11,278 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - - - + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java index 4f4e41d04e..3199dbd7e8 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataResultViewerThumbnail.java @@ -122,6 +122,11 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { currentPage = -1; totalPages = 0; currentPageImages = 0; + + // The GUI builder is using FlowLayout therefore this change so have no + // impact on the initally designed layout. This change will just effect + // how the components are laid out as size of the window changes. + buttonBarPanel.setLayout(new WrapLayout(java.awt.FlowLayout.LEFT)); } /** @@ -132,33 +137,84 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { + java.awt.GridBagConstraints gridBagConstraints; + buttonBarPanel = new javax.swing.JPanel(); + pagesPanel = new javax.swing.JPanel(); + pageNumberPane = new javax.swing.JPanel(); pageLabel = new javax.swing.JLabel(); + pageNumLabel = new javax.swing.JLabel(); + pageButtonPanel = new javax.swing.JPanel(); pagesLabel = new javax.swing.JLabel(); pagePrevButton = new javax.swing.JButton(); pageNextButton = new javax.swing.JButton(); - imagesLabel = new javax.swing.JLabel(); - imagesRangeLabel = new javax.swing.JLabel(); - pageNumLabel = new javax.swing.JLabel(); - filePathLabel = new javax.swing.JLabel(); + pageGotoPane = new javax.swing.JPanel(); goToPageLabel = new javax.swing.JLabel(); goToPageField = new javax.swing.JTextField(); + imagePane = new javax.swing.JPanel(); + imagesLabel = new javax.swing.JLabel(); + imagesRangeLabel = new javax.swing.JLabel(); thumbnailSizeComboBox = new javax.swing.JComboBox<>(); - iconView = new org.openide.explorer.view.IconView(); - sortButton = new javax.swing.JButton(); + sortPane = new javax.swing.JPanel(); sortLabel = new javax.swing.JLabel(); + sortButton = new javax.swing.JButton(); + filePathLabel = new javax.swing.JLabel(); + iconView = new org.openide.explorer.view.IconView(); + + setLayout(new java.awt.BorderLayout()); + + buttonBarPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT)); + + pagesPanel.setLayout(new java.awt.GridBagLayout()); + + pageNumberPane.setLayout(new java.awt.GridBagLayout()); pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9); + pageNumberPane.add(pageLabel, gridBagConstraints); + + pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNumLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15); + pageNumberPane.add(pageNumLabel, gridBagConstraints); + + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + pagesPanel.add(pageNumberPane, gridBagConstraints); + + buttonBarPanel.add(pagesPanel); + + pageButtonPanel.setLayout(new java.awt.GridBagLayout()); pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagesLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9); + pageButtonPanel.add(pagesLabel, gridBagConstraints); pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagePrevButton.text")); // NOI18N + pagePrevButton.setBorder(null); pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N pagePrevButton.setFocusable(false); pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); - pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23)); + pagePrevButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); pagePrevButton.addActionListener(new java.awt.event.ActionListener() { @@ -166,13 +222,20 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { pagePrevButtonActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + pageButtonPanel.add(pagePrevButton, gridBagConstraints); pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNextButton.text")); // NOI18N + pageNextButton.setBorder(null); pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N pageNextButton.setFocusable(false); pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + pageNextButton.setMargin(new java.awt.Insets(0, 0, 0, 0)); pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23)); pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23)); pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N @@ -182,16 +245,27 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { pageNextButtonActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 2; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 2; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15); + pageButtonPanel.add(pageNextButton, gridBagConstraints); - imagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesLabel.text")); // NOI18N + buttonBarPanel.add(pageButtonPanel); - imagesRangeLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesRangeLabel.text")); // NOI18N - - pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNumLabel.text")); // NOI18N - - filePathLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.filePathLabel.text")); // NOI18N + pageGotoPane.setLayout(new java.awt.GridBagLayout()); goToPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.goToPageLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9); + pageGotoPane.add(goToPageLabel, gridBagConstraints); goToPageField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.goToPageField.text")); // NOI18N goToPageField.addActionListener(new java.awt.event.ActionListener() { @@ -199,12 +273,49 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { goToPageFieldActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.gridheight = 2; + gridBagConstraints.ipadx = 75; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15); + pageGotoPane.add(goToPageField, gridBagConstraints); + + buttonBarPanel.add(pageGotoPane); + + imagePane.setLayout(new java.awt.GridBagLayout()); + + imagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9); + imagePane.add(imagesLabel, gridBagConstraints); + + imagesRangeLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesRangeLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15); + imagePane.add(imagesRangeLabel, gridBagConstraints); + + buttonBarPanel.add(imagePane); thumbnailSizeComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { thumbnailSizeComboBoxActionPerformed(evt); } }); + buttonBarPanel.add(thumbnailSizeComboBox); + + sortPane.setLayout(new java.awt.GridBagLayout()); + + sortLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 1; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.weighty = 1.0; + sortPane.add(sortLabel, gridBagConstraints); sortButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortButton.text")); // NOI18N sortButton.addActionListener(new java.awt.event.ActionListener() { @@ -212,65 +323,20 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { sortButtonActionPerformed(evt); } }); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START; + gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 9); + sortPane.add(sortButton, gridBagConstraints); - sortLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortLabel.text")); // NOI18N + buttonBarPanel.add(sortPane); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addContainerGap() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING) - .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup() - .addComponent(pageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(pagesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) - .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(goToPageLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(12, 12, 12) - .addComponent(imagesLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(30, 30, 30) - .addComponent(sortButton) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(sortLabel)) - .addComponent(filePathLabel, javax.swing.GroupLayout.Alignment.LEADING, 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() - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER) - .addComponent(pageLabel) - .addComponent(pageNumLabel) - .addComponent(pagesLabel) - .addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(goToPageLabel) - .addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(imagesLabel) - .addComponent(imagesRangeLabel) - .addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(sortButton) - .addComponent(sortLabel)) - .addGap(13, 13, 13) - .addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(filePathLabel)) - ); + add(buttonBarPanel, java.awt.BorderLayout.NORTH); + + filePathLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.filePathLabel.text")); // NOI18N + add(filePathLabel, java.awt.BorderLayout.SOUTH); + add(iconView, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed @@ -355,19 +421,26 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JPanel buttonBarPanel; private javax.swing.JLabel filePathLabel; private javax.swing.JTextField goToPageField; private javax.swing.JLabel goToPageLabel; private org.openide.explorer.view.IconView iconView; + private javax.swing.JPanel imagePane; private javax.swing.JLabel imagesLabel; private javax.swing.JLabel imagesRangeLabel; + private javax.swing.JPanel pageButtonPanel; + private javax.swing.JPanel pageGotoPane; private javax.swing.JLabel pageLabel; private javax.swing.JButton pageNextButton; private javax.swing.JLabel pageNumLabel; + private javax.swing.JPanel pageNumberPane; private javax.swing.JButton pagePrevButton; private javax.swing.JLabel pagesLabel; + private javax.swing.JPanel pagesPanel; private javax.swing.JButton sortButton; private javax.swing.JLabel sortLabel; + private javax.swing.JPanel sortPane; private javax.swing.JComboBox thumbnailSizeComboBox; // End of variables declaration//GEN-END:variables @@ -679,5 +752,5 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer { } } } - } + } } diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java index f23caa3c61..3bc034467b 100755 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/ViewPreferencesPanel.java @@ -23,6 +23,7 @@ import java.util.EnumSet; import java.util.Objects; import java.util.TimeZone; import javax.swing.JPanel; +import javax.swing.JSpinner; import org.netbeans.spi.options.OptionsPanelController; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.CasePreferences; @@ -55,6 +56,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel { groupByDataSourceCheckbox.setEnabled(evt.getNewValue() != null); }); this.timeZoneList.setListData(TimeZoneUtils.createTimeZoneList().stream().toArray(String[]::new)); + + // Disable manual editing of max results spinner + ((JSpinner.DefaultEditor)maxResultsSpinner.getEditor()).getTextField().setEditable(false); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/WrapLayout.java b/Core/src/org/sleuthkit/autopsy/corecomponents/WrapLayout.java new file mode 100755 index 0000000000..be35ea0581 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/WrapLayout.java @@ -0,0 +1,203 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2019 Basis Technology Corp. + * Contact: carrier sleuthkit org + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.sleuthkit.autopsy.corecomponents; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** +* FlowLayout subclass that fully supports wrapping of components. +* +* Originally written by Rob Camick +* https://tips4java.wordpress.com/2008/11/06/wrap-layout/ +*/ +class WrapLayout extends FlowLayout { + + /** + * Constructs a new WrapLayout with a left alignment and a + * default 5-unit horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new FlowLayout with the specified alignment + * and a default 5-unit horizontal and vertical gap. The value of the + * alignment argument must be one of WrapLayout, + * WrapLayout, or WrapLayout. + * + * @param align the alignment value + */ + public WrapLayout(int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment and + * the indicated horizontal and vertical gaps. + *

+ * The value of the alignment argument must be one of + * WrapLayout, WrapLayout, or + * WrapLayout. + * + * @param align the alignment value + * @param hgap the horizontal gap between components + * @param vgap the vertical gap between components + */ + public WrapLayout(int align, int hgap, int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the + * visible components in the specified target container. + * + * @param target the component which needs to be laid out + * + * @return the preferred dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension preferredLayoutSize(Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the visible + * components contained in the specified target container. + * + * @param target the component which needs to be laid out + * + * @return the minimum dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension minimumLayoutSize(Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the + * target container. + * + * @param target target to get layout size for + * @param preferred should preferred size be calculated + * + * @return the dimension to layout the target container + */ + private Dimension layoutSize(Container target, boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the containter. + // When the container width = 0, the preferred width of the container + // has not yet been calculated so lets ask for the maximum. + + int targetWidth = target.getSize().width; + Container container = target; + + while (container.getSize().width == 0 && container.getParent() != null) { + container = container.getParent(); + } + + targetWidth = container.getSize().width; + + if (targetWidth == 0) { + targetWidth = Integer.MAX_VALUE; + } + + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + + // Fit components into the allowed width + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + + int nmembers = target.getComponentCount(); + + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); + + // Can't add the component to current row. Start a new row. + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + + // Add a horizontal gap for all components after the first + if (rowWidth != 0) { + rowWidth += hgap; + } + + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + + addRow(dim, rowWidth, rowHeight); + + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + + // When using a scroll pane or the DecoratedLookAndFeel we need to + // make sure the preferred size is less than the size of the + // target containter so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do this. + Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); + + if (scrollPane != null && target.isValid()) { + dim.width -= (hgap + 1); + } + + return dim; + } + } + + /* + * A new row has been completed. Use the dimensions of this row to + * update the preferred size for the container. + * + * @param dim update the width and height when appropriate @param + * rowWidth the width of the row to add @param rowHeight the height of + * the row to add + */ + private void addRow(Dimension dim, int rowWidth, int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + + if (dim.height > 0) { + dim.height += getVgap(); + } + + dim.height += rowHeight; + } + } \ No newline at end of file diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java b/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java index aded120ac7..0f89eb2291 100644 --- a/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java +++ b/Core/src/org/sleuthkit/autopsy/coreutils/StringExtract.java @@ -221,7 +221,8 @@ public class StringExtract { StringExtractResult resWin = null; if (enableUTF8 && resUTF16 != null) { resWin = runUTF16 && resUTF16.numChars > resUTF8.numChars ? resUTF16 : resUTF8; - } else if (enableUTF16) { + } else if (runUTF16) { + //Only let resUTF16 "win" if it was actually run. resWin = resUTF16; } else if (enableUTF8) { resWin = resUTF8; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java index 158f2ef4d9..e9b395f292 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractAbstractFileNode.java @@ -102,7 +102,7 @@ public abstract class AbstractAbstractFileNode extends A // or when tags are added. Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); } - + /** * The finalizer removes event listeners as the BlackboardArtifactNode is * being garbage collected. Yes, we know that finalizers are considered to @@ -123,7 +123,6 @@ public abstract class AbstractAbstractFileNode extends A Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl); } - private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> { String eventType = evt.getPropertyName(); @@ -201,16 +200,16 @@ public abstract class AbstractAbstractFileNode extends A this.setShortDescription(content.getName()); updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName())); } else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) { - SCOData scoData = (SCOData)evt.getNewValue(); + SCOData scoData = (SCOData) evt.getNewValue(); if (scoData.getScoreAndDescription() != null) { updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft())); } if (scoData.getComment() != null) { updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment())); } - if (scoData.getCountAndDescription() != null && - !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { - updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); + if (scoData.getCountAndDescription() != null + && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { + updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); } } }; @@ -331,12 +330,11 @@ public abstract class AbstractAbstractFileNode extends A if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, "")); } - - + // Get the SCO columns data in a background task backgroundTasksPool.submit(new GetSCOTask( - new WeakReference<>(this), weakPcl)); - + new WeakReference<>(this), weakPcl)); + properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content))); properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content))); properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content))); @@ -394,20 +392,19 @@ public abstract class AbstractAbstractFileNode extends A @NbBundle.Messages({ "AbstractAbstractFileNode.createSheet.count.displayName=O", - "AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated", "AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated", - "# {0} - occuranceCount", - "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"}) + "# {0} - occurenceCount", + "AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurences of the MD5 correlation value"}) @Override - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting - String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description(); + String description = defaultDescription; try { //don't perform the query if there is no correlation value - if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) { - count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue()); + if (attributeType != null && StringUtils.isNotBlank(attributeValue)) { + count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue); description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count); - } else if (attribute != null) { + } else if (attributeType != null) { description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description(); } } catch (EamDbException ex) { @@ -415,7 +412,6 @@ public abstract class AbstractAbstractFileNode extends A } catch (CorrelationAttributeNormalizationException ex) { logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex); } - return Pair.of(count, description); } @@ -535,7 +531,7 @@ public abstract class AbstractAbstractFileNode extends A protected List getAllTagsFromDatabase() { return new ArrayList<>(getContentTagsFromDatabase()); } - + @Override protected CorrelationAttributeInstance getCorrelationAttributeInstance() { CorrelationAttributeInstance attribute = null; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java index b9e012e6af..8b1d22d349 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/AbstractContentNode.java @@ -35,6 +35,7 @@ import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; @@ -60,21 +61,21 @@ public abstract class AbstractContentNode extends ContentNode private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName()); /** - * A pool of background tasks to run any long computation needed to - * populate this node. + * A pool of background tasks to run any long computation needed to populate + * this node. */ static final ExecutorService backgroundTasksPool; private static final Integer MAX_POOL_SIZE = 10; - + /** * Default no description string */ @NbBundle.Messages({"AbstractContentNode.nodescription=no description", - "AbstractContentNode.valueLoading=value loading"}) + "AbstractContentNode.valueLoading=value loading"}) protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription(); protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading(); - - /** + + /** * Event signals to indicate the background tasks have completed processing. * Currently, we have one property task in the background: * @@ -84,35 +85,36 @@ public abstract class AbstractContentNode extends ContentNode TRANSLATION_AVAILABLE, SCO_AVAILABLE } - + static { //Initialize this pool only once! This will be used by every instance of AAFN //to do their heavy duty SCO column and translation updates. backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE, new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build()); } + /** * Handles aspects that depend on the Content object * * @param content Underlying Content instances */ AbstractContentNode(T content) { - this(content, Lookups.singleton(content) ); + this(content, Lookups.singleton(content)); } /** * Handles aspects that depend on the Content object * * @param content Underlying Content instances - * @param lookup The Lookup object for the node. + * @param lookup The Lookup object for the node. */ AbstractContentNode(T content, Lookup lookup) { - super(Children.create(new ContentChildren(content), true), lookup); + super(Children.create(new ContentChildren(content), false), lookup); this.content = content; //super.setName(ContentUtils.getSystemName(content)); super.setName("content_" + Long.toString(content.getId())); //NON-NLS } - + /** * Return the content data associated with this node * @@ -141,40 +143,40 @@ public abstract class AbstractContentNode extends ContentNode public boolean hasVisibleContentChildren() { return contentHasVisibleContentChildren(content); } - + /** * Return true if the given content object has children. Useful for lazy * loading. - * + * * @param c The content object to look for children on + * * @return true if has children */ - public static boolean contentHasVisibleContentChildren(Content c){ + public static boolean contentHasVisibleContentChildren(Content c) { if (c != null) { - + try { - if( ! c.hasChildren()) { + if (!c.hasChildren()) { return false; } } catch (TskCoreException ex) { - + logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS return false; } - + String query = "SELECT COUNT(obj_id) AS count FROM " - + " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = " - + TskData.ObjectType.ARTIFACT.getObjectType() - + " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId() - + " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() - + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") " - + " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() - + " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS; - - + + " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = " + + TskData.ObjectType.ARTIFACT.getObjectType() + + " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId() + + " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID() + + " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") " + + " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + + " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS; + try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) { ResultSet resultSet = dbQuery.getResultSet(); - if(resultSet.next()){ + if (resultSet.next()) { return (0 < resultSet.getInt("count")); } } catch (TskCoreException | SQLException | NoCurrentCaseException ex) { @@ -183,7 +185,7 @@ public abstract class AbstractContentNode extends ContentNode } return false; } - + /** * Return true if the underlying content object has children Useful for lazy * loading. @@ -203,7 +205,7 @@ public abstract class AbstractContentNode extends ContentNode return hasChildren; } - + /** * Return ids of children of the underlying content. The ids can be treated * as keys - useful for lazy loading. @@ -281,8 +283,7 @@ public abstract class AbstractContentNode extends ContentNode public int read(byte[] buf, long offset, long len) throws TskException { return content.read(buf, offset, len); } - - + /** * Updates the values of the properties in the current property sheet with * the new properties being passed in. Only if that property exists in the @@ -314,46 +315,51 @@ public abstract class AbstractContentNode extends ContentNode //setSheet() will notify Netbeans to update this node in the UI. this.setSheet(visibleSheet); } - + /** * Reads and returns a list of all tags associated with this content node. - * + * * @return list of tags associated with the node. - */ + */ abstract protected List getAllTagsFromDatabase(); - - /** - * Returns correlation attribute instance for the underlying content of the node. - * - * @return correlation attribute instance for the underlying content of the node. - */ + + /** + * Returns correlation attribute instance for the underlying content of the + * node. + * + * @return correlation attribute instance for the underlying content of the + * node. + */ abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance(); - + /** * Returns Score property for the node. - * + * * @param tags list of tags. - * + * * @return Score property for the underlying content of the node. - */ + */ abstract protected Pair getScorePropertyAndDescription(List tags); - + /** * Returns comment property for the node. - * - * @param tags list of tags + * + * @param tags list of tags * @param attribute correlation attribute instance - * + * * @return Comment property for the underlying content of the node. - */ + */ abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute); - + /** * Returns occurrences/count property for the node. - * - * @param attribute correlation attribute instance - * + * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to count + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * * @return count property for the underlying content of the node. - */ - abstract protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute); + */ + abstract protected Pair getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java index 23ab78fa01..ef96097391 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/BlackboardArtifactNode.java @@ -52,6 +52,7 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent; import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException; import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; @@ -99,7 +100,7 @@ public class BlackboardArtifactNode extends AbstractContentNode> customProperties; - + /* * Artifact types which should have the full unique path of the associated * content as a property. @@ -151,18 +152,17 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft())); } if (scoData.getComment() != null) { updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment())); } - if (scoData.getCountAndDescription() != null && - !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { - updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); + if (scoData.getCountAndDescription() != null + && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) { + updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft())); } } } @@ -333,7 +333,7 @@ public class BlackboardArtifactNode extends AbstractContentNode(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, "")); - sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, "")); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, "")); if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) { sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, "")); } - + // Get the SCO columns data in a background task backgroundTasksPool.submit(new GetSCOTask( - new WeakReference<>(this), weakPcl)); - + new WeakReference<>(this), weakPcl)); + if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) { try { BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT)); @@ -582,6 +582,12 @@ public class BlackboardArtifactNode extends AbstractContentNode t.getName().getDisplayName()).collect(Collectors.joining(", ")))); } + /** + * Gets the correlation attribute for the associated file + * + * @return the correlation attribute for the file associated with this + * BlackboardArtifactNode + */ @Override protected final CorrelationAttributeInstance getCorrelationAttributeInstance() { CorrelationAttributeInstance correlationAttribute = null; @@ -600,19 +606,19 @@ public class BlackboardArtifactNode extends AbstractContentNode tags, CorrelationAttributeInstance attribute) { - HasCommentStatus status = getCommentProperty(tags, attribute ); - sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, + HasCommentStatus status = getCommentProperty(tags, attribute); + sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, status)); } - + /** * Gets the comment property for the node * @@ -620,6 +626,7 @@ public class BlackboardArtifactNode extends AbstractContentNode scoreAndDescription = getScorePropertyAndDescription(tags); sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft())); } - + /** * Get the score property for the node. * - * @param tags the list of tags associated with the file - * - * @return score property and description + * @param tags the list of tags associated with the file + * + * @return score property and description */ @Override protected Pair getScorePropertyAndDescription(List tags) { - Score score = Score.NO_SCORE; + Score score = Score.NO_SCORE; String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description(); if (associated instanceof AbstractFile) { if (((AbstractFile) associated).getKnown() == TskData.FileKnown.BAD) { @@ -721,51 +729,56 @@ public class BlackboardArtifactNode extends AbstractContentNode countAndDescription = getCountPropertyAndDescription(attribute); + Pair countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description()); sheetSet.put( new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft())); - } - + } + /** * Gets the Occurrences property for the node. * - * @param attribute correlation attribute instance - * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to count + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * * @return count and description - * + * */ @Override - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + protected Pair getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription) { Long count = -1L; - String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description(); + String description = defaultDescription; try { //don't perform the query if there is no correlation value - if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) { - count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue()); - description = Bundle.BlackboardArtifactNode_createSheet_count_description(count); - } else if (attribute != null) { - description = Bundle.BlackboardArtifactNode_createSheet_count_hashLookupNotRun_description(); + if (attributeType != null && StringUtils.isNotBlank(attributeValue)) { + count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue); + description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attributeType.getDisplayName()); + } else if (attributeType != null) { + description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description(); } } catch (EamDbException ex) { logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex); @@ -773,12 +786,12 @@ public class BlackboardArtifactNode extends AbstractContentNode 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME; init(); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java index 25069d9564..adc5a34fa7 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/DeletedContent.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -37,7 +36,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -59,7 +57,7 @@ import org.sleuthkit.datamodel.VirtualDirectory; public class DeletedContent implements AutopsyVisitableItem { private SleuthkitCase skCase; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source @NbBundle.Messages({"DeletedContent.fsDelFilter.text=File System", "DeletedContent.allDelFilter.text=All"}) @@ -105,11 +103,11 @@ public class DeletedContent implements AutopsyVisitableItem { public DeletedContent(SleuthkitCase skCase, long dsObjId) { this.skCase = skCase; - this.datasourceObjId = dsObjId; + this.filteringDSObjId = dsObjId; } long filteringDataSourceObjId() { - return this.datasourceObjId; + return this.filteringDSObjId; } @Override @@ -439,7 +437,7 @@ public class DeletedContent implements AutopsyVisitableItem { } - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { + if (filteringDSObjId > 0) { query += " AND data_source_obj_id = " + filteringDSObjId; } return query; diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java index 83fcee1b65..7285b2cb8d 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/EmailExtracted.java @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -40,7 +39,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -88,7 +86,7 @@ public class EmailExtracted implements AutopsyVisitableItem { } private SleuthkitCase skCase; private final EmailResults emailResults; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source @@ -110,7 +108,7 @@ public class EmailExtracted implements AutopsyVisitableItem { */ public EmailExtracted(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; emailResults = new EmailResults(); } @@ -162,8 +160,8 @@ public class EmailExtracted implements AutopsyVisitableItem { + "attribute_type_id=" + pathAttrId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; + if (filteringDSObjId > 0) { + query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java index a21afff190..a51e36eb87 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ExtractedContent.java @@ -26,7 +26,6 @@ import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.List; -import java.util.Objects; import java.util.logging.Level; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -35,7 +34,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -64,7 +62,7 @@ public class ExtractedContent implements AutopsyVisitableItem { private SleuthkitCase skCase; // set to null after case has been closed private Blackboard blackboard; public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text"); - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source /** * Constructs extracted content object @@ -83,7 +81,7 @@ public class ExtractedContent implements AutopsyVisitableItem { */ public ExtractedContent(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; this.blackboard = skCase.getBlackboard(); } @@ -307,8 +305,8 @@ public class ExtractedContent implements AutopsyVisitableItem { protected boolean createKeys(List list) { if (skCase != null) { try { - List types = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? - blackboard.getArtifactTypesInUse(datasourceObjId) : + List types = (filteringDSObjId > 0) ? + blackboard.getArtifactTypesInUse(filteringDSObjId) : skCase.getArtifactTypesInUse() ; types.removeAll(doNotShow); @@ -372,8 +370,8 @@ public class ExtractedContent implements AutopsyVisitableItem { // a performance increase might be had by adding a // "getBlackboardArtifactCount()" method to skCase try { - this.childCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? - blackboard.getArtifactsCount(type.getTypeID(), datasourceObjId) : + this.childCount = (filteringDSObjId > 0) ? + blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId) : skCase.getBlackboardArtifactsTypeCount(type.getTypeID()); } catch (TskException ex) { Logger.getLogger(TypeNode.class.getName()) @@ -501,8 +499,8 @@ public class ExtractedContent implements AutopsyVisitableItem { protected List makeKeys() { if (skCase != null) { try { - return Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? blackboard.getArtifacts(type.getTypeID(), datasourceObjId) + return (filteringDSObjId > 0) + ? blackboard.getArtifacts(type.getTypeID(), filteringDSObjId) : skCase.getBlackboardArtifacts(type.getTypeID()); } catch (TskException ex) { Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java index e9c49c596e..9cceeb5a29 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileSize.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -37,9 +36,7 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.datamodel.AbstractFile; @@ -63,7 +60,7 @@ import org.sleuthkit.datamodel.VirtualDirectory; public class FileSize implements AutopsyVisitableItem { private SleuthkitCase skCase; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source public enum FileSizeFilter implements AutopsyVisitableItem { @@ -105,7 +102,7 @@ public class FileSize implements AutopsyVisitableItem { public FileSize(SleuthkitCase skCase, long dsObjId) { this.skCase = skCase; - this.datasourceObjId = dsObjId; + this.filteringDSObjId = dsObjId; } @Override @@ -118,7 +115,7 @@ public class FileSize implements AutopsyVisitableItem { } long filteringDataSourceObjId() { - return this.datasourceObjId; + return this.filteringDSObjId; } /* * Root node. Children are nodes for specific sizes. @@ -437,7 +434,7 @@ public class FileSize implements AutopsyVisitableItem { query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS // filter by datasource if indicated in case preferences - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { + if (filteringDSObjId > 0) { query += " AND data_source_obj_id = " + filteringDSObjId; } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java index b3005c28b4..34ec74280a 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByExtension.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -39,7 +38,6 @@ import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.coreutils.Logger; @@ -366,7 +364,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem { + (UserPreferences.hideKnownFilesInViewsTree() ? " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")" : " ") - + (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) + + (filteringDataSourceObjId() > 0 ? " AND data_source_obj_id = " + filteringDataSourceObjId() : " ") + " AND (extension IN (" + filter.getFilter().stream() diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java index 1e4f61fc87..86cc42aa8c 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/FileTypesByMimeType.java @@ -28,7 +28,6 @@ import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -42,7 +41,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree; import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree; @@ -103,7 +101,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi + TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal() + (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal())) + "))" - + ( Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ") + + ( (filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ") + (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : ""); } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java index b7f4e38a5e..bcdca8632e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/GetSCOTask.java @@ -22,30 +22,42 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.lang.ref.WeakReference; import java.util.List; +import java.util.logging.Level; +import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance; +import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil; +import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; import org.sleuthkit.autopsy.core.UserPreferences; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.events.AutopsyEvent; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.Tag; +import org.sleuthkit.datamodel.TskCoreException; /** - * Background task to get Score, Comment and Occurrences values for an - * Abstract content node. - * + * Background task to get Score, Comment and Occurrences values for an Abstract + * content node. + * */ class GetSCOTask implements Runnable { private final WeakReference> weakNodeRef; private final PropertyChangeListener listener; + private static final Logger logger = Logger.getLogger(GetSCOTask.class.getName()); GetSCOTask(WeakReference> weakContentRef, PropertyChangeListener listener) { this.weakNodeRef = weakContentRef; this.listener = listener; } + @Messages({"GetSCOTask.occurrences.defaultDescription=No correlation properties found", + "GetSCOTask.occurrences.multipleProperties=Multiple different correlation properties exist for this result"}) @Override public void run() { AbstractContentNode contentNode = weakNodeRef.get(); - + //Check for stale reference if (contentNode == null) { return; @@ -53,17 +65,61 @@ class GetSCOTask implements Runnable { // get the SCO column values List tags = contentNode.getAllTagsFromDatabase(); - CorrelationAttributeInstance attribute = contentNode.getCorrelationAttributeInstance(); + CorrelationAttributeInstance fileAttribute = contentNode.getCorrelationAttributeInstance(); SCOData scoData = new SCOData(); scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags)); - scoData.setComment(contentNode.getCommentProperty(tags, attribute)); + scoData.setComment(contentNode.getCommentProperty(tags, fileAttribute)); + if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) { - scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(attribute)); + Type type = null; + String value = null; + String description = Bundle.GetSCOTask_occurrences_defaultDescription(); + if (contentNode instanceof BlackboardArtifactNode) { + BlackboardArtifact bbArtifact = ((BlackboardArtifactNode) contentNode).getArtifact(); + //for specific artifact types we still want to display information for the file instance correlation attribute + if (bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID() + || bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { + try { + if (bbArtifact.getParent() instanceof AbstractFile) { + type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); + value = ((AbstractFile) bbArtifact.getParent()).getMd5Hash(); + } + } catch (TskCoreException | EamDbException ex) { + logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex); + } + } else { + List listOfPossibleAttributes = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false); + if (listOfPossibleAttributes.size() > 1) { + //Don't display anything if there is more than 1 correlation property for an artifact but let the user know + description = Bundle.GetSCOTask_occurrences_multipleProperties(); + } else if (!listOfPossibleAttributes.isEmpty()) { + //there should only be one item in the list + type = listOfPossibleAttributes.get(0).getCorrelationType(); + value = listOfPossibleAttributes.get(0).getCorrelationValue(); + } + } + } else if (contentNode.getContent() instanceof AbstractFile) { + //use the file instance correlation attribute if the node is not a BlackboardArtifactNode + try { + type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID); + value = ((AbstractFile) contentNode.getContent()).getMd5Hash(); + } catch (EamDbException ex) { + logger.log(Level.WARNING, "Unable to get correlation type to determine value for O column for file", ex); + } + } + scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(type, value, description)); } - + // signal SCO data is available. - if (listener != null) { + if (listener + != null) { listener.propertyChange(new PropertyChangeEvent( AutopsyEvent.SourceType.LOCAL.toString(), AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(), diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java index 9d6ed53431..03c72d2d2e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/HashsetHits.java @@ -30,7 +30,6 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -42,7 +41,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -65,7 +63,7 @@ public class HashsetHits implements AutopsyVisitableItem { private static final Logger logger = Logger.getLogger(HashsetHits.class.getName()); private SleuthkitCase skCase; private final HashsetResults hashsetResults; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source /** @@ -87,7 +85,7 @@ public class HashsetHits implements AutopsyVisitableItem { */ public HashsetHits(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; hashsetResults = new HashsetResults(); } @@ -142,8 +140,8 @@ public class HashsetHits implements AutopsyVisitableItem { + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; + if (filteringDSObjId > 0) { + query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java index b2c80f1322..da82c315b5 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ImageNode.java @@ -130,7 +130,7 @@ public class ImageNode extends AbstractContentNode { "ImageNode.createSheet.type.text=Image", "ImageNode.createSheet.sectorSize.name=Sector Size (Bytes)", "ImageNode.createSheet.sectorSize.displayName=Sector Size (Bytes)", - "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.", + "ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.", "ImageNode.createSheet.timezone.name=Timezone", "ImageNode.createSheet.timezone.displayName=Timezone", "ImageNode.createSheet.timezone.desc=Timezone of the image", @@ -257,64 +257,72 @@ public class ImageNode extends AbstractContentNode { /** * Reads and returns a list of all tags associated with this content node. * - * Null implementation of an abstract method. - * + * Null implementation of an abstract method. + * * @return list of tags associated with the node. - */ + */ @Override protected List getAllTagsFromDatabase() { return new ArrayList<>(); } + /** - * Returns correlation attribute instance for the underlying content of the node. - * - * Null implementation of an abstract method. - * - * @return correlation attribute instance for the underlying content of the node. - */ + * Returns correlation attribute instance for the underlying content of the + * node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the + * node. + */ @Override protected CorrelationAttributeInstance getCorrelationAttributeInstance() { return null; } - + /** * Returns Score property for the node. - * - * Null implementation of an abstract method. - * + * + * Null implementation of an abstract method. + * * @param tags list of tags. - * + * * @return Score property for the underlying content of the node. - */ + */ @Override protected Pair getScorePropertyAndDescription(List tags) { return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); } + /** * Returns comment property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags + * + * Null implementation of an abstract method. + * + * @param tags list of tags * @param attribute correlation attribute instance - * + * * @return Comment property for the underlying content of the node. - */ + */ @Override protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { return DataResultViewerTable.HasCommentStatus.NO_COMMENT; } + /** * Returns occurrences/count property for the node. * - * Null implementation of an abstract method. - * - * @param attribute correlation attribute instance - * + * Null implementation of an abstract method. + * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to coun + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * * @return count property for the underlying content of the node. - */ + */ @Override - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { return Pair.of(-1L, NO_DESCR); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java index c36833992c..482ebf1558 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/InterestingHits.java @@ -30,7 +30,6 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -42,7 +41,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.ingest.IngestManager; @@ -61,7 +59,7 @@ public class InterestingHits implements AutopsyVisitableItem { private static final Logger logger = Logger.getLogger(InterestingHits.class.getName()); private SleuthkitCase skCase; private final InterestingResults interestingResults = new InterestingResults(); - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source /** * Constructor @@ -82,7 +80,7 @@ public class InterestingHits implements AutopsyVisitableItem { */ public InterestingHits(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; interestingResults.update(); } @@ -133,8 +131,8 @@ public class InterestingHits implements AutopsyVisitableItem { + "attribute_type_id=" + setNameId //NON-NLS + " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS + " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; + if (filteringDSObjId > 0) { + query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } try (CaseDbQuery dbQuery = skCase.executeQuery(query)) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java index cb24744b0c..655f0c1973 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/KeywordHits.java @@ -31,7 +31,6 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -45,7 +44,6 @@ import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.coreutils.Logger; import static org.sleuthkit.autopsy.datamodel.Bundle.*; @@ -76,7 +74,7 @@ public class KeywordHits implements AutopsyVisitableItem { private SleuthkitCase skCase; private final KeywordResults keywordResults; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source /** * String used in the instance MAP so that exact matches and substring can @@ -123,7 +121,7 @@ public class KeywordHits implements AutopsyVisitableItem { */ public KeywordHits(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; keywordResults = new KeywordResults(); } @@ -344,8 +342,8 @@ public class KeywordHits implements AutopsyVisitableItem { } String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY; - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId; + if (filteringDSObjId > 0) { + queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId; } try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) { diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java index 66291955a8..c5d77692c8 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/RootContentChildren.java @@ -25,6 +25,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.datamodel.accounts.Accounts; +import org.sleuthkit.datamodel.SleuthkitVisitableItem; /** * Children implementation for the root node of a ContentNode tree. Accepts a @@ -34,6 +35,7 @@ public class RootContentChildren extends Children.Keys { private final Collection contentKeys; private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor(); + private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor(); /** * @param contentKeys root Content objects for the Node tree @@ -68,7 +70,7 @@ public class RootContentChildren extends Children.Keys { if (key instanceof AutopsyVisitableItem) { return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)}; } else { - return null; + return new Node[] {((SleuthkitVisitableItem)key).accept(createSleuthkitNodeVisitor)}; } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java index abf77350d2..2dfc02678b 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/Tags.java @@ -23,7 +23,6 @@ import java.beans.PropertyChangeListener; import java.util.Collections; import java.util.EnumSet; import java.util.List; -import java.util.Objects; import java.util.Observable; import java.util.Observer; import java.util.Set; @@ -35,7 +34,6 @@ import org.openide.nodes.Sheet; import org.openide.util.NbBundle; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; import org.sleuthkit.autopsy.casemodule.services.TagsManager; import org.sleuthkit.autopsy.core.UserPreferences; @@ -61,14 +59,14 @@ public class Tags implements AutopsyVisitableItem { private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source Tags() { this(0); } Tags(long dsObjId) { - this.datasourceObjId = dsObjId; + this.filteringDSObjId = dsObjId; } /** @@ -81,7 +79,7 @@ public class Tags implements AutopsyVisitableItem { } long filteringDataSourceObjId() { - return this.datasourceObjId; + return this.filteringDSObjId; } @Override @@ -156,7 +154,7 @@ public class Tags implements AutopsyVisitableItem { private class TagNameNodeFactory extends ChildFactory.Detachable implements Observer { - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source private final Set CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED, Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED, @@ -219,7 +217,7 @@ public class Tags implements AutopsyVisitableItem { * @param objId data source object id */ TagNameNodeFactory(long objId) { - this.datasourceObjId = objId; + this.filteringDSObjId = objId; } @@ -246,12 +244,12 @@ public class Tags implements AutopsyVisitableItem { List tagNamesInUse; if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); - tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName) + tagNamesInUse = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(filteringDSObjId, userName) : Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName); } else { - tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId) + tagNamesInUse = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(); } Collections.sort(tagNamesInUse); @@ -303,17 +301,17 @@ public class Tags implements AutopsyVisitableItem { TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager(); if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName); - tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName); + if (filteringDSObjId > 0) { + tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName); + tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName); } else { tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName); tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName); } } else { - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId); - tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId); + if (filteringDSObjId > 0) { + tagsCount = tm.getContentTagsCountByTagName(tagName, filteringDSObjId); + tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId); } else { tagsCount = tm.getContentTagsCountByTagName(tagName); tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName); @@ -424,12 +422,12 @@ public class Tags implements AutopsyVisitableItem { if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); - tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName) + tagsCount = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName) : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName); } else { - tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId) + tagsCount = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName); } } catch (TskCoreException | NoCurrentCaseException ex) { @@ -486,8 +484,8 @@ public class Tags implements AutopsyVisitableItem { protected boolean createKeys(List keys) { // Use the content tags bearing the specified tag name as the keys. try { - List contentTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId) + List contentTags = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName); if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); @@ -544,12 +542,12 @@ public class Tags implements AutopsyVisitableItem { try { if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); - tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName) + tagsCount = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName) : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName); } else { - tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId) + tagsCount = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName); } } catch (TskCoreException | NoCurrentCaseException ex) { @@ -606,8 +604,8 @@ public class Tags implements AutopsyVisitableItem { protected boolean createKeys(List keys) { try { // Use the blackboard artifact tags bearing the specified tag name as the keys. - List artifactTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) - ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId) + List artifactTags = (filteringDSObjId > 0) + ? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, filteringDSObjId) : Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName); if (UserPreferences.showOnlyCurrentUserTags()) { String userName = System.getProperty(USER_NAME_PROPERTY); diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java index 233d098071..307013136f 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/VolumeNode.java @@ -50,6 +50,7 @@ import org.sleuthkit.datamodel.Tag; * root directory of a file system */ public class VolumeNode extends AbstractContentNode { + private static final Logger logger = Logger.getLogger(VolumeNode.class.getName()); /** @@ -217,67 +218,76 @@ public class VolumeNode extends AbstractContentNode { public String getItemType() { return DisplayableItemNode.FILE_PARENT_NODE_KEY; } + /** * Reads and returns a list of all tags associated with this content node. * - * Null implementation of an abstract method. - * + * Null implementation of an abstract method. + * * @return list of tags associated with the node. - */ + */ @Override protected List getAllTagsFromDatabase() { return new ArrayList<>(); } + /** - * Returns correlation attribute instance for the underlying content of the node. - * - * Null implementation of an abstract method. - * - * @return correlation attribute instance for the underlying content of the node. - */ + * Returns correlation attribute instance for the underlying content of the + * node. + * + * Null implementation of an abstract method. + * + * @return correlation attribute instance for the underlying content of the + * node. + */ @Override protected CorrelationAttributeInstance getCorrelationAttributeInstance() { return null; } - + /** * Returns Score property for the node. - * - * Null implementation of an abstract method. - * + * + * Null implementation of an abstract method. + * * @param tags list of tags. - * + * * @return Score property for the underlying content of the node. - */ + */ @Override protected Pair getScorePropertyAndDescription(List tags) { return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR); } + /** * Returns comment property for the node. - * - * Null implementation of an abstract method. - * - * @param tags list of tags + * + * Null implementation of an abstract method. + * + * @param tags list of tags * @param attribute correlation attribute instance - * + * * @return Comment property for the underlying content of the node. - */ + */ @Override protected DataResultViewerTable.HasCommentStatus getCommentProperty(List tags, CorrelationAttributeInstance attribute) { return DataResultViewerTable.HasCommentStatus.NO_COMMENT; } + /** * Returns occurrences/count property for the node. * - * Null implementation of an abstract method. - * - * @param attribute correlation attribute instance - * + * Null implementation of an abstract method. + * + * @param attributeType the type of the attribute to count + * @param attributeValue the value of the attribute to coun + * @param defaultDescription a description to use when none is determined by + * the getCountPropertyAndDescription method + * * @return count property for the underlying content of the node. - */ + */ @Override - protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance attribute) { + protected Pair getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) { return Pair.of(-1L, NO_DESCR); } } diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java index d8e40d8a3e..4cd6e330e0 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/accounts/Accounts.java @@ -57,9 +57,7 @@ import org.openide.util.NbBundle; import org.openide.util.Utilities; import org.openide.util.lookup.Lookups; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.casemodule.CasePreferences; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor; @@ -96,7 +94,7 @@ final public class Accounts implements AutopsyVisitableItem { final public static String NAME = Bundle.AccountsRootNode_name(); private SleuthkitCase skCase; - private final long datasourceObjId; + private final long filteringDSObjId; // 0 if not filtering/grouping by data source private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus"); @@ -123,7 +121,7 @@ final public class Accounts implements AutopsyVisitableItem { */ public Accounts(SleuthkitCase skCase, long objId) { this.skCase = skCase; - this.datasourceObjId = objId; + this.filteringDSObjId = objId; this.rejectActionInstance = new RejectAccounts(); this.approveActionInstance = new ApproveAccounts(); @@ -153,8 +151,8 @@ final public class Accounts implements AutopsyVisitableItem { * based on the CasePreferences groupItemsInTreeByDataSource setting */ private String getFilterByDataSourceClause() { - if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) { - return " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId + " "; + if (filteringDSObjId > 0) { + return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " "; } return " "; diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties index 35378ab1a3..b3732ce149 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties @@ -6,7 +6,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets. -FilesSetPanel.interesting.title=Interesting Files Set +FilesSetPanel.interesting.title=Interesting Files Set Rule FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named. FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name. FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files @@ -31,13 +31,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, * FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression. FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0} FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists. -FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator +FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest: FilesSetRulePanel.jLabel1.text=Type: FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find. FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on. FilesSetRulePanel.nameCheck.text=Name: -FilesSetRulePanel.pathCheck.text=Path Substring: +FilesSetRulePanel.pathCheck.text=Folder Name: FilesSetRulePanel.filesRadioButton.text=Files FilesSetRulePanel.dirsRadioButton.text=Directories FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets: diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED index 7ca4901b1b..ad6fe6f1c9 100755 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/Bundle.properties-MERGED @@ -37,6 +37,8 @@ FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters... FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named. FilesSetPanel.rule.title=File Filter Rule FilesSetRulePanel.bytes=Bytes +#{0} - regex +FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: "{0}") FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty. FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer. FilesSetRulePanel.gigaBytes=Gigabytes @@ -60,7 +62,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets. -FilesSetPanel.interesting.title=Interesting Files Set +FilesSetPanel.interesting.title=Interesting Files Set Rule FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named. FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name. FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files @@ -85,13 +87,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, * FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression. FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0} FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists. -FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator +FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest: FilesSetRulePanel.jLabel1.text=Type: FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find. FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on. FilesSetRulePanel.nameCheck.text=Name: -FilesSetRulePanel.pathCheck.text=Path Substring: +FilesSetRulePanel.pathCheck.text=Folder Name: FilesSetRulePanel.filesRadioButton.text=Files FilesSetRulePanel.dirsRadioButton.text=Directories FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets: diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form index 416aad285c..7497f936bb 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.form @@ -832,6 +832,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java index 25db05decb..5ff9d2a172 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetDefsPanel.java @@ -811,6 +811,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp org.openide.awt.Mnemonics.setLocalizedText(jLabel7, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.jLabel7.text")); // NOI18N mimeTypeComboBox.setBackground(new java.awt.Color(240, 240, 240)); + mimeTypeComboBox.setEditable(true); mimeTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] {""})); mimeTypeComboBox.setEnabled(false); mimeTypeComboBox.setMinimumSize(new java.awt.Dimension(0, 20)); diff --git a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java index 9744b0c5bf..90cc881ad8 100644 --- a/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java +++ b/Core/src/org/sleuthkit/autopsy/modules/interestingitems/FilesSetRulePanel.java @@ -57,7 +57,9 @@ final class FilesSetRulePanel extends javax.swing.JPanel { "FilesSetRulePanel.NoPathError=Path cannot be empty", "FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty.", "FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer.", - "FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected)." + "FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected).", + "#{0} - regex", + "FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: \"{0}\")", }) private static final long serialVersionUID = 1L; @@ -130,6 +132,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel { this.setButtons(okButton, cancelButton); updateNameTextFieldPrompt(); + setComponentsForSearchType(); } /** @@ -358,6 +361,16 @@ final class FilesSetRulePanel extends javax.swing.JPanel { return false; } if (this.nameRegexCheckbox.isSelected()) { + + // If extension is also selected and the regex contains a comma, display a warning + // since it is unclear whether the comma is part of a regex or is separating extensions. + if (this.extensionRadioButton.isSelected() && this.nameTextField.getText().contains(",")) { + NotifyDescriptor notifyDesc = new NotifyDescriptor.Message( + Bundle.FilesSetRulePanel_CommaInRegexWarning(this.nameTextField.getText()), + NotifyDescriptor.WARNING_MESSAGE); + DialogDisplayer.getDefault().notify(notifyDesc); + } + try { Pattern.compile(this.nameTextField.getText()); } catch (PatternSyntaxException ex) { diff --git a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java index b54af094ad..be1ec45965 100644 --- a/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java +++ b/Core/src/org/sleuthkit/autopsy/report/ReportHTML.java @@ -48,18 +48,18 @@ import java.util.TreeMap; import java.util.logging.Level; import javax.imageio.ImageIO; import javax.swing.JPanel; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringEscapeUtils; import org.openide.filesystems.FileUtil; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException; -import org.sleuthkit.autopsy.casemodule.services.Services; import org.sleuthkit.autopsy.casemodule.services.TagsManager; -import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException; -import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager; +import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion; +import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility; import org.sleuthkit.autopsy.coreutils.EscapeUtil; import org.sleuthkit.autopsy.coreutils.ImageUtils; import org.sleuthkit.autopsy.coreutils.Logger; @@ -731,6 +731,29 @@ class ReportHTML implements TableReportModule { logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS } } + + /** + * Finds all associated image tags. + * + * @param contentTags + * @return + */ + private List getTaggedRegions(List contentTags) { + ArrayList tagRegions = new ArrayList<>(); + contentTags.forEach((contentTag) -> { + try { + ContentViewerTag contentViewerTag = ContentViewerTagManager + .getTag(contentTag, ImageTagRegion.class); + if (contentViewerTag != null) { + tagRegions.add(contentViewerTag.getDetails()); + } + } catch (TskCoreException | NoCurrentCaseException ex) { + logger.log(Level.WARNING, "Could not get content viewer tag " + + "from case db for content_tag with id %d", contentTag.getId()); + } + }); + return tagRegions; + } /** * Add the body of the thumbnails table. @@ -770,13 +793,54 @@ class ReportHTML implements TableReportModule { } AbstractFile file = (AbstractFile) content; + List contentTags = new ArrayList<>(); + + String thumbnailPath = null; + String imageWithTagsFullPath = null; + try { + //Get content tags and all image tags + contentTags = Case.getCurrentCase().getServices() + .getTagsManager().getContentTagsByContent(file); + List imageTags = getTaggedRegions(contentTags); + + if(!imageTags.isEmpty()) { + //Write the tags to the fullsize and thumbnail images + BufferedImage fullImageWithTags = ImageTagsUtility.writeTags(file, imageTags, "png"); + + BufferedImage thumbnailImageWithTags = ImageTagsUtility.makeThumbnail(file, + imageTags, ImageTagsUtility.IconSize.MEDIUM, "png"); + + String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName()); + + //Create paths in report to write tagged images + File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) + ".png").toFile(); + String fullImageWithTagsPath = makeCustomUniqueFilePath(file, "thumbs_fullsize"); + fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) + ".png"; + File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile(); + + //Save images + ImageIO.write(thumbnailImageWithTags, "png", thumbnailImageWithTagsFile); + ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile); + + thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName(); + //Relative path + imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length()); + } + } catch (TskCoreException ex) { + logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS + } catch (IOException ex) { + logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS + } // save copies of the orginal image and thumbnail image - String thumbnailPath = prepareThumbnail(file); + if(thumbnailPath == null) { + thumbnailPath = prepareThumbnail(file); + } + if (thumbnailPath == null) { continue; } - String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS + String contentPath = saveContent(file, "original"); //NON-NLS String nameInImage; try { nameInImage = file.getUniquePath(); @@ -785,32 +849,27 @@ class ReportHTML implements TableReportModule { } StringBuilder linkToThumbnail = new StringBuilder(); - linkToThumbnail.append("