diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java new file mode 100755 index 0000000000..b1fc5559b9 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/datamodel/RecentFilesSummary.java @@ -0,0 +1,399 @@ +/* + * Autopsy Forensic Browser + * + * 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.datasourcesummary.datamodel; + +import java.nio.file.Paths; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.SortedMap; +import java.util.TreeMap; +import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_EMAIL_MSG; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_MESSAGE; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_RECENT_OBJECT; +import static org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE.TSK_WEB_DOWNLOAD; +import org.sleuthkit.datamodel.BlackboardAttribute; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.SleuthkitCaseProvider.SleuthkitCaseProviderException; + +/** + * Helper class for getting data for the Recent Files Data Summary tab. + */ +public class RecentFilesSummary { + + private final static BlackboardAttribute.Type DATETIME_ACCESSED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_ACCESSED); + private final static BlackboardAttribute.Type DOMAIN_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DOMAIN); + private final static BlackboardAttribute.Type PATH_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH); + private final static BlackboardAttribute.Type DATETIME_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME); + private final static BlackboardAttribute.Type ASSOCATED_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT); + private final static BlackboardAttribute.Type EMAIL_FROM_ATT = new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM); + private final static BlackboardArtifact.Type ASSOCATED_OBJ_ART = new BlackboardArtifact.Type(BlackboardArtifact.ARTIFACT_TYPE.TSK_ASSOCIATED_OBJECT); + + private static final DateFormat DATETIME_FORMAT = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss", Locale.getDefault()); + + private final SleuthkitCaseProvider provider; + + /** + * Default constructor. + */ + public RecentFilesSummary() { + this(SleuthkitCaseProvider.DEFAULT); + } + + /** + * Construct object with given SleuthkitCaseProvider + * + * @param provider SleuthkitCaseProvider provider, cannot be null. + */ + public RecentFilesSummary(SleuthkitCaseProvider provider) { + if (provider == null) { + throw new IllegalArgumentException("Unable to construct RecentFileSummary object. SleuthkitCaseProvider cannot be null"); + } + + this.provider = provider; + } + + /** + * Return a list of the most recently opened documents based on the + * TSK_RECENT_OBJECT artifact. + * + * @param dataSource The data source to query. + * @param maxCount The maximum number of results to return, pass 0 to get + * a list of all results. + * + * @return A list RecentFileDetails representing the most recently opened + * documents or an empty list if none were found. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getRecentlyOpenedDocuments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException { + if (dataSource == null) { + throw new IllegalArgumentException("Failed to get recently opened documents given data source was null"); + } + + List artifactList + = DataSourceInfoUtilities.getArtifacts(provider.get(), + new BlackboardArtifact.Type(TSK_RECENT_OBJECT), + dataSource, + DATETIME_ATT, + DataSourceInfoUtilities.SortOrder.DESCENDING, + 10); + + List fileDetails = new ArrayList<>(); + for (BlackboardArtifact artifact : artifactList) { + Long accessedTime = null; + String path = ""; + + // Get all the attributes in one call. + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + + if (attribute.getAttributeType().equals(DATETIME_ATT)) { + accessedTime = attribute.getValueLong(); + } else if (attribute.getAttributeType().equals(PATH_ATT)) { + path = attribute.getValueString(); + } + + if (accessedTime != null) { + fileDetails.add(new RecentFileDetails(path, accessedTime)); + } + } + + } + + return fileDetails; + } + + /** + * Return a list of the most recent downloads based on the value of the the + * artifact TSK_DATETIME_ACCESSED attribute. + * + * @param dataSource Data source to query. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. + * + * @return A list of RecentFileDetails objects or empty list if none were + * found. + * + * @throws TskCoreException + * @throws SleuthkitCaseProviderException + */ + public List getRecentDownloads(DataSource dataSource, int maxCount) throws TskCoreException, SleuthkitCaseProviderException { + List artifactList + = DataSourceInfoUtilities.getArtifacts(provider.get(), + new BlackboardArtifact.Type(TSK_WEB_DOWNLOAD), + dataSource, + DATETIME_ACCESSED_ATT, + DataSourceInfoUtilities.SortOrder.DESCENDING, + maxCount); + + List fileDetails = new ArrayList<>(); + for (BlackboardArtifact artifact : artifactList) { + // Get all the attributes in one call. + Long accessedTime = null; + String domain = ""; + String path = ""; + + List attributeList = artifact.getAttributes(); + for (BlackboardAttribute attribute : attributeList) { + + if (attribute.getAttributeType().equals(DATETIME_ACCESSED_ATT)) { + accessedTime = attribute.getValueLong(); + } else if (attribute.getAttributeType().equals(DOMAIN_ATT)) { + domain = attribute.getValueString(); + } else if (attribute.getAttributeType().equals(PATH_ATT)) { + path = attribute.getValueString(); + } + } + if (accessedTime != null) { + fileDetails.add(new RecentDownloadDetails(path, accessedTime, domain)); + } + } + + return fileDetails; + } + + /** + * Returns a list of the most recent message attachments. + * + * @param dataSource Data source to query. + * @param maxCount Maximum number of results to return, passing 0 will + * return all results. + * + * @return A list of RecentFileDetails of the most recent attachments. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + public List getRecentAttachments(DataSource dataSource, int maxCount) throws SleuthkitCaseProviderException, TskCoreException { + return createListFromMap(buildAttachmentMap(dataSource), maxCount); + } + + /** + * Build a map of all of the message attachment sorted in date order. + * + * @param dataSource Data source to query. + * + * @return Returns a SortedMap of details objects returned in descending + * order. + * + * @throws SleuthkitCaseProviderException + * @throws TskCoreException + */ + private SortedMap> buildAttachmentMap(DataSource dataSource) throws SleuthkitCaseProviderException, TskCoreException { + SleuthkitCase skCase = provider.get(); + TreeMap> sortedMap = new TreeMap<>(); + + List associatedArtifacts = skCase.getBlackboard().getArtifacts(ASSOCATED_OBJ_ART.getTypeID(), dataSource.getId()); + for (BlackboardArtifact artifact : associatedArtifacts) { + BlackboardAttribute attribute = artifact.getAttribute(ASSOCATED_ATT); + if (attribute == null) { + continue; + } + + BlackboardArtifact messageArtifact = skCase.getBlackboardArtifact(attribute.getValueLong()); + if (isMessageArtifact(messageArtifact)) { + Content content = artifact.getParent(); + if (content instanceof AbstractFile) { + String sender; + Long date; + String path; + + BlackboardAttribute senderAttribute = messageArtifact.getAttribute(EMAIL_FROM_ATT); + if (senderAttribute != null) { + sender = senderAttribute.getValueString(); + } else { + sender = ""; + } + AbstractFile abstractFile = (AbstractFile) content; + date = abstractFile.getCrtime(); + path = Paths.get(abstractFile.getParentPath(), abstractFile.getName()).toString(); + + List list = sortedMap.get(date); + if (list == null) { + list = new ArrayList<>(); + sortedMap.put(date, list); + } + + list.add(new RecentAttachmentDetails(path, date, sender)); + } + } + } + return sortedMap.descendingMap(); + } + + /** + * Create a list of detail objects from the given sorted map of the max + * size. + * + * @param sortedMap A Map of attachment details sorted by date. + * @param maxCount Maximum number of values to return. + * + * @return A list of the details of the most recent attachments or empty + * list if none where found. + */ + private List createListFromMap(SortedMap> sortedMap, int maxCount) { + List fileList = new ArrayList<>(); + + for (List mapList : sortedMap.values()) { + if (maxCount == 0 || fileList.size() + mapList.size() <= maxCount) { + fileList.addAll(mapList); + continue; + } + + if (maxCount == fileList.size()) { + break; + } + + for (RecentAttachmentDetails details : mapList) { + if (fileList.size() < maxCount) { + fileList.add(details); + } else { + break; + } + } + } + + return fileList; + } + + /** + * Is the given artifact a message. + * + * @param nodeArtifact An artifact that might be a message. Must not be + * null. + * + * @return True if the given artifact is a message artifact + */ + private boolean isMessageArtifact(BlackboardArtifact nodeArtifact) { + final int artifactTypeID = nodeArtifact.getArtifactTypeID(); + return artifactTypeID == TSK_EMAIL_MSG.getTypeID() + || artifactTypeID == TSK_MESSAGE.getTypeID(); + } + + /** + * General data model object for files object. + */ + public static class RecentFileDetails { + + private final String path; + private final long date; + + /** + * Constructor for files with just a path and date. + * + * @param path File path. + * @param date File access date\time in seconds with java epoch + */ + RecentFileDetails(String path, long date) { + this.path = path; + this.date = date; + } + + /** + * Returns the formatted date because the JTablePanel render assumes + * only string data. + * + * @return Formatted date time. + */ + public String getDate() { + return DATETIME_FORMAT.format(date * 1000); + } + + /** + * Returns the file path. + * + * @return The file path. + */ + public String getPath() { + return path; + } + + } + + /** + * Data model for recently downloaded files. + */ + public static class RecentDownloadDetails extends RecentFileDetails { + + private final String webDomain; + + /** + * Constructor for files with just a path and date. + * + * @param path File path. + * @param date File access date\time in seconds with java epoch. + * @param webDomain The webdomain from which the file was downloaded. + */ + RecentDownloadDetails(String path, long date, String webDomain) { + super(path, date); + this.webDomain = webDomain; + } + + /** + * Returns the web domain. + * + * @return The web domain or empty string if not available or + * applicable. + */ + public String getWebDomain() { + return webDomain; + } + } + + /** + * Data model for recently attachments. + */ + public static class RecentAttachmentDetails extends RecentFileDetails { + + private final String sender; + + /** + * Constructor for recent download files which have a path, date and + * domain value. + * + * @param path File path. + * @param date File crtime. + * @param sender The sender of the message from which the + * file was attached. + */ + RecentAttachmentDetails(String path, long date, String sender) { + super(path, date); + this.sender = sender; + } + + /** + * Return the sender of the attached file. + * + * @return The sender of the attached file or empty string if not + * available. + */ + public String getSender() { + return sender; + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties index 708010e07a..ddad1061f9 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties @@ -37,3 +37,6 @@ DataSourceSummaryCountsPanel.byCategoryLabel.text=Files by Category DataSourceSummaryCountsPanel.resultsByTypeLabel.text=Results by Type DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains +RecentFilesPanel.openDocsLabel.text=Recently Opened Documents +RecentFilesPanel.downloadLabel.text=Recent Downloads +RecentFilesPanel.attachmentLabel.text=Recent Attachements diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED index 98f650b3de..5d126b8427 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/Bundle.properties-MERGED @@ -72,6 +72,7 @@ DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source DataSourceSummaryTabbedPane_countsTab_title=Counts DataSourceSummaryTabbedPane_detailsTab_title=Details DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History +DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files DataSourceSummaryTabbedPane_userActivityTab_title=User Activity DataSourceSummaryUserActivityPanel.programsRunLabel.text=Recent Programs DataSourceSummaryUserActivityPanel.recentDomainsLabel.text=Recent Domains @@ -84,4 +85,12 @@ DataSourceSummaryUserActivityPanel_TopProgramsTableModel_count_header=Run Times DataSourceSummaryUserActivityPanel_TopProgramsTableModel_folder_header=Folder DataSourceSummaryUserActivityPanel_TopProgramsTableModel_lastrun_header=Last Run DataSourceSummaryUserActivityPanel_TopProgramsTableModel_name_header=Program +RecentFilePanel_col_header_domain=Domain +RecentFilePanel_col_header_path=Path +RecentFilePanel_col_header_sender=Sender +RecentFilePanel_no_open_documents=No recently open documents found. +RecentFilesPanel.openDocsLabel.text=Recently Opened Documents +RecentFilesPanel.downloadLabel.text=Recent Downloads +RecentFilesPanel.attachmentLabel.text=Recent Attachements +RecentFilesPanel_col_head_date=Date ViewSummaryInformationAction.name.text=View Summary Information diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java index 7e0cd951c3..2c2d402b36 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/DataSourceSummaryTabbedPane.java @@ -35,7 +35,8 @@ import org.sleuthkit.datamodel.DataSource; "DataSourceSummaryTabbedPane_countsTab_title=Counts", "DataSourceSummaryTabbedPane_detailsTab_title=Details", "DataSourceSummaryTabbedPane_userActivityTab_title=User Activity", - "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History" + "DataSourceSummaryTabbedPane_ingestHistoryTab_title=Ingest History", + "DataSourceSummaryTabbedPane_recentFileTab_title=Recent Files" }) public class DataSourceSummaryTabbedPane extends JTabbedPane { @@ -45,7 +46,8 @@ public class DataSourceSummaryTabbedPane extends JTabbedPane { private final List> tabs = Arrays.asList( Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryDetailsPanel()), Pair.of(Bundle.DataSourceSummaryTabbedPane_countsTab_title(), new DataSourceSummaryCountsPanel()), - Pair.of(Bundle.DataSourceSummaryTabbedPane_detailsTab_title(), new DataSourceSummaryUserActivityPanel()) + Pair.of(Bundle.DataSourceSummaryTabbedPane_userActivityTab_title(), new DataSourceSummaryUserActivityPanel()), + Pair.of(Bundle.DataSourceSummaryTabbedPane_recentFileTab_title(), new RecentFilesPanel()) ); private final IngestJobInfoPanel ingestHistoryPanel = new IngestJobInfoPanel(); diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form new file mode 100755 index 0000000000..0bd5c19938 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.form @@ -0,0 +1,92 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java new file mode 100755 index 0000000000..777766a9cb --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/ui/RecentFilesPanel.java @@ -0,0 +1,294 @@ +/* + * Autopsy Forensic Browser + * + * 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.datasourcesummary.ui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; +import org.openide.util.NbBundle.Messages; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentAttachmentDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentDownloadDetails; +import org.sleuthkit.autopsy.datasourcesummary.datamodel.RecentFilesSummary.RecentFileDetails; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.CellModelTableCellRenderer.DefaultCellModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchResult; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.DataFetchWorker; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.JTablePanel.ColumnModel; +import org.sleuthkit.autopsy.datasourcesummary.uiutils.ListTableModel; +import org.sleuthkit.datamodel.DataSource; + +/** + * Data Source Summary recent files panel. + */ +public final class RecentFilesPanel extends BaseDataSourceSummaryPanel { + + private static final long serialVersionUID = 1L; + + private final List> tablePanelList = new ArrayList<>(); + private final List> dataFetchComponents = new ArrayList<>(); + + private final RecentFilesSummary dataHandler; + + @Messages({ + "RecentFilesPanel_col_head_date=Date", + "RecentFilePanel_col_header_domain=Domain", + "RecentFilePanel_col_header_path=Path", + "RecentFilePanel_col_header_sender=Sender" + }) + + /** + * Default constructor. + */ + public RecentFilesPanel() { + this(new RecentFilesSummary()); + } + + /** + * Creates new form RecentFilesPanel + */ + public RecentFilesPanel(RecentFilesSummary dataHandler) { + this.dataHandler = dataHandler; + + initComponents(); + initalizeTables(); + } + + @Override + protected void onNewDataSource(DataSource dataSource) { + // if no data source is present or the case is not open, + // set results for tables to null. + if (dataSource == null || !Case.isCaseOpen()) { + this.dataFetchComponents.forEach((item) -> item.getResultHandler() + .accept(DataFetchResult.getSuccessResult(null))); + + } else { + // set tables to display loading screen + tablePanelList.forEach((table) -> table.showDefaultLoadingMessage()); + + // create swing workers to run for each table + List> workers = dataFetchComponents + .stream() + .map((components) -> new DataFetchWorker<>(components, dataSource)) + .collect(Collectors.toList()); + + // submit swing workers to run + submit(workers); + } + } + + /** + * Setup the data model and columns for the panel tables. + */ + private void initalizeTables() { + initalizeOpenDocsTable(); + initalizeDownloadTable(); + initalizeAttchementsTable(); + } + + @Messages({ + "RecentFilePanel_no_open_documents=No recently open documents found." + }) + /** + * Setup the data model and columns for the recently open table. + */ + @SuppressWarnings("unchecked") + private void initalizeOpenDocsTable() { + List> list = Arrays.asList( + new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + (prog) -> { + return new DefaultCellModel(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), + (prog) -> { + return new DefaultCellModel(prog.getDate()); + }, 80)); + + ListTableModel tableModel = JTablePanel.getTableModel(list); + + JTablePanel pane = (JTablePanel) openedDocPane; + pane.setModel(tableModel); + pane.setColumnModel(JTablePanel.getTableColumnModel(list)); + tablePanelList.add(pane); + + DataFetchWorker.DataFetchComponents> worker + = new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> dataHandler.getRecentlyOpenedDocuments(dataSource, 10), + (result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.RecentFilePanel_no_open_documents())); + + dataFetchComponents.add(worker); + } + + /** + * Setup the data model and columns for the recent download table. + */ + @SuppressWarnings("unchecked") + private void initalizeDownloadTable() { + List> list = Arrays.asList( + new ColumnModel<>(Bundle.RecentFilePanel_col_header_domain(), + (prog) -> { + return new DefaultCellModel(prog.getWebDomain()); + }, 100), + new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + (prog) -> { + return new DefaultCellModel(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), + (prog) -> { + return new DefaultCellModel(prog.getDate()); + }, 80)); + + ListTableModel tableModel = JTablePanel.getTableModel(list); + + JTablePanel pane = (JTablePanel) downloadsPane; + pane.setModel(tableModel); + pane.setColumnModel(JTablePanel.getTableColumnModel(list)); + tablePanelList.add(pane); + + DataFetchWorker.DataFetchComponents> worker + = new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> dataHandler.getRecentDownloads(dataSource, 10), + (result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.RecentFilePanel_no_open_documents())); + + dataFetchComponents.add(worker); + } + + /** + * Setup the data model and columns for the recent attachments. + */ + @SuppressWarnings("unchecked") + private void initalizeAttchementsTable() { + List> list = Arrays.asList( + new ColumnModel<>(Bundle.RecentFilePanel_col_header_path(), + (prog) -> { + return new DefaultCellModel(prog.getPath()); + }, 250), + new ColumnModel<>(Bundle.RecentFilesPanel_col_head_date(), + (prog) -> { + return new DefaultCellModel(prog.getDate()); + }, 80), + new ColumnModel<>(Bundle.RecentFilePanel_col_header_sender(), + (prog) -> { + return new DefaultCellModel(prog.getSender()); + }, 150)); + + ListTableModel tableModel = JTablePanel.getTableModel(list); + + JTablePanel pane = (JTablePanel) attachmentsPane; + pane.setModel(tableModel); + pane.setColumnModel(JTablePanel.getTableColumnModel(list)); + tablePanelList.add(pane); + + DataFetchWorker.DataFetchComponents> worker + = new DataFetchWorker.DataFetchComponents<>( + (dataSource) -> dataHandler.getRecentAttachments(dataSource, 10), + (result) -> pane.showDataFetchResult(result, JTablePanel.getDefaultErrorMessage(), + Bundle.RecentFilePanel_no_open_documents())); + + dataFetchComponents.add(worker); + } + + /** + * 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() { + java.awt.GridBagConstraints gridBagConstraints; + + openedDocPane = new JTablePanel(); + downloadsPane = new JTablePanel(); + attachmentsPane = new JTablePanel(); + openDocsLabel = new javax.swing.JLabel(); + downloadLabel = new javax.swing.JLabel(); + attachmentLabel = new javax.swing.JLabel(); + + setLayout(new java.awt.GridBagLayout()); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 1; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5); + add(openedDocPane, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 3; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 0, 5); + add(downloadsPane, gridBagConstraints); + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 5; + gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.weightx = 1.0; + gridBagConstraints.weighty = 1.0; + gridBagConstraints.insets = new java.awt.Insets(5, 5, 10, 5); + add(attachmentsPane, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(openDocsLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.openDocsLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 0; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.insets = new java.awt.Insets(10, 5, 0, 5); + add(openDocsLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(downloadLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.downloadLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 2; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.insets = new java.awt.Insets(15, 5, 0, 5); + add(downloadLabel, gridBagConstraints); + + org.openide.awt.Mnemonics.setLocalizedText(attachmentLabel, org.openide.util.NbBundle.getMessage(RecentFilesPanel.class, "RecentFilesPanel.attachmentLabel.text")); // NOI18N + gridBagConstraints = new java.awt.GridBagConstraints(); + gridBagConstraints.gridx = 0; + gridBagConstraints.gridy = 4; + gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL; + gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH; + gridBagConstraints.insets = new java.awt.Insets(15, 5, 0, 5); + add(attachmentLabel, gridBagConstraints); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JLabel attachmentLabel; + private javax.swing.JPanel attachmentsPane; + private javax.swing.JLabel downloadLabel; + private javax.swing.JPanel downloadsPane; + private javax.swing.JLabel openDocsLabel; + private javax.swing.JPanel openedDocPane; + // End of variables declaration//GEN-END:variables + +} diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java index cb826fefbd..955ae4f438 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/CellModelTableCellRenderer.java @@ -98,6 +98,7 @@ public class CellModelTableCellRenderer extends DefaultTableCellRenderer { */ public DefaultCellModel(String text) { this.text = text; + this.setTooltip(text); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java index 1e4184d85b..9bc3a8d380 100644 --- a/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java +++ b/Core/src/org/sleuthkit/autopsy/datasourcesummary/uiutils/JTablePanel.java @@ -231,7 +231,7 @@ public class JTablePanel extends JPanel { .map((colModel) -> colModel.getCellRenderer()) .collect(Collectors.toList()); - return new DefaultListTableModel(columnRenderers); + return new DefaultListTableModel<>(columnRenderers); } /** @@ -264,26 +264,47 @@ public class JTablePanel extends JPanel { private final JScrollPane tableScrollPane; private final Overlay overlayLayer; - private final ListTableModel tableModel; + private ListTableModel tableModel; private final JTable table; /** - * Main constructor. + * Panel constructor. * * @param tableModel The model to use for the table. */ public JTablePanel(ListTableModel tableModel) { - this.tableModel = tableModel; - this.table = new JTable(tableModel); + this(); + setModel(tableModel); + } + + /** + * Default constructor. + */ + public JTablePanel() { + this.table = new JTable(); this.table.getTableHeader().setReorderingAllowed(false); this.overlayLayer = new Overlay(); this.tableScrollPane = new JScrollPane(table); - JLayer dualLayer = new JLayer(tableScrollPane, overlayLayer); + JLayer dualLayer = new JLayer<>(tableScrollPane, overlayLayer); setLayout(new BorderLayout()); add(dualLayer, BorderLayout.CENTER); } + /** + * Set the table model. + * + * @param tableModel + */ + public void setModel(ListTableModel tableModel) { + if (tableModel == null) { + throw new IllegalArgumentException("Null table model passed to setModel"); + } + + this.tableModel = tableModel; + table.setModel(tableModel); + } + /** * @return The underlying JTable's column model. */