diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form index c2451e28cb..0179bb756e 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.form @@ -11,30 +11,27 @@ - - + + + + + + + + + + + + + + + - - - - - - + - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java index e0a41fe0de..db66946a31 100644 --- a/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java +++ b/Core/src/org/sleuthkit/autopsy/contentviewers/artifactviewers/GeneralPurposeArtifactViewer.java @@ -37,7 +37,6 @@ import java.util.logging.Level; import javax.swing.JLabel; import javax.swing.JMenuItem; import javax.swing.JPopupMenu; -import javax.swing.JScrollPane; import javax.swing.JTextPane; import javax.swing.SwingUtilities; import org.apache.commons.lang.StringUtils; @@ -82,6 +81,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private final GridBagLayout gridBagLayout = new GridBagLayout(); private final GridBagConstraints gridBagConstraints = new GridBagConstraints(); private final Map orderingMap = new HashMap<>(); + private final javax.swing.JPanel detailsPanel = new javax.swing.JPanel(); /** * Creates new form GeneralPurposeArtifactViewer. @@ -90,6 +90,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i public GeneralPurposeArtifactViewer() { addOrderings(); initComponents(); + gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; detailsPanel.setLayout(gridBagLayout); } @@ -161,8 +162,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i } updateView(artifact, attributeMap, dataSourceName, sourceFileName); } + detailsScrollPane.setViewportView(detailsPanel); + detailsScrollPane.revalidate(); revalidate(); - repaint(); } /** @@ -172,7 +174,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private void resetComponent() { // clear the panel detailsPanel.removeAll(); - gridBagConstraints.anchor = GridBagConstraints.FIRST_LINE_START; + detailsPanel.setLayout(gridBagLayout); + detailsPanel.revalidate(); gridBagConstraints.gridy = 0; gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.weighty = 0.0; @@ -183,14 +186,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override - public Component getComponent() { - // Slap a vertical scrollbar on the panel - return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED); - } - - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - @Override - public boolean isSupported(BlackboardArtifact artifact) { return (artifact != null) && (artifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID() @@ -218,22 +213,20 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i // //GEN-BEGIN:initComponents private void initComponents() { - detailsPanel = new javax.swing.JPanel(); + detailsScrollPane = new javax.swing.JScrollPane(); - setLayout(new java.awt.BorderLayout()); - - javax.swing.GroupLayout detailsPanelLayout = new javax.swing.GroupLayout(detailsPanel); - detailsPanel.setLayout(detailsPanelLayout); - detailsPanelLayout.setHorizontalGroup( - detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, 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(detailsScrollPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 62, Short.MAX_VALUE) ); - detailsPanelLayout.setVerticalGroup( - detailsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGap(0, 0, Short.MAX_VALUE) + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(detailsScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 93, Short.MAX_VALUE) + .addGap(0, 0, 0)) ); - - add(detailsPanel, java.awt.BorderLayout.PAGE_START); }// //GEN-END:initComponents /** @@ -257,7 +250,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private void updateView(BlackboardArtifact artifact, Map> attributeMap, String dataSourceName, String sourceFilePath) { final Integer artifactTypeId = artifact.getArtifactTypeID(); if (!(artifactTypeId < 1 || artifactTypeId >= Integer.MAX_VALUE)) { - addDetailsHeader(artifactTypeId); + JTextPane firstTextPane = addDetailsHeader(artifactTypeId); Integer[] orderingArray = orderingMap.get(artifactTypeId); if (orderingArray == null) { orderingArray = DEFAULT_ORDERING; @@ -282,7 +275,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i addNameValueRow(bba.getAttributeType().getDisplayName(), displayString); } else { addNameValueRow(bba.getAttributeType().getDisplayName(), bba.getDisplayString()); - } } } @@ -311,7 +303,11 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i addNameValueRow(Bundle.GeneralPurposeArtifactViewer_details_file(), sourceFilePath); // add veritcal glue at the end addPageEndGlue(); + if (firstTextPane != null) { + firstTextPane.setCaretPosition(0); + } } + detailsPanel.revalidate(); } /** @@ -353,7 +349,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i "GeneralPurposeArtifactViewer.details.searchHeader=Web Search", "GeneralPurposeArtifactViewer.details.cachedHeader=Cached File", "GeneralPurposeArtifactViewer.details.cookieHeader=Cookie Details",}) - private void addDetailsHeader(int artifactTypeId) { + private JTextPane addDetailsHeader(int artifactTypeId) { String header; if (artifactTypeId == BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_HISTORY.getTypeID()) { header = Bundle.GeneralPurposeArtifactViewer_details_historyHeader(); @@ -370,7 +366,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i } else { header = Bundle.GeneralPurposeArtifactViewer_details_attrHeader(); } - addHeader(header); + return addHeader(header); } /** @@ -381,16 +377,21 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i * @return JLabel Heading label added. */ @ThreadConfined(type = ThreadConfined.ThreadType.AWT) - private JLabel addHeader(String headerString) { + private JTextPane addHeader(String headerString) { // create label for heading - javax.swing.JLabel headingLabel = new javax.swing.JLabel(); + javax.swing.JTextPane headingLabel = new javax.swing.JTextPane(); + headingLabel.setOpaque(false); + headingLabel.setFocusable(false); + headingLabel.setEditable(false); // add a blank line before the start of new section, unless it's // the first section if (gridBagConstraints.gridy != 0) { gridBagConstraints.gridy++; - detailsPanel.add(new javax.swing.JLabel(" "), gridBagConstraints); + // add to panel + addToPanel(new javax.swing.JLabel(" ")); addLineEndGlue(); - } + headingLabel.setFocusable(false); + } gridBagConstraints.gridy++; gridBagConstraints.gridx = LABEL_COLUMN;; // let the header span all of the row @@ -401,7 +402,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i // make it large and bold headingLabel.setFont(headingLabel.getFont().deriveFont(Font.BOLD, headingLabel.getFont().getSize() + 2)); // add to panel - detailsPanel.add(headingLabel, gridBagConstraints); + addToPanel(headingLabel); // reset constraints to normal gridBagConstraints.gridwidth = LABEL_WIDTH; // add line end glue @@ -417,9 +418,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i * @param keyString Key name to display. * @param valueString Value string to display. */ - private void addNameValueRow(String keyString, String valueString) { + private JTextPane addNameValueRow(String keyString, String valueString) { addKeyAtCol(keyString); - addValueAtCol(valueString); + return addValueAtCol(valueString); } /** @@ -432,7 +433,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i gridBagConstraints.weightx = GLUE_WEIGHT_X; // take up all the horizontal space gridBagConstraints.fill = GridBagConstraints.BOTH; javax.swing.Box.Filler horizontalFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(32767, 0)); - detailsPanel.add(horizontalFiller, gridBagConstraints); + // add to panel + addToPanel(horizontalFiller); // restore fill & weight gridBagConstraints.fill = GridBagConstraints.NONE; gridBagConstraints.weightx = TEXT_WEIGHT_X; @@ -446,7 +448,8 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i gridBagConstraints.weighty = 1.0; // take up all the vertical space gridBagConstraints.fill = GridBagConstraints.VERTICAL; javax.swing.Box.Filler vertFiller = new javax.swing.Box.Filler(new Dimension(0, 0), new Dimension(0, 0), new Dimension(0, 32767)); - detailsPanel.add(vertFiller, gridBagConstraints); + // add to panel + addToPanel(vertFiller); } /** @@ -459,16 +462,22 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private JLabel addKeyAtCol(String keyString) { // create label javax.swing.JLabel keyLabel = new javax.swing.JLabel(); + keyLabel.setFocusable(false); gridBagConstraints.gridy++; gridBagConstraints.gridx = LABEL_COLUMN; gridBagConstraints.gridwidth = LABEL_WIDTH; // set text keyLabel.setText(keyString + ": "); // add to panel - detailsPanel.add(keyLabel, gridBagConstraints); + addToPanel(keyLabel); return keyLabel; } + private void addToPanel(Component comp) { + detailsPanel.add(comp, gridBagConstraints); + detailsPanel.revalidate(); + } + /** * Adds a value string to the panel at specified column. * @@ -479,6 +488,7 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i private JTextPane addValueAtCol(String valueString) { // create label, JTextPane valueField = new JTextPane(); + valueField.setFocusable(false); valueField.setEditable(false); valueField.setOpaque(false); gridBagConstraints.gridx = VALUE_COLUMN; @@ -488,8 +498,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i cloneConstraints.fill = GridBagConstraints.BOTH; // set text valueField.setText(valueString); - // scroll to start of text - valueField.setCaretPosition(0); // attach a right click menu with Copy option valueField.addMouseListener(new java.awt.event.MouseAdapter() { @Override @@ -497,8 +505,9 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i valueLabelMouseClicked(evt, valueField); } }); - // add label to panel + // add label to panel with cloned contraintsF detailsPanel.add(valueField, cloneConstraints); + revalidate(); // end the line addLineEndGlue(); return valueField; @@ -531,6 +540,6 @@ public class GeneralPurposeArtifactViewer extends AbstractArtifactDetailsPanel i // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JPanel detailsPanel; + private javax.swing.JScrollPane detailsScrollPane; // End of variables declaration//GEN-END:variables } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java index 0ceda46203..61efc8cfbe 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/search/DomainSearch.java @@ -201,7 +201,7 @@ public class DomainSearch { * @throws DiscoveryException if unable to get the artifacts or the date * attributes from an artifact. */ - public List getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException, InterruptedException { + public List getAllArtifactsForDomain(SleuthkitCase sleuthkitCase, String domain) throws DiscoveryException { List artifacts = new ArrayList<>(); Map> dateMap = new HashMap<>(); if (!StringUtils.isBlank(domain)) { diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java index b56e60aa8f..93ae57db36 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/AbstractArtifactDetailsPanel.java @@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.discovery.ui; import java.awt.Component; import javax.swing.JPanel; -import javax.swing.JScrollPane; import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -30,14 +29,13 @@ import org.sleuthkit.datamodel.BlackboardArtifact; */ public abstract class AbstractArtifactDetailsPanel extends JPanel { + private static final long serialVersionUID = 1L; + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public Component getComponent() { - // Slap a vertical scrollbar on the panel. - return new JScrollPane(this, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + return this; } - private static final long serialVersionUID = 1L; - /** * Called to display the contents of the given artifact. * diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form index 3b9f9f4c7a..002baadab7 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.form @@ -4,7 +4,7 @@ - + @@ -22,12 +22,12 @@ - + - + @@ -37,9 +37,13 @@ + + + + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java index 6cce1aeeb9..fb4a835acf 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsListPanel.java @@ -94,11 +94,15 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { @Override BlackboardArtifact getSelectedArtifact() { - int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex(); - if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) { + if (artifactsTable.getModel() instanceof DomainArtifactTableModel) { + int selectedIndex = artifactsTable.getSelectionModel().getLeadSelectionIndex(); + if (selectedIndex < artifactsTable.getSelectionModel().getMinSelectionIndex() || artifactsTable.getSelectionModel().getMaxSelectionIndex() < 0 || selectedIndex > artifactsTable.getSelectionModel().getMaxSelectionIndex()) { + return null; + } + return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex)); + } else { return null; } - return tableModel.getArtifactByRow(artifactsTable.convertRowIndexToModel(selectedIndex)); } @Override @@ -124,7 +128,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override void addArtifacts(List artifactList) { - tableModel.setContents(artifactList); + if (!artifactList.isEmpty()) { + artifactsTable.setModel(tableModel); + tableModel.setContents(artifactList); + } else { + artifactsTable.setModel(new EmptyTableModel()); + } artifactsTable.validate(); artifactsTable.repaint(); tableModel.fireTableDataChanged(); @@ -152,10 +161,12 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { artifactsTable = new javax.swing.JTable(); setOpaque(false); - setPreferredSize(new java.awt.Dimension(300, 0)); + setPreferredSize(new java.awt.Dimension(350, 10)); jScrollPane1.setBorder(null); + jScrollPane1.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); + jScrollPane1.setPreferredSize(new java.awt.Dimension(350, 10)); artifactsTable.setAutoCreateRowSorter(true); artifactsTable.setModel(tableModel); @@ -166,11 +177,11 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 0, Short.MAX_VALUE) + .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) ); }// //GEN-END:initComponents @@ -357,6 +368,45 @@ final class ArtifactsListPanel extends AbstractArtifactListPanel { } } } + + /** + * Table model which displays only that no results were found. + */ + private class EmptyTableModel extends AbstractTableModel { + + private static final long serialVersionUID = 1L; + + @Override + public int getRowCount() { + return 1; + } + + @Override + public int getColumnCount() { + return 1; + } + + @NbBundle.Messages({"ArtifactsListPanel.noResultsFound.text=No results found"}) + @Override + public Object getValueAt(int rowIndex, int columnIndex) { + return Bundle.ArtifactsListPanel_noResultsFound_text(); + } + @Override + public String getColumnName(int column) { + switch (column) { + case 0: + return Bundle.ArtifactsListPanel_dateColumn_name(); + case 1: + return Bundle.ArtifactsListPanel_titleColumn_name(); + case 2: + return Bundle.ArtifactsListPanel_mimeTypeColumn_name(); + default: + return ""; + } + } + + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JTable artifactsTable; // End of variables declaration//GEN-END:variables diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java index 2c919f371f..1202f20647 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/ArtifactsWorker.java @@ -61,7 +61,8 @@ class ArtifactsWorker extends SwingWorker, Void> { return domainSearch.getArtifacts(new DomainSearchArtifactsRequest(Case.getCurrentCase().getSleuthkitCase(), domain, artifactType)); } catch (DiscoveryException ex) { if (ex.getCause() instanceof InterruptedException) { - logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); + this.cancel(true); + //ignore the exception as it was cancelled while the cache was performing its get and we support cancellation } else { throw ex; } @@ -84,6 +85,6 @@ class ArtifactsWorker extends SwingWorker, Void> { //Worker was cancelled after previously finishing its background work, exception ignored to cut down on non-helpful logging } } - + } } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties index e6ce6cfda7..143353da81 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties @@ -61,4 +61,5 @@ FileDetailsPanel.instancesList.border.title=Instances CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel2.text= PreviouslyNotableFilterPanel.text_1=Include only previously notable domains -KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type \ No newline at end of file +KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type +LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED index 3fcce54c93..ffa7f36036 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/Bundle.properties-MERGED @@ -3,6 +3,7 @@ ArtifactMenuMouseAdapter_label=Extract Files ArtifactsListPanel.dateColumn.name=Date/Time ArtifactsListPanel.fileNameColumn.name=Name ArtifactsListPanel.mimeTypeColumn.name=MIME Type +ArtifactsListPanel.noResultsFound.text=No results found ArtifactsListPanel.termColumn.name=Term ArtifactsListPanel.titleColumn.name=Title ArtifactsListPanel.urlColumn.name=URL @@ -52,7 +53,7 @@ DocumentPanel.numberOfImages.noImages=No images # {0} - numberOfImages DocumentPanel.numberOfImages.text=1 of {0} images DocumentWrapper.previewInitialValue=Preview not generated yet. -DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline +DomainDetailsPanel.miniTimelineTitle.text=Timeline # {0} - startDate # {1} - endDate DomainSummaryPanel.activity.text=Activity: {0} to {1} @@ -68,12 +69,16 @@ ImageThumbnailPanel.isDeleted.text=All instances of file are deleted. # {0} - otherInstanceCount ImageThumbnailPanel.nameLabel.more.text=\ and {0} more InterestingItemsFilterPanel.error.text=At least one interesting file set name must be selected. +LoadingPanel.loading.text=Loading, please wait. +# {0} - resultInfo +LoadingPanel.retrieving.text=Retrieving results for {0}. MiniTimelineArtifactListPanel.descriptionColumn.name=\ Description MiniTimelineArtifactListPanel.typeColumn.name=Result Type MiniTimelineArtifactListPanel.value.noValue=No value available. MiniTimelineDateListPanel.countColumn.name=Count MiniTimelineDateListPanel.dateColumn.name=Date MiniTimelineDateListPanel.value.noValue=No value available. +MiniTimelinePanel.loadingPanel.details=the Timeline view MonthAbbreviation.aprilAbbrev=Apr MonthAbbreviation.augustAbbrev=Aug MonthAbbreviation.decemberAbbrev=Dec @@ -165,6 +170,7 @@ CookieDetailsPanel.jLabel1.text=Artifact: CookieDetailsPanel.jLabel2.text= PreviouslyNotableFilterPanel.text_1=Include only previously notable domains KnownAccountTypeFilterPanel.text_1=Include only domains with a known account type +LoadingPanel.detailsLabel.AccessibleContext.accessibleName=detailsLabel VideoThumbnailPanel.bytes.text=bytes VideoThumbnailPanel.deleted.text=All instances of file are deleted. VideoThumbnailPanel.gigaBytes.text=GB diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java index 299e1285df..901d8ba60a 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DiscoveryTopComponent.java @@ -60,9 +60,11 @@ public final class DiscoveryTopComponent extends TopComponent { private volatile static int previousDividerLocation = 250; private final GroupListPanel groupListPanel; private final ResultsPanel resultsPanel; + private JPanel detailsPanel = new JPanel(); private String selectedDomainTabName; private Type searchType; private int dividerLocation = JSplitPane.UNDEFINED_CONDITION; + private SwingAnimator animator = null; /** @@ -83,25 +85,25 @@ public final class DiscoveryTopComponent extends TopComponent { } }); + rightSplitPane.setTopComponent(resultsPanel); + resetBottomComponent(); rightSplitPane.addPropertyChangeListener(JSplitPane.DIVIDER_LOCATION_PROPERTY, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { - if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY)) { + if (evt.getPropertyName().equalsIgnoreCase(JSplitPane.DIVIDER_LOCATION_PROPERTY) + && ((animator == null || !animator.isRunning()) + && evt.getNewValue() instanceof Integer + && evt.getOldValue() instanceof Integer + && ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize()) + && (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue()) + && ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION))) { //Only change the saved location when it was a manual change by the user and not the animation or the window opening initially - if ((animator == null || !animator.isRunning()) - && evt.getNewValue() instanceof Integer - && evt.getOldValue() instanceof Integer - && ((int) evt.getNewValue() + 5) < (rightSplitPane.getHeight() - rightSplitPane.getDividerSize()) - && (JSplitPane.UNDEFINED_CONDITION != (int) evt.getNewValue()) - && ((int) evt.getOldValue() != JSplitPane.UNDEFINED_CONDITION)) { - previousDividerLocation = (int) evt.getNewValue(); - - } + previousDividerLocation = (int) evt.getNewValue(); } } - }); - rightSplitPane.setTopComponent(resultsPanel); - rightSplitPane.setBottomComponent(new JPanel()); + } + ); + } /** @@ -131,7 +133,8 @@ public final class DiscoveryTopComponent extends TopComponent { * @return The open DiscoveryTopComponent or null if it has not been opened. */ public static DiscoveryTopComponent getTopComponent() { - return (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + DiscoveryTopComponent discoveryTopComp = (DiscoveryTopComponent) WindowManager.getDefault().findTopComponent(PREFERRED_ID); + return discoveryTopComp; } /** @@ -153,6 +156,11 @@ public final class DiscoveryTopComponent extends TopComponent { DiscoveryEventUtils.getDiscoveryEventBus().register(groupListPanel); } + private void resetBottomComponent() { + rightSplitPane.setBottomComponent(new JPanel()); + rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION); + } + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @Override protected void componentClosed() { @@ -162,11 +170,11 @@ public final class DiscoveryTopComponent extends TopComponent { DiscoveryEventUtils.getDiscoveryEventBus().unregister(this); DiscoveryEventUtils.getDiscoveryEventBus().unregister(groupListPanel); DiscoveryEventUtils.getDiscoveryEventBus().unregister(resultsPanel); - DiscoveryEventUtils.getDiscoveryEventBus().unregister(rightSplitPane.getBottomComponent()); - if (rightSplitPane.getBottomComponent() instanceof DomainDetailsPanel) { - selectedDomainTabName = ((DomainDetailsPanel) rightSplitPane.getBottomComponent()).getSelectedTabName(); + DiscoveryEventUtils.getDiscoveryEventBus().unregister(detailsPanel); + if (detailsPanel instanceof DomainDetailsPanel) { + selectedDomainTabName = ((DomainDetailsPanel) detailsPanel).getSelectedTabName(); } - rightSplitPane.setDividerLocation(JSplitPane.UNDEFINED_CONDITION); + resetBottomComponent(); super.componentClosed(); } @@ -336,12 +344,13 @@ public final class DiscoveryTopComponent extends TopComponent { } selectedDomainTabName = validateLastSelectedType(searchCompleteEvent); DomainDetailsPanel domainDetailsPanel = new DomainDetailsPanel(); - rightSplitPane.setBottomComponent(domainDetailsPanel); domainDetailsPanel.configureArtifactTabs(selectedDomainTabName); + detailsPanel = domainDetailsPanel; } else { - rightSplitPane.setBottomComponent(new FileDetailsPanel()); + detailsPanel = new FileDetailsPanel(); } - DiscoveryEventUtils.getDiscoveryEventBus().register(rightSplitPane.getBottomComponent()); + rightSplitPane.setBottomComponent(detailsPanel); + DiscoveryEventUtils.getDiscoveryEventBus().register(detailsPanel); descriptionText += searchCompleteEvent.getFilters().stream().map(AbstractFilter::getDesc).collect(Collectors.joining("; ")); progressMessageTextArea.setText(Bundle.DiscoveryTopComponent_searchComplete_text(descriptionText)); progressMessageTextArea.setCaretPosition(0); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form index fbd55756e9..8b939f2521 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.form @@ -1,13 +1,20 @@
+ + + + + + + + + + - - - @@ -23,23 +30,4 @@ - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java index 3bc5ae14a7..754415705d 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainArtifactsTabPanel.java @@ -22,7 +22,6 @@ import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifa import com.google.common.eventbus.Subscribe; import java.util.logging.Level; import javax.swing.JPanel; -import javax.swing.JScrollPane; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; @@ -64,7 +63,8 @@ final class DomainArtifactsTabPanel extends JPanel { this.artifactType = type; listPanel = new ArtifactsListPanel(artifactType); listPanel.addMouseListener(new ArtifactMenuMouseAdapter(listPanel)); - jSplitPane1.setLeftComponent(listPanel); + mainSplitPane.setLeftComponent(listPanel); + add(mainSplitPane); setRightComponent(); listPanel.addSelectionListener(listener); } @@ -91,7 +91,7 @@ final class DomainArtifactsTabPanel extends JPanel { break; } if (rightPanel != null) { - jSplitPane1.setRightComponent(rightPanel.getComponent()); + mainSplitPane.setRightComponent(rightPanel.getComponent()); } } @@ -115,10 +115,14 @@ final class DomainArtifactsTabPanel extends JPanel { this.status = status; if (status == ArtifactRetrievalStatus.UNPOPULATED) { listPanel.clearList(); - if (rightPanel != null){ + removeAll(); + add(mainSplitPane); + if (rightPanel != null) { rightPanel.setArtifact(null); } - + } else if (status == ArtifactRetrievalStatus.POPULATING) { + removeAll(); + add(new LoadingPanel(artifactType.getDisplayName())); } } @@ -130,7 +134,7 @@ final class DomainArtifactsTabPanel extends JPanel { */ @Subscribe void handleArtifactSearchResultEvent(DiscoveryEventUtils.ArtifactSearchResultEvent artifactresultEvent) { - if (artifactType == artifactresultEvent.getArtifactType()) { + if (artifactType == artifactresultEvent.getArtifactType() && status == ArtifactRetrievalStatus.POPULATING) { SwingUtilities.invokeLater(() -> { listPanel.removeSelectionListener(listener); listPanel.addArtifacts(artifactresultEvent.getListOfArtifacts()); @@ -138,6 +142,8 @@ final class DomainArtifactsTabPanel extends JPanel { setEnabled(!listPanel.isEmpty()); listPanel.addSelectionListener(listener); listPanel.selectFirst(); + removeAll(); + add(mainSplitPane); revalidate(); repaint(); try { @@ -169,20 +175,18 @@ final class DomainArtifactsTabPanel extends JPanel { // //GEN-BEGIN:initComponents private void initComponents() { - jSplitPane1 = new javax.swing.JSplitPane(); + mainSplitPane = new javax.swing.JSplitPane(); + + mainSplitPane.setDividerLocation(350); + mainSplitPane.setResizeWeight(0.1); setMinimumSize(new java.awt.Dimension(0, 0)); - setPreferredSize(new java.awt.Dimension(0, 0)); setLayout(new java.awt.BorderLayout()); - - jSplitPane1.setMinimumSize(new java.awt.Dimension(0, 0)); - jSplitPane1.setPreferredSize(new java.awt.Dimension(0, 0)); - add(jSplitPane1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JSplitPane jSplitPane1; + private javax.swing.JSplitPane mainSplitPane; // End of variables declaration//GEN-END:variables /** diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.form index b6e16aff08..ada26d6fb9 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.form @@ -2,13 +2,13 @@
- + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java index b7c372aa64..63f6b0921b 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainDetailsPanel.java @@ -63,7 +63,7 @@ final class DomainDetailsPanel extends JPanel { * * @param tabName The name of the tab to select initially. */ - @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Mini Timeline"}) + @NbBundle.Messages({"DomainDetailsPanel.miniTimelineTitle.text=Timeline"}) @ThreadConfined(type = ThreadConfined.ThreadType.AWT) void configureArtifactTabs(String tabName) { selectedTabName = tabName; @@ -104,6 +104,20 @@ final class DomainDetailsPanel extends JPanel { } } + /** + * Get the status of the currently selected tab. + * + * @return The loading status of the currently selected tab. + */ + DomainArtifactsTabPanel.ArtifactRetrievalStatus getCurrentTabStatus() { + if (jTabbedPane1.getSelectedComponent() instanceof MiniTimelinePanel) { + return ((MiniTimelinePanel) jTabbedPane1.getSelectedComponent()).getStatus(); + } else if (jTabbedPane1.getSelectedComponent() instanceof DomainArtifactsTabPanel) { + return ((DomainArtifactsTabPanel) jTabbedPane1.getSelectedComponent()).getStatus(); + } + return null; + } + /** * Run the worker which retrieves the list of artifacts for the domain to * populate the details area. @@ -146,20 +160,19 @@ final class DomainDetailsPanel extends JPanel { */ @Subscribe void handlePopulateDomainTabsEvent(DiscoveryEventUtils.PopulateDomainTabsEvent populateEvent) { - domain = populateEvent.getDomain(); SwingUtilities.invokeLater(() -> { - resetTabsStatus(); - selectTab(); - Component selectedComponent = jTabbedPane1.getSelectedComponent(); - if (selectedComponent instanceof DomainArtifactsTabPanel) { - runDomainWorker((DomainArtifactsTabPanel) selectedComponent); - } else if (selectedComponent instanceof MiniTimelinePanel) { - runMiniTimelineWorker((MiniTimelinePanel) selectedComponent); - } - if (StringUtils.isBlank(domain)) { + if (StringUtils.isBlank(populateEvent.getDomain())) { + resetTabsStatus(); //send fade out event DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(false)); } else { + domain = populateEvent.getDomain(); + Component selectedComponent = jTabbedPane1.getSelectedComponent(); + if (selectedComponent instanceof DomainArtifactsTabPanel) { + runDomainWorker((DomainArtifactsTabPanel) selectedComponent); + } else if (selectedComponent instanceof MiniTimelinePanel) { + runMiniTimelineWorker((MiniTimelinePanel) selectedComponent); + } //send fade in event DiscoveryEventUtils.getDiscoveryEventBus().post(new DiscoveryEventUtils.DetailsVisibleEvent(true)); } diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form index 9f1e3516b9..ee52e40121 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.form @@ -33,6 +33,7 @@ + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java index 66125e36d0..ef3c7d6ad6 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/DomainSummaryViewer.java @@ -64,6 +64,7 @@ public class DomainSummaryViewer extends javax.swing.JPanel { setLayout(new java.awt.BorderLayout()); domainList.setModel(domainListModel); + domainList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION); domainList.setCellRenderer(new DomainSummaryPanel()); domainScrollPane.setViewportView(domainList); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form new file mode 100644 index 0000000000..649519a353 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.form @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java new file mode 100644 index 0000000000..1b92fe67cf --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/LoadingPanel.java @@ -0,0 +1,97 @@ +/* + * Autopsy + * + * Copyright 2020 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.discovery.ui; + +import javax.swing.JPanel; +import org.apache.commons.lang3.StringUtils; +import org.openide.util.NbBundle; + +/** + * Panel to let user know that the information is loading. + */ +class LoadingPanel extends JPanel { + + private static final long serialVersionUID = 1L; + + /** + * Creates new form LoadingPanel + */ + LoadingPanel() { + this(null); + } + + @NbBundle.Messages({"LoadingPanel.loading.text=Loading, please wait.", + "# {0} - resultInfo", + "LoadingPanel.retrieving.text=Retrieving results for {0}."}) + + LoadingPanel(String details) { + initComponents(); + loadingLabel.setText(Bundle.LoadingPanel_loading_text()); + if (!StringUtils.isBlank(details)) { + detailsLabel.setText(Bundle.LoadingPanel_retrieving_text(details)); + } + } + + /** + * This method is called from within the constructor to initialize the form. + * WARNING: Do NOT modify this code. The content of this method is always + * regenerated by the Form Editor. + */ + @SuppressWarnings("unchecked") + // //GEN-BEGIN:initComponents + private void initComponents() { + + loadingLabel = new javax.swing.JLabel(); + detailsLabel = new javax.swing.JLabel(); + + loadingLabel.setFont(loadingLabel.getFont().deriveFont(loadingLabel.getFont().getSize()+4f)); + loadingLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + loadingLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + + detailsLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER); + detailsLabel.setVerticalAlignment(javax.swing.SwingConstants.TOP); + detailsLabel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + detailsLabel.setVerticalTextPosition(javax.swing.SwingConstants.TOP); + + javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); + this.setLayout(layout); + layout.setHorizontalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addComponent(loadingLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(detailsLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE) + ); + layout.setVerticalGroup( + layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(layout.createSequentialGroup() + .addComponent(loadingLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 48, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) + .addComponent(detailsLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE) + .addContainerGap(30, Short.MAX_VALUE)) + ); + + detailsLabel.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(LoadingPanel.class, "LoadingPanel.detailsLabel.AccessibleContext.accessibleName")); // NOI18N + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel detailsLabel; + private javax.swing.JLabel loadingLabel; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java index 01f3d67183..3ac2cf6e9d 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineArtifactListPanel.java @@ -138,8 +138,8 @@ class MiniTimelineArtifactListPanel extends AbstractArtifactListPanel { artifactsTable = new javax.swing.JTable(); setOpaque(false); - setPreferredSize(new java.awt.Dimension(300, 0)); - + setPreferredSize(new java.awt.Dimension(200, 10)); + jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10)); jScrollPane1.setBorder(null); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java index 6bff7eb150..b25ccc75c4 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineDateListPanel.java @@ -139,8 +139,8 @@ class MiniTimelineDateListPanel extends JPanel { jTable1 = new javax.swing.JTable(); setOpaque(false); - setPreferredSize(new java.awt.Dimension(200, 0)); - + setPreferredSize(new java.awt.Dimension(200, 10)); + jScrollPane1.setPreferredSize(new java.awt.Dimension(200, 10)); jScrollPane1.setBorder(null); jScrollPane1.setMinimumSize(new java.awt.Dimension(0, 0)); diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form index 3e133fcf77..0f0598a148 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.form @@ -1,13 +1,42 @@
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - @@ -23,45 +52,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java index f577622a36..43f8027687 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelinePanel.java @@ -23,6 +23,7 @@ import java.util.logging.Level; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.contentviewers.artifactviewers.GeneralPurposeArtifactViewer; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.ThreadConfined; @@ -32,7 +33,7 @@ import org.sleuthkit.datamodel.BlackboardArtifact; /** * Panel to display the entire mini timeline feature. */ -class MiniTimelinePanel extends javax.swing.JPanel { +final class MiniTimelinePanel extends javax.swing.JPanel { private static final long serialVersionUID = 1L; @@ -44,6 +45,7 @@ class MiniTimelinePanel extends javax.swing.JPanel { private final ListSelectionListener artifactListener; private final ListSelectionListener dateListener; + @NbBundle.Messages({"MiniTimelinePanel.loadingPanel.details=the Timeline view"}) /** * Creates new form MiniTimelinePanel. */ @@ -62,8 +64,8 @@ class MiniTimelinePanel extends javax.swing.JPanel { } else { rightPanel = new GeneralPurposeArtifactViewer(); } - rightPanel.setArtifact(artifact); mainSplitPane.setRightComponent(rightPanel.getComponent()); + rightPanel.setArtifact(artifact); validate(); repaint(); } @@ -88,6 +90,7 @@ class MiniTimelinePanel extends javax.swing.JPanel { leftSplitPane.setLeftComponent(dateListPanel); leftSplitPane.setRightComponent(artifactListPanel); mainSplitPane.setRightComponent(rightPanel.getComponent()); + add(mainSplitPane); } /** @@ -111,10 +114,16 @@ class MiniTimelinePanel extends javax.swing.JPanel { if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.UNPOPULATED) { artifactListPanel.clearList(); dateListPanel.clearList(); + removeAll(); + add(mainSplitPane); if (rightPanel != null) { rightPanel.setArtifact(null); } + } else if (status == DomainArtifactsTabPanel.ArtifactRetrievalStatus.POPULATING) { + removeAll(); + add(new LoadingPanel(Bundle.MiniTimelinePanel_loadingPanel_details())); } + } /** @@ -134,6 +143,8 @@ class MiniTimelinePanel extends javax.swing.JPanel { dateListPanel.addSelectionListener(dateListener); artifactListPanel.addSelectionListener(artifactListener); dateListPanel.selectFirst(); + removeAll(); + add(mainSplitPane); revalidate(); repaint(); try { @@ -157,21 +168,18 @@ class MiniTimelinePanel extends javax.swing.JPanel { mainSplitPane = new javax.swing.JSplitPane(); leftSplitPane = new javax.swing.JSplitPane(); - setMinimumSize(new java.awt.Dimension(0, 0)); - setPreferredSize(new java.awt.Dimension(0, 0)); - setLayout(new java.awt.BorderLayout()); - - mainSplitPane.setResizeWeight(0.4); + mainSplitPane.setDividerLocation(400); + mainSplitPane.setResizeWeight(0.1); mainSplitPane.setToolTipText(""); mainSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); - mainSplitPane.setPreferredSize(new java.awt.Dimension(0, 0)); + leftSplitPane.setDividerLocation(198); leftSplitPane.setResizeWeight(0.5); leftSplitPane.setMinimumSize(new java.awt.Dimension(0, 0)); - leftSplitPane.setPreferredSize(new java.awt.Dimension(300, 0)); mainSplitPane.setLeftComponent(leftSplitPane); - add(mainSplitPane, java.awt.BorderLayout.CENTER); + setMinimumSize(new java.awt.Dimension(0, 0)); + setLayout(new java.awt.BorderLayout()); }// //GEN-END:initComponents diff --git a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java index 5df30ba3f6..e9122256ba 100644 --- a/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java +++ b/Core/src/org/sleuthkit/autopsy/discovery/ui/MiniTimelineWorker.java @@ -57,10 +57,10 @@ class MiniTimelineWorker extends SwingWorker, Void> { DomainSearch domainSearch = new DomainSearch(); try { results.addAll(domainSearch.getAllArtifactsForDomain(Case.getCurrentCase().getSleuthkitCase(), domain)); - } catch (DiscoveryException ex) { if (ex.getCause() instanceof InterruptedException) { - logger.log(Level.INFO, "MiniTimeline search was cancelled or interrupted for domain: {0}", domain); + this.cancel(true); + //ignore the exception as it was cancelled while the cache was performing its get and we support cancellation } else { throw ex; } diff --git a/docs/doxygen-user/data_source_summary.dox b/docs/doxygen-user/data_source_summary.dox index f656e5a6ab..459dd7f611 100644 --- a/docs/doxygen-user/data_source_summary.dox +++ b/docs/doxygen-user/data_source_summary.dox @@ -32,7 +32,7 @@ The Types tab shows counts of different file types found in the data source. \subsection ds_summary_user_activity User Activity -The User Activity tab shows the most recent results found in the data source. +The User Activity tab shows the most recent results found in the data source. You can right click on a row to navigate directly to the corresponding result. \image html ds_summary_user_activity.png @@ -44,7 +44,7 @@ The Analysis tab shows the sets with the most results from the \ref hash_db_page \subsection ds_summary_recent_files Recent Files -The Recent Files tab shows information on the most recent files opened and downloaded. +The Recent Files tab shows information on the most recent files opened and downloaded. You can right click on a row to navigate directly to the corresponding file or result. \image html ds_summary_recent_files.png @@ -56,6 +56,18 @@ The Past Cases tab shows which cases had results or notable files in common with Note that because these entries are based on the Interesting Items results created during ingest and not querying the central repository, they will not reflect any matches in cases processed after this case. For example, suppose we create Case A and ingest a data source with Device Z. If we make a new case Case B afterward and ingest a data source that also has Device Z, we would see Case A listed in this tab for Case B, but if we reopened Case A we would not see Case B listed unless ingest was run again. +\subsection ds_summary_geo Geolocation + +The Geolocation tab uses the coordinates from geolocation results to find the nearest city for each and displays the most recent cities and most common cities. If the location is more than 150 km from a city then it will be displayed as "Unknown". The "View in Map" button under the recent cities table will open the \ref geolocation_page "Geolocation window" showing all waypoints for this data source with timestamps in the last 30 days. The "View in Map" button under the most common cities will show all waypoints for this data source. + +\image html ds_summary_geo.png + +\subsection ds_summary_timeline Timeline + +The Timeline tab shows a simplified version of the \ref timeline_page "Timeline Viewer" for the selected data source. It will show events for the last 30 days of activity in the data source and give the first and last dates of activity. "File events" represent file creation, modification, access, and change. "Result events" represent the results from running ingest, such as the time a message was sent or when a URL was accessed. The "View in Timeline" button will open the main \ref timeline_page "Timeline Viewer". + +\image html ds_summary_timeline.png + \subsection ds_summary_ingest_history Ingest History The Ingest History tab shows which ingest modules have been run on the data source and the version of each module. diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png index 15957a0712..a1e6bfb842 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_analysis.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png index 3adf7ec40d..3d51efee73 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_container.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png new file mode 100644 index 0000000000..6b6d1c5931 Binary files /dev/null and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_geo.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png index 203e9c44ee..d3a1a970a9 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_ingest.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png index 591cb66867..3b47578b0e 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_past_cases.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png index af63e103f7..3eeaeffd89 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_recent_files.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png index ad16f2e031..8f2b4d3f47 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_result_viewer.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png new file mode 100644 index 0000000000..1ab2cdc033 Binary files /dev/null and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_timeline.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png index db3f4a14f9..2bf93d3f90 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_types.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png index 2ce9dcaf16..7b6d4d09f5 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_user_activity.png differ diff --git a/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png b/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png index 745c3b1d97..a9d61e9084 100644 Binary files a/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png and b/docs/doxygen-user/images/DataSourceSummary/ds_summary_window.png differ