diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form index fc3eb5b61f..610873d75f 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.form @@ -50,7 +50,7 @@ - + @@ -65,7 +65,7 @@ - + @@ -82,7 +82,14 @@ - + + + + + + + + @@ -99,33 +106,20 @@ - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - - @@ -242,6 +236,15 @@ + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java index c7226483ef..3ed728006b 100644 --- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java +++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerArtifact.java @@ -1,15 +1,15 @@ /* * Autopsy Forensic Browser - * + * * Copyright 2011-2013 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. @@ -18,32 +18,37 @@ */ package org.sleuthkit.autopsy.corecomponents; -import java.awt.*; +import java.awt.Component; +import java.awt.Cursor; +import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.datatransfer.StringSelection; +import java.awt.event.KeyEvent; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.logging.Level; import javax.swing.JMenuItem; -import javax.swing.JTextPane; +import javax.swing.KeyStroke; import javax.swing.SwingWorker; +import javax.swing.table.DefaultTableModel; +import org.apache.commons.lang.StringUtils; import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ServiceProvider; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.contentviewers.Utilities; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.autopsy.datamodel.ArtifactStringContent; +import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE; import org.sleuthkit.datamodel.BlackboardAttribute; import org.sleuthkit.datamodel.Content; -import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; +import org.netbeans.swing.etable.ETable; /** * Instances of this class display the BlackboardArtifacts associated with the @@ -53,21 +58,57 @@ import org.sleuthkit.datamodel.TskException; @ServiceProvider(service = DataContentViewer.class, position = 3) public class DataContentViewerArtifact extends javax.swing.JPanel implements DataContentViewer { + @NbBundle.Messages({ + "DataContentViewerArtifact.attrsTableHeader.type=Type", + "DataContentViewerArtifact.attrsTableHeader.value=Value", + "DataContentViewerArtifact.attrsTableHeader.sources=Source(s)", + "DataContentViewerArtifact.failedToGetSourcePath.message=Failed to get source file path from case database", + "DataContentViewerArtifact.failedToGetAttributes.message=Failed to get some or all attributes from case database" + }) private final static Logger logger = Logger.getLogger(DataContentViewerArtifact.class.getName()); private final static String WAIT_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.waitText"); private final static String ERROR_TEXT = NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.errorText"); private Node currentNode; // @@@ Remove this when the redundant setNode() calls problem is fixed. private int currentPage = 1; private final Object lock = new Object(); - private List artifactContentStrings; // Accessed by multiple threads, use getArtifactContentStrings() and setArtifactContentStrings() + private List artifactTableContents; // Accessed by multiple threads, use getArtifactContents() and setArtifactContents() SwingWorker currentTask; // Accessed by multiple threads, use startNewTask() + private static final String[] COLUMN_HEADERS = { + Bundle.DataContentViewerArtifact_attrsTableHeader_type(), + Bundle.DataContentViewerArtifact_attrsTableHeader_value(), + Bundle.DataContentViewerArtifact_attrsTableHeader_sources()}; public DataContentViewerArtifact() { + initResultsTable(); initComponents(); + resultsTableScrollPane.setViewportView(resultsTable); customizeComponents(); resetComponents(); } + private void initResultsTable() { + resultsTable = new ETable(); + resultsTable.setModel(new javax.swing.table.DefaultTableModel() { + private static final long serialVersionUID = 1L; + + public boolean isCellEditable(int rowIndex, int columnIndex) { + return false; + } + }); + resultsTable.setCellSelectionEnabled(true); + resultsTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_INTERVAL_SELECTION); + updateColumnSizes(); + } + + private void updateColumnSizes() { + resultsTable.setAutoResizeMode(javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN); + if (resultsTable.getColumnModel().getColumnCount() > 0) { + resultsTable.getColumnModel().getColumn(0).setPreferredWidth(100); + resultsTable.getColumnModel().getColumn(1).setPreferredWidth(800); + resultsTable.getColumnModel().getColumn(2).setPreferredWidth(100); + } + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always @@ -81,175 +122,191 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat copyMenuItem = new javax.swing.JMenuItem(); selectAllMenuItem = new javax.swing.JMenuItem(); jPanel1 = new javax.swing.JPanel(); - jScrollPane1 = new javax.swing.JScrollPane(); - outputViewPane = new JTextPane(){ - public boolean getScrollableTracksViewportWidth() { - return (getSize().width < 400); - }}; - totalPageLabel = new javax.swing.JLabel(); - ofLabel = new javax.swing.JLabel(); - currentPageLabel = new javax.swing.JLabel(); - pageLabel = new javax.swing.JLabel(); - nextPageButton = new javax.swing.JButton(); - pageLabel2 = new javax.swing.JLabel(); - prevPageButton = new javax.swing.JButton(); + totalPageLabel = new javax.swing.JLabel(); + ofLabel = new javax.swing.JLabel(); + currentPageLabel = new javax.swing.JLabel(); + pageLabel = new javax.swing.JLabel(); + nextPageButton = new javax.swing.JButton(); + pageLabel2 = new javax.swing.JLabel(); + prevPageButton = new javax.swing.JButton(); + resultsTableScrollPane = new javax.swing.JScrollPane(); + artifactLabel = new javax.swing.JLabel(); - copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N - rightClickMenu.add(copyMenuItem); + copyMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.copyMenuItem.text")); // NOI18N + rightClickMenu.add(copyMenuItem); - selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N - rightClickMenu.add(selectAllMenuItem); + selectAllMenuItem.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.selectAllMenuItem.text")); // NOI18N + rightClickMenu.add(selectAllMenuItem); - setPreferredSize(new java.awt.Dimension(622, 424)); + setPreferredSize(new java.awt.Dimension(622, 424)); - jPanel1.setPreferredSize(new java.awt.Dimension(622, 424)); + jPanel1.setPreferredSize(new java.awt.Dimension(622, 424)); - outputViewPane.setEditable(false); - outputViewPane.setPreferredSize(new java.awt.Dimension(700, 400)); - jScrollPane1.setViewportView(outputViewPane); + totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N - totalPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.totalPageLabel.text")); // NOI18N + ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N - ofLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.ofLabel.text")); // NOI18N + currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N + currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14)); + currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14)); + currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14)); - currentPageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.currentPageLabel.text")); // NOI18N - currentPageLabel.setMaximumSize(new java.awt.Dimension(18, 14)); - currentPageLabel.setMinimumSize(new java.awt.Dimension(18, 14)); - currentPageLabel.setPreferredSize(new java.awt.Dimension(18, 14)); + pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N + pageLabel.setMaximumSize(new java.awt.Dimension(33, 14)); + pageLabel.setMinimumSize(new java.awt.Dimension(33, 14)); + pageLabel.setPreferredSize(new java.awt.Dimension(33, 14)); - pageLabel.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel.text")); // NOI18N - pageLabel.setMaximumSize(new java.awt.Dimension(33, 14)); - pageLabel.setMinimumSize(new java.awt.Dimension(33, 14)); - pageLabel.setPreferredSize(new java.awt.Dimension(33, 14)); + nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N + nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N + nextPageButton.setBorderPainted(false); + nextPageButton.setContentAreaFilled(false); + nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N + nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); + nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N + nextPageButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + nextPageButtonActionPerformed(evt); + } + }); - nextPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N NON-NLS - nextPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.nextPageButton.text")); // NOI18N - nextPageButton.setBorderPainted(false); - nextPageButton.setContentAreaFilled(false); - nextPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N NON-NLS - nextPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); - nextPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); - nextPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N NON-NLS - nextPageButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - nextPageButtonActionPerformed(evt); - } - }); + pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N + pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14)); + pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14)); + pageLabel2.setPreferredSize(new java.awt.Dimension(29, 14)); - pageLabel2.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.pageLabel2.text")); // NOI18N - pageLabel2.setMaximumSize(new java.awt.Dimension(29, 14)); - pageLabel2.setMinimumSize(new java.awt.Dimension(29, 14)); - pageLabel2.setPreferredSize(new java.awt.Dimension(29, 14)); + prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N + prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N + prevPageButton.setBorderPainted(false); + prevPageButton.setContentAreaFilled(false); + prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N + prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); + prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); + prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N + prevPageButton.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + prevPageButtonActionPerformed(evt); + } + }); - prevPageButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N NON-NLS - prevPageButton.setText(org.openide.util.NbBundle.getMessage(DataContentViewerArtifact.class, "DataContentViewerArtifact.prevPageButton.text")); // NOI18N - prevPageButton.setBorderPainted(false); - prevPageButton.setContentAreaFilled(false); - prevPageButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N NON-NLS - prevPageButton.setMargin(new java.awt.Insets(2, 0, 2, 0)); - prevPageButton.setPreferredSize(new java.awt.Dimension(23, 23)); - prevPageButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N NON-NLS - prevPageButton.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - prevPageButtonActionPerformed(evt); - } - }); - - javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); - jPanel1.setLayout(jPanel1Layout); - jPanel1Layout.setHorizontalGroup( - jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createSequentialGroup() - .addContainerGap() - .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(ofLabel) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) - .addComponent(totalPageLabel) - .addGap(41, 41, 41) - .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addGap(0, 0, 0) + javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1); + jPanel1.setLayout(jPanel1Layout); + jPanel1Layout.setHorizontalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addContainerGap() + .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 45, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(ofLabel) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED) + .addComponent(totalPageLabel) + .addGap(41, 41, 41) + .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0) + .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(366, Short.MAX_VALUE)) + .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 622, Short.MAX_VALUE) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup() + .addContainerGap(280, Short.MAX_VALUE) + .addComponent(artifactLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 258, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(84, Short.MAX_VALUE))) + ); + jPanel1Layout.setVerticalGroup( + jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createSequentialGroup() + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) + .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(ofLabel) + .addComponent(totalPageLabel)) .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addContainerGap(366, Short.MAX_VALUE)) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 622, Short.MAX_VALUE) - ); - jPanel1Layout.setVerticalGroup( - jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(resultsTableScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 394, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 0, 0)) + .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(jPanel1Layout.createSequentialGroup() - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) - .addComponent(pageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(currentPageLabel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(ofLabel) - .addComponent(totalPageLabel)) - .addComponent(nextPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(prevPageButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) - .addComponent(pageLabel2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)) - .addGap(0, 0, 0) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 401, Short.MAX_VALUE)) - ); + .addComponent(artifactLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE) + .addGap(0, 401, Short.MAX_VALUE))) + ); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - ); - }// //GEN-END:initComponents + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(jPanel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + ); + }// //GEN-END:initComponents private void nextPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextPageButtonActionPerformed currentPage = currentPage + 1; currentPageLabel.setText(Integer.toString(currentPage)); + artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName()); startNewTask(new SelectedArtifactChangedTask(currentPage)); }//GEN-LAST:event_nextPageButtonActionPerformed private void prevPageButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevPageButtonActionPerformed currentPage = currentPage - 1; currentPageLabel.setText(Integer.toString(currentPage)); + artifactLabel.setText(artifactTableContents.get(currentPage - 1).getArtifactDisplayName()); startNewTask(new SelectedArtifactChangedTask(currentPage)); }//GEN-LAST:event_prevPageButtonActionPerformed // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel artifactLabel; private javax.swing.JMenuItem copyMenuItem; private javax.swing.JLabel currentPageLabel; private javax.swing.JPanel jPanel1; - private javax.swing.JScrollPane jScrollPane1; private javax.swing.JButton nextPageButton; private javax.swing.JLabel ofLabel; - private javax.swing.JTextPane outputViewPane; private javax.swing.JLabel pageLabel; private javax.swing.JLabel pageLabel2; private javax.swing.JButton prevPageButton; + private javax.swing.JScrollPane resultsTableScrollPane; private javax.swing.JPopupMenu rightClickMenu; private javax.swing.JMenuItem selectAllMenuItem; private javax.swing.JLabel totalPageLabel; // End of variables declaration//GEN-END:variables + private ETable resultsTable; private void customizeComponents() { - outputViewPane.setComponentPopupMenu(rightClickMenu); + resultsTable.setComponentPopupMenu(rightClickMenu); ActionListener actList = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JMenuItem jmi = (JMenuItem) e.getSource(); if (jmi.equals(copyMenuItem)) { - outputViewPane.copy(); + StringBuilder selectedText = new StringBuilder(512); + for (int row : resultsTable.getSelectedRows()) { + for (int col : resultsTable.getSelectedColumns()) { + selectedText.append((String) resultsTable.getValueAt(row, col)); + selectedText.append("\t"); + } + //if its the last row selected don't add a new line + if (row != resultsTable.getSelectedRows()[resultsTable.getSelectedRows().length - 1]) { + selectedText.append(System.lineSeparator()); + } + } + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(new StringSelection(selectedText.toString()), null); } else if (jmi.equals(selectAllMenuItem)) { - outputViewPane.selectAll(); + resultsTable.selectAll(); } } }; copyMenuItem.addActionListener(actList); - selectAllMenuItem.addActionListener(actList); - Utilities.configureTextPaneAsHtml(outputViewPane); + selectAllMenuItem.addActionListener(actList); } /** @@ -258,8 +315,9 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat private void resetComponents() { currentPage = 1; currentPageLabel.setText(""); + artifactLabel.setText(""); totalPageLabel.setText(""); - outputViewPane.setText(""); + ((DefaultTableModel) resultsTable.getModel()).setRowCount(0); prevPageButton.setEnabled(false); nextPageButton.setEnabled(false); currentNode = null; @@ -344,6 +402,101 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat } } + /** + * This class is a container to hold the data necessary for each of the + * result pages associated with file or artifact beivng viewed. + */ + private class ResultsTableArtifact { + + private final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); + private String[][] rowData = null; + private final String artifactDisplayName; + + ResultsTableArtifact(BlackboardArtifact artifact) { + artifactDisplayName = artifact.getDisplayName(); + addRows(artifact); + + } + + ResultsTableArtifact(String errorMsg) { + artifactDisplayName = errorMsg; + rowData = new String[1][3]; + rowData[0] = new String[]{"", errorMsg, ""}; + + } + + private String[][] getRows() { + return rowData; + } + + private void addRows(BlackboardArtifact artifact) { + List rowsToAdd = new ArrayList<>(); + try { + Content content = artifact.getSleuthkitCase().getContentById(artifact.getObjectID()); + /* + * Add rows for each attribute. + */ + for (BlackboardAttribute attr : artifact.getAttributes()) { + /* + * Attribute value column. + */ + String value = ""; + switch (attr.getAttributeType().getValueType()) { + case STRING: + case INTEGER: + case LONG: + case DOUBLE: + case BYTE: + default: + value = attr.getDisplayString(); + break; + // Use Autopsy date formatting settings, not TSK defaults + case DATETIME: + long epoch = attr.getValueLong(); + value = "0000-00-00 00:00:00"; + if (null != content && 0 != epoch) { + dateFormatter.setTimeZone(ContentUtils.getTimeZone(content)); + value = dateFormatter.format(new java.util.Date(epoch * 1000)); + } + break; + } + /* + * Attribute sources column. + */ + String sources = StringUtils.join(attr.getSources(), ", "); + rowsToAdd.add(new String[]{attr.getAttributeType().getDisplayName(), value, sources}); + } + /* + * Add a row for the source content path. + */ + String path = ""; + try { + if (null != content) { + path = content.getUniquePath(); + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, String.format("Error getting source content path for artifact (artifact_id=%d, obj_id=%d)", artifact.getArtifactID(), artifact.getObjectID()), ex); + path = Bundle.DataContentViewerArtifact_failedToGetSourcePath_message(); + } + rowsToAdd.add(new String[]{"Source File Path", path, ""}); + /* + * Add a row for the artifact id. + */ + rowsToAdd.add(new String[]{"Artifact ID", Long.toString(artifact.getArtifactID()), ""}); + } catch (TskCoreException ex) { + rowsToAdd.add(new String[]{"", Bundle.DataContentViewerArtifact_failedToGetAttributes_message(), ""}); + } + rowData = rowsToAdd.toArray(new String[0][0]); + } + + /** + * @return the artifactDisplayName + */ + String getArtifactDisplayName() { + return artifactDisplayName; + } + } + /** * Instances of this class are simple containers for view update information * generated by a background thread. @@ -352,12 +505,18 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat int numberOfPages; int currentPage; - String text; + ResultsTableArtifact tableContents; - ViewUpdate(int numberOfPages, int currentPage, String text) { + ViewUpdate(int numberOfPages, int currentPage, ResultsTableArtifact contents) { this.currentPage = currentPage; this.numberOfPages = numberOfPages; - this.text = text; + this.tableContents = contents; + } + + ViewUpdate(int numberOfPages, int currentPage, String errorMsg) { + this.currentPage = currentPage; + this.numberOfPages = numberOfPages; + this.tableContents = new ResultsTableArtifact(errorMsg); } } @@ -376,13 +535,15 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat currentPage = viewUpdate.currentPage; totalPageLabel.setText(Integer.toString(viewUpdate.numberOfPages)); currentPageLabel.setText(Integer.toString(currentPage)); - + artifactLabel.setText(viewUpdate.tableContents.getArtifactDisplayName()); // @@@ This can take a long time. Perhaps a faster HTML renderer can be found. // Note that the rendering appears to be done on a background thread, since the // wait cursor reset below happens before the new text hits the JTextPane. On the // other hand, the UI is unresponsive... - outputViewPane.setText(viewUpdate.text); - outputViewPane.moveCaretPosition(0); + DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel()); + tModel.setDataVector(viewUpdate.tableContents.getRows(), COLUMN_HEADERS); + updateColumnSizes(); + resultsTable.clearSelection(); this.setCursor(null); } @@ -394,9 +555,12 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat * @param task A new SwingWorker object to execute as a background thread. */ private synchronized void startNewTask(SwingWorker task) { - outputViewPane.setText(WAIT_TEXT); - outputViewPane.moveCaretPosition(0); - + String[][] waitRow = new String[1][3]; + waitRow[0] = new String[]{"", WAIT_TEXT, ""}; + DefaultTableModel tModel = ((DefaultTableModel) resultsTable.getModel()); + tModel.setDataVector(waitRow, COLUMN_HEADERS); + updateColumnSizes(); + resultsTable.clearSelection(); // The output of the previous task is no longer relevant. if (currentTask != null) { // This call sets a cancellation flag. It does not terminate the background thread running the task. @@ -410,24 +574,25 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat } /** - * Populate the cache of artifact represented as strings. + * Populate the cache of artifact represented as ResultsTableArtifacts. * - * @param artifactStrings A list of string representations of artifacts. + * @param artifactList A list of ResultsTableArtifact representations of + * artifacts. */ - private void setArtifactContentStrings(List artifactStrings) { + private void setArtifactContents(List artifactList) { synchronized (lock) { - this.artifactContentStrings = artifactStrings; + this.artifactTableContents = artifactList; } } /** - * Retrieve the cache of artifact represented as strings. + * Retrieve the cache of artifact represented as ResultsTableArtifacts. * - * @return A list of string representations of artifacts. + * @return A list of ResultsTableArtifact representations of artifacts. */ - private List getArtifactContentStrings() { + private List getArtifactContents() { synchronized (lock) { - return artifactContentStrings; + return artifactTableContents; } } @@ -453,7 +618,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat // Get the content. Content content = lookup.lookup(Content.class); if (content == null) { - return new ViewUpdate(getArtifactContentStrings().size(), currentPage, ERROR_TEXT); + return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT); } // Get all of the blackboard artifacts associated with the content. These are what this @@ -463,17 +628,17 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat artifacts = content.getAllArtifacts(); } catch (TskException ex) { logger.log(Level.WARNING, "Couldn't get artifacts", ex); //NON-NLS - return new ViewUpdate(getArtifactContentStrings().size(), currentPage, ERROR_TEXT); + return new ViewUpdate(getArtifactContents().size(), currentPage, ERROR_TEXT); } if (isCancelled()) { return null; } - // Build the new artifact strings cache. - ArrayList artifactStrings = new ArrayList<>(); + // Build the new artifact contents cache. + ArrayList artifactContents = new ArrayList<>(); for (BlackboardArtifact artifact : artifacts) { - artifactStrings.add(new ArtifactStringContent(artifact)); + artifactContents.add(new ResultsTableArtifact(artifact)); } // If the node has an underlying blackboard artifact, show it. If not, @@ -514,19 +679,16 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat return null; } - // Add one to the index of the artifact string for the corresponding page index. Note that the getString() method - // of ArtifactStringContent does a lazy fetch of the attributes of the correspoding artifact and represents them as - // HTML. - ViewUpdate viewUpdate = new ViewUpdate(artifactStrings.size(), index + 1, artifactStrings.get(index).getString()); + // Add one to the index of the artifact content for the corresponding page index. + ViewUpdate viewUpdate = new ViewUpdate(artifactContents.size(), index + 1, artifactContents.get(index)); - // It may take a considerable amount of time to fetch the attributes of the selected artifact and render them - // as HTML, so check for cancellation. + // It may take a considerable amount of time to fetch the attributes of the selected artifact if (isCancelled()) { return null; } - // Update the artifact strings cache. - setArtifactContentStrings(artifactStrings); + // Update the artifact contents cache. + setArtifactContents(artifactContents); return viewUpdate; } @@ -561,14 +723,10 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat @Override protected ViewUpdate doInBackground() { - // Get the artifact string to display from the cache. Note that one must be subtracted from the - // page index to get the corresponding artifact string index. - List artifactStrings = getArtifactContentStrings(); - ArtifactStringContent artifactStringContent = artifactStrings.get(pageIndex - 1); - - // The getString() method of ArtifactStringContent does a lazy fetch of the attributes of the - // correspoding artifact and represents them as HTML. - String artifactString = artifactStringContent.getString(); + // Get the artifact content to display from the cache. Note that one must be subtracted from the + // page index to get the corresponding artifact content index. + List artifactContents = getArtifactContents(); + ResultsTableArtifact artifactContent = artifactContents.get(pageIndex - 1); // It may take a considerable amount of time to fetch the attributes of the selected artifact and render them // as HTML, so check for cancellation. @@ -576,7 +734,7 @@ public class DataContentViewerArtifact extends javax.swing.JPanel implements Dat return null; } - return new ViewUpdate(artifactStrings.size(), pageIndex, artifactString); + return new ViewUpdate(artifactContents.size(), pageIndex, artifactContent); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java index cece255c93..774d1f2f9e 100644 --- a/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java +++ b/Core/src/org/sleuthkit/autopsy/datamodel/ArtifactStringContent.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.datamodel; import java.text.SimpleDateFormat; -import java.util.Arrays; import java.util.logging.Level; import org.apache.commons.lang.StringUtils; import org.openide.util.NbBundle; @@ -35,7 +34,10 @@ import org.sleuthkit.datamodel.TskCoreException; * HTML, so any styling needs to be supplied by the display mechansim. For * example, GUI components such as content viewers might use HTMLEditorKit to * add styling. + * + * @deprecated - No longer used by DataContentViewerArtifact because the table is no longer HTML */ +@Deprecated public class ArtifactStringContent implements StringContent { private final static SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");