Merge remote-tracking branch 'upstream/develop' into 2146-3
@ -99,7 +99,7 @@ class AddImageTask implements Runnable {
|
||||
List<String> errorMessages = new ArrayList<>();
|
||||
List<Content> newDataSources = new ArrayList<>();
|
||||
try {
|
||||
currentCase.getSleuthkitCase().acquireExclusiveLock();
|
||||
currentCase.getSleuthkitCase().acquireExclusiveLockForSQLite();
|
||||
synchronized (tskAddImageProcessLock) {
|
||||
tskAddImageProcess = currentCase.makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles);
|
||||
}
|
||||
@ -112,7 +112,7 @@ class AddImageTask implements Runnable {
|
||||
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
|
||||
progressMonitor.setProgress(100);
|
||||
} finally {
|
||||
currentCase.getSleuthkitCase().releaseExclusiveLock();
|
||||
currentCase.getSleuthkitCase().releaseExclusiveLockForSQLite();
|
||||
DataSourceProcessorCallback.DataSourceProcessorResult result;
|
||||
if (criticalErrorOccurred) {
|
||||
result = DataSourceProcessorResult.CRITICAL_ERRORS;
|
||||
|
@ -46,7 +46,6 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.NbBundle.Messages;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
@ -76,6 +75,7 @@ import org.sleuthkit.autopsy.events.AutopsyEventException;
|
||||
import org.sleuthkit.autopsy.events.AutopsyEventPublisher;
|
||||
import org.sleuthkit.autopsy.ingest.IngestJob;
|
||||
import org.sleuthkit.autopsy.ingest.IngestManager;
|
||||
import org.sleuthkit.autopsy.timeline.OpenTimelineAction;
|
||||
import org.sleuthkit.datamodel.BlackboardArtifactTag;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.ContentTag;
|
||||
@ -1519,28 +1519,22 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
||||
|
||||
if (RuntimeProperties.coreComponentsAreActive()) {
|
||||
// enable these menus
|
||||
CallableSystemAction.get(AddImageAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); // Delete Case menu
|
||||
|
||||
if (toChangeTo.hasData()) {
|
||||
// open all top components
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
CoreComponentControl.openCoreWindows();
|
||||
});
|
||||
} else {
|
||||
// close all top components
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
CoreComponentControl.closeCoreWindows();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (RuntimeProperties.coreComponentsAreActive()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
updateMainWindowTitle(currentCase.getName());
|
||||
CallableSystemAction.get(AddImageAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(true);
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(true); // Delete Case menu
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(true);
|
||||
|
||||
if (toChangeTo.hasData()) {
|
||||
// open all top components
|
||||
CoreComponentControl.openCoreWindows();
|
||||
} else {
|
||||
// close all top components
|
||||
CoreComponentControl.closeCoreWindows();
|
||||
}
|
||||
});
|
||||
updateMainWindowTitle(currentCase.getName());
|
||||
} else {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Frame f = WindowManager.getDefault().getMainWindow();
|
||||
@ -1549,9 +1543,9 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
||||
}
|
||||
|
||||
} else { // case is closed
|
||||
if (RuntimeProperties.coreComponentsAreActive()) {
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
if (RuntimeProperties.coreComponentsAreActive()) {
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
// close all top components first
|
||||
CoreComponentControl.closeCoreWindows();
|
||||
|
||||
@ -1560,15 +1554,11 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
||||
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu
|
||||
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu
|
||||
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu
|
||||
});
|
||||
}
|
||||
CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
|
||||
}
|
||||
|
||||
//clear pending notifications
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
//clear pending notifications
|
||||
MessageNotifyUtil.Notify.clear();
|
||||
});
|
||||
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
Frame f = WindowManager.getDefault().getMainWindow();
|
||||
f.setTitle(Case.getAppName()); // set the window name to just application name
|
||||
});
|
||||
@ -1664,7 +1654,7 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
||||
* Deletes reports from the case.
|
||||
*
|
||||
* @param reports Collection of Report to be deleted from the case.
|
||||
* @param deleteFromDisk No longer supported - ignored.
|
||||
* @param deleteFromDisk No longer supported - ignored.
|
||||
*
|
||||
* @throws TskCoreException
|
||||
* @deprecated Use deleteReports(Collection<? extends Report> reports)
|
||||
@ -1674,5 +1664,5 @@ public class Case implements SleuthkitCase.ErrorObserver {
|
||||
public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
|
||||
deleteReports(reports);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -40,10 +40,10 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskFileRange;
|
||||
import org.sleuthkit.datamodel.VirtualDirectory;
|
||||
import org.sleuthkit.datamodel.CarvedFileContainer;
|
||||
import org.sleuthkit.datamodel.LocalFilesDataSource;
|
||||
import org.sleuthkit.datamodel.TskDataException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.sleuthkit.datamodel.CarvingResult;
|
||||
|
||||
/**
|
||||
* A manager that provides methods for retrieving files from the current case
|
||||
@ -53,7 +53,7 @@ import org.apache.commons.lang3.StringUtils;
|
||||
public class FileManager implements Closeable {
|
||||
|
||||
private SleuthkitCase caseDb;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a manager that provides methods for retrieving files from the
|
||||
* current case and for adding local files, carved files, and derived files
|
||||
@ -112,7 +112,7 @@ public class FileManager implements Closeable {
|
||||
String types = StringUtils.join(mimeTypes, "', '");
|
||||
return "mime_type IN ('" + types + "')";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Finds all files and directories with a given file name. The name search
|
||||
* is for full or partial matches and is case insensitive (a case
|
||||
@ -324,47 +324,23 @@ public class FileManager implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a carved file to the '$CarvedFiles' virtual directory of a data
|
||||
* source, volume or file system.
|
||||
* Adds a carving result to the case database.
|
||||
*
|
||||
* @param fileName The name of the file.
|
||||
* @param fileSize The size of the file.
|
||||
* @param parentObjId The object id of the parent data source, volume or
|
||||
* file system.
|
||||
* @param layout A list of the offsets and sizes that gives the layout
|
||||
* of the file within its parent.
|
||||
* @param carvingResult The carving result (a set of carved files and their
|
||||
* parent) to be added.
|
||||
*
|
||||
* @return A LayoutFile object representing the carved file.
|
||||
* @return A list of LayoutFile representations of the carved files.
|
||||
*
|
||||
* @throws TskCoreException if there is a problem adding the file to the
|
||||
* case database.
|
||||
* @throws TskCoreException If there is a problem completing a case database
|
||||
* operation.
|
||||
*/
|
||||
public synchronized LayoutFile addCarvedFile(String fileName, long fileSize, long parentObjId, List<TskFileRange> layout) throws TskCoreException {
|
||||
public synchronized List<LayoutFile> addCarvedFiles(CarvingResult carvingResult) throws TskCoreException {
|
||||
if (null == caseDb) {
|
||||
throw new TskCoreException("File manager has been closed");
|
||||
}
|
||||
return caseDb.addCarvedFile(fileName, fileSize, parentObjId, layout);
|
||||
return caseDb.addCarvedFiles(carvingResult);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of carved files to the '$CarvedFiles' virtual directory
|
||||
* of a data source, volume or file system.
|
||||
*
|
||||
* @param A collection of CarvedFileContainer objects, one per carved file,
|
||||
* all of which must have the same parent object id.
|
||||
*
|
||||
* @return A collection of LayoutFile object representing the carved files.
|
||||
*
|
||||
* @throws TskCoreException if there is a problem adding the files to the
|
||||
* case database.
|
||||
*/
|
||||
public synchronized List<LayoutFile> addCarvedFiles(List<CarvedFileContainer> filesToAdd) throws TskCoreException {
|
||||
if (null == caseDb) {
|
||||
throw new TskCoreException("File manager has been closed");
|
||||
}
|
||||
return caseDb.addCarvedFiles(filesToAdd);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Interface for receiving a notification for each file or directory added
|
||||
* to the case database by a FileManager add files operation.
|
||||
@ -552,6 +528,16 @@ public class FileManager implements Closeable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the file manager.
|
||||
*
|
||||
* @throws IOException If there is a problem closing the file manager.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
caseDb = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a set of local/logical files and/or directories to the case database
|
||||
* as data source.
|
||||
@ -583,13 +569,55 @@ public class FileManager implements Closeable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the file manager.
|
||||
* Adds a carved file to the '$CarvedFiles' virtual directory of a data
|
||||
* source, volume or file system.
|
||||
*
|
||||
* @throws IOException If there is a problem closing the file manager.
|
||||
* @param fileName The name of the file.
|
||||
* @param fileSize The size of the file.
|
||||
* @param parentObjId The object id of the parent data source, volume or
|
||||
* file system.
|
||||
* @param layout A list of the offsets and sizes that gives the layout
|
||||
* of the file within its parent.
|
||||
*
|
||||
* @return A LayoutFile object representing the carved file.
|
||||
*
|
||||
* @throws TskCoreException if there is a problem adding the file to the
|
||||
* case database.
|
||||
* @deprecated Use List<LayoutFile> addCarvedFiles(CarvingResult
|
||||
* carvingResult instead.
|
||||
*/
|
||||
@Override
|
||||
public synchronized void close() throws IOException {
|
||||
caseDb = null;
|
||||
@Deprecated
|
||||
public synchronized LayoutFile addCarvedFile(String fileName, long fileSize, long parentObjId, List<TskFileRange> layout) throws TskCoreException {
|
||||
if (null == caseDb) {
|
||||
throw new TskCoreException("File manager has been closed");
|
||||
}
|
||||
Content parent = caseDb.getContentById(parentObjId);
|
||||
List<CarvingResult.CarvedFile> carvedFiles = new ArrayList<>();
|
||||
carvedFiles.add(new CarvingResult.CarvedFile(fileName, fileSize, layout));
|
||||
List<LayoutFile> layoutFiles = caseDb.addCarvedFiles(new CarvingResult(parent, carvedFiles));
|
||||
return layoutFiles.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of carved files to the '$CarvedFiles' virtual directory
|
||||
* of a data source, volume or file system.
|
||||
*
|
||||
* @param A collection of CarvedFileContainer objects, one per carved file,
|
||||
* all of which must have the same parent object id.
|
||||
*
|
||||
* @return A collection of LayoutFile object representing the carved files.
|
||||
*
|
||||
* @throws TskCoreException if there is a problem adding the files to the
|
||||
* case database.
|
||||
* @deprecated Use List<LayoutFile> addCarvedFiles(CarvingResult
|
||||
* carvingResult instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public synchronized List<LayoutFile> addCarvedFiles(List<org.sleuthkit.datamodel.CarvedFileContainer> filesToAdd) throws TskCoreException {
|
||||
if (null == caseDb) {
|
||||
throw new TskCoreException("File manager has been closed");
|
||||
}
|
||||
return caseDb.addCarvedFiles(filesToAdd);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -387,11 +387,11 @@
|
||||
<file name="org-sleuthkit-autopsy-report-ReportWizardAction.shadow">
|
||||
<attr name="displayName" bundlevalue="org.sleuthkit.autopsy.report.Bundle#Toolbars/Reports/org-sleuthkit-autopsy-report-ReportWizardAction.shadow"/>
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-report-ReportWizardAction.instance"/>
|
||||
<attr name="position" intvalue="102"/>
|
||||
<attr name="position" intvalue="103"/>
|
||||
</file>
|
||||
</folder>
|
||||
<folder name="Ingest">
|
||||
<attr intvalue="103" name="position"/>
|
||||
<attr intvalue="104" name="position"/>
|
||||
<file name="org-sleuthkit-autopsy-ingest-IngestMessagesAction.shadow">
|
||||
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-ingest-IngestMessagesAction.instance"/>
|
||||
</file>
|
||||
|
@ -3,7 +3,7 @@
|
||||
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[5, 5]"/>
|
||||
<Dimension value="[0, 5]"/>
|
||||
</Property>
|
||||
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[5, 5]"/>
|
||||
@ -56,6 +56,9 @@
|
||||
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultPanel.directoryTablePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[5, 14]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="numberMatchLabel">
|
||||
@ -73,6 +76,11 @@
|
||||
</Properties>
|
||||
</Component>
|
||||
<Container class="javax.swing.JTabbedPane" name="dataResultTabbedPanel">
|
||||
<Properties>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[0, 5]"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
|
||||
</Container>
|
||||
|
@ -544,26 +544,29 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
matchLabel = new javax.swing.JLabel();
|
||||
dataResultTabbedPanel = new javax.swing.JTabbedPane();
|
||||
|
||||
setMinimumSize(new java.awt.Dimension(5, 5));
|
||||
setMinimumSize(new java.awt.Dimension(0, 5));
|
||||
setPreferredSize(new java.awt.Dimension(5, 5));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N
|
||||
directoryTablePath.setMinimumSize(new java.awt.Dimension(5, 14));
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(numberMatchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.numberMatchLabel.text")); // NOI18N
|
||||
|
||||
org.openide.awt.Mnemonics.setLocalizedText(matchLabel, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.matchLabel.text")); // NOI18N
|
||||
|
||||
dataResultTabbedPanel.setMinimumSize(new java.awt.Dimension(0, 5));
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addComponent(directoryTablePath)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 518, Short.MAX_VALUE)
|
||||
.addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(numberMatchLabel)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(matchLabel))
|
||||
.addComponent(dataResultTabbedPanel)
|
||||
.addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
@ -572,9 +575,9 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(numberMatchLabel)
|
||||
.addComponent(matchLabel))
|
||||
.addComponent(directoryTablePath))
|
||||
.addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
|
||||
.addGap(0, 0, 0)
|
||||
.addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 340, Short.MAX_VALUE))
|
||||
.addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -147,7 +147,6 @@ HashsetHits.createSheet.name.name=Name
|
||||
HashsetHits.createSheet.name.displayName=Name
|
||||
HashsetHits.createSheet.name.desc=no description
|
||||
ImageNode.getActions.viewInNewWin.text=View in New Window
|
||||
ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes
|
||||
ImageNode.createSheet.name.name=Name
|
||||
ImageNode.createSheet.name.displayName=Name
|
||||
ImageNode.createSheet.name.desc=no description
|
||||
|
@ -18,11 +18,14 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -32,6 +35,8 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.ingest.RunIngestModulesDialog;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.Image;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
@ -76,15 +81,25 @@ public class ImageNode extends AbstractContentNode<Image> {
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules",
|
||||
"ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",})
|
||||
public Action[] getActions(boolean context) {
|
||||
|
||||
List<Action> actionsList = new ArrayList<Action>();
|
||||
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||
actionsList.add(new FileSearchAction(
|
||||
Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
|
||||
actionsList.add(new AbstractAction(
|
||||
Bundle.ImageNode_action_runIngestMods_text()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final RunIngestModulesDialog ingestDialog = new RunIngestModulesDialog(Collections.<Content>singletonList(content));
|
||||
ingestDialog.display();
|
||||
}
|
||||
});
|
||||
|
||||
actionsList.add(new NewWindowViewAction(
|
||||
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this));
|
||||
actionsList.add(new FileSearchAction(
|
||||
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.openFileSearchByAttr.text")));
|
||||
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||
|
||||
return actionsList.toArray(new Action[0]);
|
||||
}
|
||||
|
||||
|
@ -18,13 +18,16 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.datamodel;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.AbstractAction;
|
||||
import javax.swing.Action;
|
||||
import org.openide.nodes.Sheet;
|
||||
import org.openide.util.NbBundle;
|
||||
@ -33,7 +36,10 @@ import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||
import org.sleuthkit.autopsy.ingest.RunIngestModulesDialog;
|
||||
import org.sleuthkit.datamodel.Content;
|
||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskData;
|
||||
@ -80,13 +86,25 @@ public class VirtualDirectoryNode extends AbstractAbstractFileNode<VirtualDirect
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
@NbBundle.Messages({"VirtualDirectoryNode.action.runIngestMods.text=Run Ingest Modules"})
|
||||
public Action[] getActions(boolean popup) {
|
||||
List<Action> actions = new ArrayList<>();
|
||||
|
||||
actions.add(new NewWindowViewAction(
|
||||
NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.getActions.viewInNewWin.text"), this));
|
||||
actions.add(null); // creates a menu separator
|
||||
actions.add(ExtractAction.getInstance());
|
||||
actions.add(null); // creates a menu separator
|
||||
actions.add(new FileSearchAction(
|
||||
Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
|
||||
actions.add(new AbstractAction(
|
||||
Bundle.VirtualDirectoryNode_action_runIngestMods_text()) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
final RunIngestModulesDialog ingestDialog = new RunIngestModulesDialog(Collections.<Content>singletonList(content));
|
||||
ingestDialog.display();
|
||||
}
|
||||
});
|
||||
actions.addAll(ContextMenuExtensionPoint.getActions());
|
||||
return actions.toArray(new Action[0]);
|
||||
}
|
||||
|
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
@ -39,4 +40,9 @@ abstract class AbstractFileSearchFilter<T extends JComponent> implements FileSea
|
||||
public T getComponent() {
|
||||
return this.component;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener listener) {
|
||||
this.getComponent().addPropertyChangeListener(listener);
|
||||
}
|
||||
}
|
||||
|
@ -205,6 +205,11 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
|
||||
getComponent().addActionListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return this.getComponent().isValidSearch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to put the separator inside the combo box.
|
||||
*/
|
||||
|
@ -236,6 +236,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.modifiedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="modifiedCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="changedCheckBox">
|
||||
<Properties>
|
||||
@ -244,6 +247,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.changedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="changedCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="accessedCheckBox">
|
||||
<Properties>
|
||||
@ -252,6 +258,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.accessedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="accessedCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="createdCheckBox">
|
||||
<Properties>
|
||||
@ -260,6 +269,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.createdCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createdCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="org.jbundle.thin.base.screen.jcalendarbutton.JCalendarButton" name="dateFromButtonCalendar">
|
||||
<Properties>
|
||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.util.Date;
|
||||
@ -37,6 +39,7 @@ class DateSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
DateFormat dateFormat;
|
||||
List<String> timeZones;
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
DateSearchPanel(DateFormat dateFormat, List<String> timeZones) {
|
||||
this.dateFormat = dateFormat;
|
||||
@ -133,6 +136,16 @@ class DateSearchPanel extends javax.swing.JPanel {
|
||||
this.changedCheckBox.setEnabled(enable);
|
||||
this.createdCheckBox.setEnabled(enable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@ -209,15 +222,35 @@ class DateSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
modifiedCheckBox.setSelected(true);
|
||||
modifiedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.modifiedCheckBox.text")); // NOI18N
|
||||
modifiedCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
modifiedCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
changedCheckBox.setSelected(true);
|
||||
changedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.changedCheckBox.text")); // NOI18N
|
||||
changedCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
changedCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
accessedCheckBox.setSelected(true);
|
||||
accessedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.accessedCheckBox.text")); // NOI18N
|
||||
accessedCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
accessedCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
createdCheckBox.setSelected(true);
|
||||
createdCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.createdCheckBox.text")); // NOI18N
|
||||
createdCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
createdCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
dateFromButtonCalendar.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateFromButtonCalendar.text")); // NOI18N
|
||||
dateFromButtonCalendar.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
|
||||
@ -349,8 +382,25 @@ class DateSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private void dateCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dateCheckBoxActionPerformed
|
||||
this.setComponentsEnabled();
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_dateCheckBoxActionPerformed
|
||||
|
||||
private void modifiedCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_modifiedCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_modifiedCheckBoxActionPerformed
|
||||
|
||||
private void accessedCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_accessedCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_accessedCheckBoxActionPerformed
|
||||
|
||||
private void createdCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_createdCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_createdCheckBoxActionPerformed
|
||||
|
||||
private void changedCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_changedCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_changedCheckBoxActionPerformed
|
||||
|
||||
/**
|
||||
* Validate and set the datetime field on the screen given a datetime
|
||||
* string.
|
||||
@ -379,6 +429,13 @@ class DateSearchPanel extends javax.swing.JPanel {
|
||||
dateToTextField.setText(dateStringResult);
|
||||
dateToButtonCalendar.setTargetDate(date);
|
||||
}
|
||||
|
||||
boolean isValidSearch() {
|
||||
return this.accessedCheckBox.isSelected() ||
|
||||
this.changedCheckBox.isSelected() ||
|
||||
this.createdCheckBox.isSelected() ||
|
||||
this.modifiedCheckBox.isSelected();
|
||||
}
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox accessedCheckBox;
|
||||
private javax.swing.JCheckBox changedCheckBox;
|
||||
|
@ -19,6 +19,7 @@
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import javax.swing.JComponent;
|
||||
|
||||
/**
|
||||
@ -40,6 +41,13 @@ interface FileSearchFilter {
|
||||
*/
|
||||
boolean isEnabled();
|
||||
|
||||
/**
|
||||
* Checks if the panel has valid input for search.
|
||||
*
|
||||
* @return Whether the panel has valid input for search.
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* Gets predicate expression to include in the SQL filter expression
|
||||
*
|
||||
@ -56,6 +64,13 @@ interface FileSearchFilter {
|
||||
*/
|
||||
void addActionListener(ActionListener l);
|
||||
|
||||
/**
|
||||
* Adds the property change listener to the panel
|
||||
*
|
||||
* @param listener the listener to add.
|
||||
*/
|
||||
void addPropertyChangeListener(PropertyChangeListener listener);
|
||||
|
||||
/**
|
||||
* Thrown if a filter's inputs are invalid
|
||||
*/
|
||||
|
@ -17,38 +17,35 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* FileSearchPanel.java
|
||||
*
|
||||
* Created on Mar 5, 2012, 1:51:50 PM
|
||||
*/
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Component;
|
||||
import java.awt.Cursor;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeEvent;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import javax.swing.BoxLayout;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import org.openide.DialogDisplayer;
|
||||
import org.openide.NotifyDescriptor;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.windows.TopComponent;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
|
||||
import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
@ -64,6 +61,10 @@ class FileSearchPanel extends javax.swing.JPanel {
|
||||
private static int resultWindowCount = 0; //keep track of result windows so they get unique names
|
||||
private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, "FileSearchPanel.emptyWhereClause.text");
|
||||
|
||||
enum EVENT {
|
||||
CHECKED
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates new form FileSearchPanel
|
||||
*/
|
||||
@ -100,25 +101,39 @@ class FileSearchPanel extends javax.swing.JPanel {
|
||||
filterPanel.add(fa);
|
||||
}
|
||||
|
||||
for (FileSearchFilter filter : this.getFilters()) {
|
||||
filter.addPropertyChangeListener(new PropertyChangeListener() {
|
||||
@Override
|
||||
public void propertyChange(PropertyChangeEvent evt) {
|
||||
searchButton.setEnabled(isValidSearch());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addListenerToAll(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
search();
|
||||
}
|
||||
});
|
||||
searchButton.setEnabled(isValidSearch());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if any of the filters in the panel are enabled (checked)
|
||||
*/
|
||||
private boolean anyFiltersEnabled() {
|
||||
private boolean isValidSearch() {
|
||||
boolean enabled = false;
|
||||
for (FileSearchFilter filter : this.getFilters()) {
|
||||
if (filter.isEnabled()) {
|
||||
return true;
|
||||
enabled = true;
|
||||
if (!filter.isValid()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
return enabled;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +144,7 @@ class FileSearchPanel extends javax.swing.JPanel {
|
||||
// change the cursor to "waiting cursor" for this operation
|
||||
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||
try {
|
||||
if (this.anyFiltersEnabled()) {
|
||||
if (this.isValidSearch()) {
|
||||
String title = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.title", ++resultWindowCount);
|
||||
String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText");
|
||||
|
||||
@ -290,6 +305,7 @@ class FileSearchPanel extends javax.swing.JPanel {
|
||||
.addContainerGap())
|
||||
);
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JPanel filterPanel;
|
||||
private javax.swing.JButton searchButton;
|
||||
|
@ -19,7 +19,6 @@
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.datamodel.TskData.FileKnown;
|
||||
|
||||
@ -42,6 +41,7 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter<KnownStatusSearch
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.getComponent().getKnownCheckBox().isSelected();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,4 +83,9 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter<KnownStatusSearch
|
||||
@Override
|
||||
public void addActionListener(ActionListener l) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return this.getComponent().isValidSearch();
|
||||
}
|
||||
}
|
||||
|
@ -64,6 +64,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="KnownStatusSearchPanel.unknownOptionCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unknownOptionCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="knownOptionCheckBox">
|
||||
<Properties>
|
||||
@ -83,6 +86,9 @@
|
||||
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="KnownStatusSearchPanel.knownBadOptionCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, "{key}")"/>
|
||||
</Property>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="knownBadOptionCheckBoxActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Form>
|
||||
|
@ -17,13 +17,15 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* KnownStatusSearchPanel.java
|
||||
*
|
||||
* Created on Oct 19, 2011, 11:45:44 AM
|
||||
*/
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import javax.swing.JCheckBox;
|
||||
|
||||
/**
|
||||
@ -32,6 +34,8 @@ import javax.swing.JCheckBox;
|
||||
*/
|
||||
class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
/**
|
||||
* Creates new form KnownStatusSearchPanel
|
||||
*/
|
||||
@ -55,7 +59,7 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
JCheckBox getUnknownOptionCheckBox() {
|
||||
return unknownOptionCheckBox;
|
||||
}
|
||||
|
||||
|
||||
private void setComponentsEnabled() {
|
||||
boolean enabled = this.knownCheckBox.isSelected();
|
||||
this.unknownOptionCheckBox.setEnabled(enabled);
|
||||
@ -63,6 +67,20 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
this.knownBadOptionCheckBox.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
boolean isValidSearch() {
|
||||
return this.unknownOptionCheckBox.isSelected() || this.knownBadOptionCheckBox.isSelected() || this.knownOptionCheckBox.isSelected();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -86,6 +104,11 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
unknownOptionCheckBox.setSelected(true);
|
||||
unknownOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.unknownOptionCheckBox.text")); // NOI18N
|
||||
unknownOptionCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
unknownOptionCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
knownOptionCheckBox.setSelected(true);
|
||||
knownOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.knownOptionCheckBox.text")); // NOI18N
|
||||
@ -97,6 +120,11 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
knownBadOptionCheckBox.setSelected(true);
|
||||
knownBadOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.knownBadOptionCheckBox.text")); // NOI18N
|
||||
knownBadOptionCheckBox.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
knownBadOptionCheckBoxActionPerformed(evt);
|
||||
}
|
||||
});
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
||||
this.setLayout(layout);
|
||||
@ -127,13 +155,22 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
|
||||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void knownOptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownOptionCheckBoxActionPerformed
|
||||
// TODO add your handling code here:
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_knownOptionCheckBoxActionPerformed
|
||||
|
||||
private void knownCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownCheckBoxActionPerformed
|
||||
setComponentsEnabled();
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_knownCheckBoxActionPerformed
|
||||
|
||||
private void unknownOptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_unknownOptionCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_unknownOptionCheckBoxActionPerformed
|
||||
|
||||
private void knownBadOptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownBadOptionCheckBoxActionPerformed
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_knownBadOptionCheckBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
private javax.swing.JCheckBox knownBadOptionCheckBox;
|
||||
private javax.swing.JCheckBox knownCheckBox;
|
||||
|
@ -10,28 +10,28 @@ import java.awt.event.ActionListener;
|
||||
/**
|
||||
* Filter by mime type used in filter areas of file search by attribute.
|
||||
*/
|
||||
class MimeTypeFilter extends AbstractFileSearchFilter<MimeTypePanel> {
|
||||
class MimeTypeFilter extends AbstractFileSearchFilter<MimeTypePanel> {
|
||||
|
||||
public MimeTypeFilter(MimeTypePanel component) {
|
||||
super(component);
|
||||
}
|
||||
|
||||
public MimeTypeFilter() {
|
||||
this(new MimeTypePanel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return this.getComponent().isSelected() &&
|
||||
!this.getComponent().getMimeTypesSelected().isEmpty();
|
||||
return this.getComponent().isSelected();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPredicate() throws FilterValidationException {
|
||||
String predicate = "";
|
||||
for(String mimeType : this.getComponent().getMimeTypesSelected()) {
|
||||
for (String mimeType : this.getComponent().getMimeTypesSelected()) {
|
||||
predicate += "mime_type = '" + mimeType + "' OR ";
|
||||
}
|
||||
if(predicate.length() > 3) {
|
||||
if (predicate.length() > 3) {
|
||||
predicate = predicate.substring(0, predicate.length() - 3);
|
||||
}
|
||||
return predicate;
|
||||
@ -40,5 +40,9 @@ class MimeTypeFilter extends AbstractFileSearchFilter<MimeTypePanel> {
|
||||
@Override
|
||||
public void addActionListener(ActionListener l) {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return !this.getComponent().getMimeTypesSelected().isEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="jScrollPane1" pref="298" max="32767" attributes="0"/>
|
||||
<Component id="jScrollPane1" pref="0" max="32767" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="jLabel1" min="-2" pref="246" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
|
@ -5,12 +5,16 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.event.ListSelectionEvent;
|
||||
import javax.swing.event.ListSelectionListener;
|
||||
import org.apache.tika.mime.MediaType;
|
||||
import org.apache.tika.mime.MimeTypes;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -25,6 +29,7 @@ public class MimeTypePanel extends javax.swing.JPanel {
|
||||
private static final SortedSet<MediaType> mediaTypes = MimeTypes.getDefaultMimeTypes().getMediaTypeRegistry().getTypes();
|
||||
private static final Logger logger = Logger.getLogger(MimeTypePanel.class.getName());
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
/**
|
||||
* Creates new form MimeTypePanel
|
||||
@ -32,6 +37,12 @@ public class MimeTypePanel extends javax.swing.JPanel {
|
||||
public MimeTypePanel() {
|
||||
initComponents();
|
||||
setComponentsEnabled();
|
||||
this.mimeTypeList.addListSelectionListener(new ListSelectionListener() {
|
||||
@Override
|
||||
public void valueChanged(ListSelectionEvent e) {
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private String[] getMimeTypeArray() {
|
||||
@ -75,6 +86,16 @@ public class MimeTypePanel extends javax.swing.JPanel {
|
||||
this.mimeTypeList.setEnabled(enabled);
|
||||
this.jLabel1.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@ -141,6 +162,8 @@ public class MimeTypePanel extends javax.swing.JPanel {
|
||||
|
||||
private void mimeTypeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mimeTypeCheckBoxActionPerformed
|
||||
setComponentsEnabled();
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
this.mimeTypeList.setSelectedIndices(new int[0]);
|
||||
}//GEN-LAST:event_mimeTypeCheckBoxActionPerformed
|
||||
|
||||
|
||||
|
@ -19,7 +19,6 @@
|
||||
package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
|
||||
|
||||
@ -63,4 +62,9 @@ class NameSearchFilter extends AbstractFileSearchFilter<NameSearchPanel> {
|
||||
public void addActionListener(ActionListener l) {
|
||||
getComponent().addActionListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return !this.getComponent().getSearchTextField().getText().isEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/*
|
||||
/*
|
||||
* NameSearchPanel.java
|
||||
*
|
||||
* Created on Oct 19, 2011, 11:58:53 AM
|
||||
@ -26,9 +26,13 @@ package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JMenuItem;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -36,6 +40,8 @@ import javax.swing.JTextField;
|
||||
*/
|
||||
class NameSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
/**
|
||||
* Creates new form NameSearchPanel
|
||||
*/
|
||||
@ -67,6 +73,22 @@ class NameSearchPanel extends javax.swing.JPanel {
|
||||
copyMenuItem.addActionListener(actList);
|
||||
pasteMenuItem.addActionListener(actList);
|
||||
selectAllMenuItem.addActionListener(actList);
|
||||
this.searchTextField.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -77,12 +99,22 @@ class NameSearchPanel extends javax.swing.JPanel {
|
||||
JTextField getSearchTextField() {
|
||||
return searchTextField;
|
||||
}
|
||||
|
||||
|
||||
void setComponentsEnabled() {
|
||||
boolean enabled = nameCheckBox.isSelected();
|
||||
this.searchTextField.setEnabled(enabled);
|
||||
this.noteNameLabel.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@ -168,6 +200,7 @@ class NameSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private void nameCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameCheckBoxActionPerformed
|
||||
setComponentsEnabled();
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_nameCheckBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionListener;
|
||||
import javax.swing.JComboBox;
|
||||
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
|
||||
|
||||
@ -73,4 +72,9 @@ class SizeSearchFilter extends AbstractFileSearchFilter<SizeSearchPanel> {
|
||||
public void addActionListener(ActionListener l) {
|
||||
getComponent().addActionListener(l);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.filesearch;
|
||||
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.beans.PropertyChangeListener;
|
||||
import java.beans.PropertyChangeSupport;
|
||||
import java.text.NumberFormat;
|
||||
import javax.swing.JCheckBox;
|
||||
import javax.swing.JComboBox;
|
||||
@ -32,6 +34,8 @@ import javax.swing.JMenuItem;
|
||||
*/
|
||||
class SizeSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
|
||||
|
||||
/**
|
||||
* Creates new form SizeSearchPanel
|
||||
*/
|
||||
@ -81,13 +85,23 @@ class SizeSearchPanel extends javax.swing.JPanel {
|
||||
JComboBox<String> getSizeUnitComboBox() {
|
||||
return sizeUnitComboBox;
|
||||
}
|
||||
|
||||
|
||||
void setComponentsEnabled() {
|
||||
boolean enabled = this.sizeCheckBox.isSelected();
|
||||
this.sizeCompareComboBox.setEnabled(enabled);
|
||||
this.sizeUnitComboBox.setEnabled(enabled);
|
||||
this.sizeTextField.setEnabled(enabled);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addPropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.addPropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePropertyChangeListener(PropertyChangeListener pcl) {
|
||||
pcs.removePropertyChangeListener(pcl);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is called from within the constructor to initialize the form.
|
||||
@ -168,6 +182,7 @@ class SizeSearchPanel extends javax.swing.JPanel {
|
||||
|
||||
private void sizeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sizeCheckBoxActionPerformed
|
||||
setComponentsEnabled();
|
||||
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
|
||||
}//GEN-LAST:event_sizeCheckBoxActionPerformed
|
||||
|
||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014 Basis Technology Corp.
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -182,12 +182,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
||||
|
||||
Path tempFilePath = null;
|
||||
try {
|
||||
long id = getRootId(file);
|
||||
// make sure we have a valid systemID
|
||||
if (id == -1) {
|
||||
return IngestModule.ProcessResult.ERROR;
|
||||
}
|
||||
|
||||
// Verify initialization succeeded.
|
||||
if (null == this.executableFile) {
|
||||
logger.log(Level.SEVERE, "PhotoRec carver called after failed start up"); // NON-NLS
|
||||
@ -276,7 +270,7 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
||||
// Now that we've cleaned up the folders and data files, parse the xml output file to add carved items into the database
|
||||
long calcstart = System.currentTimeMillis();
|
||||
PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath);
|
||||
List<LayoutFile> carvedItems = parser.parse(newAuditFile, id, file);
|
||||
List<LayoutFile> carvedItems = parser.parse(newAuditFile, file);
|
||||
long calcdelta = (System.currentTimeMillis() - calcstart);
|
||||
totals.totalParsetime.addAndGet(calcdelta);
|
||||
if (carvedItems != null) { // if there were any results from carving, add the unallocated carving event to the reports list.
|
||||
@ -411,31 +405,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the root Volume or Image of the AbstractFile passed in.
|
||||
*
|
||||
* @param file The file we want to find the root parent for
|
||||
*
|
||||
* @return The ID of the root parent Volume or Image
|
||||
*/
|
||||
private static long getRootId(AbstractFile file) {
|
||||
long id = -1;
|
||||
Content parent = null;
|
||||
try {
|
||||
parent = file.getParent();
|
||||
while (parent != null) {
|
||||
if (parent instanceof Volume || parent instanceof Image) {
|
||||
id = parent.getId();
|
||||
break;
|
||||
}
|
||||
parent = parent.getParent();
|
||||
}
|
||||
} catch (TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "PhotoRec carver exception while trying to get parent of AbstractFile.", ex); //NON-NLS
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds and returns the path to the executable, if able.
|
||||
*
|
||||
|
@ -32,8 +32,8 @@ import org.sleuthkit.autopsy.casemodule.services.FileManager;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.XMLUtil;
|
||||
import org.sleuthkit.datamodel.AbstractFile;
|
||||
import org.sleuthkit.datamodel.CarvingResult;
|
||||
import org.sleuthkit.datamodel.LayoutFile;
|
||||
import org.sleuthkit.datamodel.CarvedFileContainer;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
import org.sleuthkit.datamodel.TskFileRange;
|
||||
import org.w3c.dom.Document;
|
||||
@ -70,7 +70,7 @@ class PhotoRecCarverOutputParser {
|
||||
* @throws FileNotFoundException
|
||||
* @throws IOException
|
||||
*/
|
||||
List<LayoutFile> parse(File xmlInputFile, long id, AbstractFile af) throws FileNotFoundException, IOException {
|
||||
List<LayoutFile> parse(File xmlInputFile, AbstractFile af) throws FileNotFoundException, IOException {
|
||||
try {
|
||||
final Document doc = XMLUtil.loadDoc(PhotoRecCarverOutputParser.class, xmlInputFile.toString());
|
||||
if (doc == null) {
|
||||
@ -99,8 +99,7 @@ class PhotoRecCarverOutputParser {
|
||||
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
|
||||
|
||||
// create and initialize the list to put into the database
|
||||
List<CarvedFileContainer> carvedFileContainer = new ArrayList<>();
|
||||
|
||||
List<CarvingResult.CarvedFile> carvedFiles = new ArrayList<>();
|
||||
for (int fileIndex = 0; fileIndex < numberOfFiles; ++fileIndex) {
|
||||
entry = (Element) fileObjects.item(fileIndex);
|
||||
fileNames = entry.getElementsByTagName("filename"); //NON-NLS
|
||||
@ -133,7 +132,7 @@ class PhotoRecCarverOutputParser {
|
||||
if (fileByteEnd > af.getSize()) {
|
||||
long overshoot = fileByteEnd - af.getSize();
|
||||
if (fileSize > overshoot) {
|
||||
fileSize = fileSize - overshoot;
|
||||
fileSize -= overshoot;
|
||||
} else {
|
||||
// This better never happen... Data for this file is corrupted. Skip it.
|
||||
continue;
|
||||
@ -144,10 +143,10 @@ class PhotoRecCarverOutputParser {
|
||||
}
|
||||
|
||||
if (!tskRanges.isEmpty()) {
|
||||
carvedFileContainer.add(new CarvedFileContainer(fileName, fileSize, id, tskRanges));
|
||||
carvedFiles.add(new CarvingResult.CarvedFile(fileName, fileSize, tskRanges));
|
||||
}
|
||||
}
|
||||
return fileManager.addCarvedFiles(carvedFileContainer);
|
||||
return fileManager.addCarvedFiles(new CarvingResult(af, carvedFiles));
|
||||
} catch (NumberFormatException | TskCoreException ex) {
|
||||
logger.log(Level.SEVERE, "Error parsing PhotoRec output and inserting it into the database: {0}", ex); //NON-NLS
|
||||
}
|
||||
|
@ -18,48 +18,45 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.modules.stix;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JPanel;
|
||||
import javax.xml.bind.JAXBContext;
|
||||
import javax.xml.bind.JAXBException;
|
||||
import javax.xml.bind.Unmarshaller;
|
||||
import javax.xml.namespace.QName;
|
||||
|
||||
import org.mitre.cybox.cybox_2.ObjectType;
|
||||
import org.mitre.cybox.cybox_2.Observable;
|
||||
import org.mitre.cybox.cybox_2.ObservableCompositionType;
|
||||
import org.mitre.cybox.cybox_2.OperatorTypeEnum;
|
||||
import org.mitre.cybox.objects.AccountObjectType;
|
||||
import org.mitre.cybox.objects.Address;
|
||||
import org.mitre.cybox.objects.DomainName;
|
||||
import org.mitre.cybox.objects.EmailMessage;
|
||||
import org.mitre.cybox.objects.FileObjectType;
|
||||
import org.mitre.cybox.objects.SystemObjectType;
|
||||
import org.mitre.cybox.objects.URIObjectType;
|
||||
import org.mitre.cybox.objects.URLHistory;
|
||||
import org.mitre.cybox.objects.WindowsNetworkShare;
|
||||
import org.mitre.cybox.objects.WindowsRegistryKey;
|
||||
import org.mitre.stix.common_1.IndicatorBaseType;
|
||||
import org.mitre.stix.indicator_2.Indicator;
|
||||
import org.mitre.stix.stix_1.STIXPackage;
|
||||
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
import org.mitre.cybox.cybox_2.OperatorTypeEnum;
|
||||
import org.mitre.cybox.objects.Address;
|
||||
import org.mitre.cybox.objects.FileObjectType;
|
||||
import org.mitre.cybox.objects.URIObjectType;
|
||||
import org.mitre.cybox.objects.EmailMessage;
|
||||
import org.mitre.cybox.objects.WindowsNetworkShare;
|
||||
import org.mitre.cybox.objects.AccountObjectType;
|
||||
import org.mitre.cybox.objects.SystemObjectType;
|
||||
import org.mitre.cybox.objects.URLHistory;
|
||||
import org.mitre.cybox.objects.DomainName;
|
||||
import org.mitre.cybox.objects.WindowsRegistryKey;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
|
||||
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
|
||||
import org.sleuthkit.autopsy.report.GeneralReportModule;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel;
|
||||
import org.sleuthkit.autopsy.report.ReportProgressPanel.ReportStatus;
|
||||
import org.sleuthkit.datamodel.TskCoreException;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -180,6 +177,9 @@ public class STIXReportModule implements GeneralReportModule {
|
||||
|
||||
// Process each STIX file
|
||||
for (File file : stixFiles) {
|
||||
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
processFile(file.getAbsolutePath(), progressPanel);
|
||||
} catch (TskCoreException ex) {
|
||||
|
0
Core/src/org/sleuthkit/autopsy/report/ReportKML.java
Normal file → Executable file
@ -24,9 +24,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import static java.util.Collections.swap;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.JList;
|
||||
import javax.swing.JPanel;
|
||||
@ -137,8 +135,8 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
|
||||
// Make sure that the report module has a valid non-null name.
|
||||
private boolean moduleIsValid(ReportModule module) {
|
||||
return module.getName() != null && !module.getName().isEmpty()
|
||||
&& module.getRelativeFilePath() != null;
|
||||
return module.getName() != null && !module.getName().isEmpty()
|
||||
&& module.getRelativeFilePath() != null;
|
||||
}
|
||||
|
||||
private void popupWarning(ReportModule module) {
|
||||
@ -163,13 +161,12 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<TableReportModule, Boolean> getTableModuleStates() {
|
||||
Map<TableReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
|
||||
TableReportModule getTableModule() {
|
||||
ReportModule mod = getSelectedModule();
|
||||
if (tableModules.contains(mod)) {
|
||||
reportModuleStates.put((TableReportModule) mod, Boolean.TRUE);
|
||||
return (TableReportModule) mod;
|
||||
}
|
||||
return reportModuleStates;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -177,13 +174,12 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<GeneralReportModule, Boolean> getGeneralModuleStates() {
|
||||
Map<GeneralReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
|
||||
GeneralReportModule getGeneralModule() {
|
||||
ReportModule mod = getSelectedModule();
|
||||
if (generalModules.contains(mod)) {
|
||||
reportModuleStates.put((GeneralReportModule) mod, Boolean.TRUE);
|
||||
return (GeneralReportModule) mod;
|
||||
}
|
||||
return reportModuleStates;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -191,13 +187,12 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
Map<FileReportModule, Boolean> getFileModuleStates() {
|
||||
Map<FileReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
|
||||
FileReportModule getFileModule() {
|
||||
ReportModule mod = getSelectedModule();
|
||||
if (fileModules.contains(mod)) {
|
||||
reportModuleStates.put((FileReportModule) mod, Boolean.TRUE);
|
||||
return (FileReportModule) mod;
|
||||
}
|
||||
return reportModuleStates;
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,4 +1,4 @@
|
||||
/*
|
||||
/*
|
||||
*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
@ -65,13 +65,17 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr
|
||||
wiz.setTitle(NbBundle.getMessage(ReportWizardAction.class, "ReportWizardAction.reportWiz.title"));
|
||||
if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) {
|
||||
@SuppressWarnings("unchecked")
|
||||
ReportGenerator generator = new ReportGenerator((Map<TableReportModule, Boolean>) wiz.getProperty("tableModuleStates"), //NON-NLS
|
||||
(Map<GeneralReportModule, Boolean>) wiz.getProperty("generalModuleStates"), //NON-NLS
|
||||
(Map<FileReportModule, Boolean>) wiz.getProperty("fileModuleStates")); //NON-NLS
|
||||
generator.generateTableReports((Map<BlackboardArtifact.Type, Boolean>) wiz.getProperty("artifactStates"), (Map<String, Boolean>) wiz.getProperty("tagStates")); //NON-NLS
|
||||
generator.generateFileListReports((Map<FileReportDataTypes, Boolean>) wiz.getProperty("fileReportOptions")); //NON-NLS
|
||||
generator.generateGeneralReports();
|
||||
generator.displayProgressPanels();
|
||||
ReportGenerator generator = new ReportGenerator(); //NON-NLS
|
||||
TableReportModule tableReport = (TableReportModule) wiz.getProperty("tableModule");
|
||||
GeneralReportModule generalReport = (GeneralReportModule) wiz.getProperty("generalModule");
|
||||
FileReportModule fileReport = (FileReportModule) wiz.getProperty("fileModule");
|
||||
if (tableReport != null) {
|
||||
generator.generateTableReport(tableReport, (Map<BlackboardArtifact.Type, Boolean>) wiz.getProperty("artifactStates"), (Map<String, Boolean>) wiz.getProperty("tagStates")); //NON-NLS
|
||||
} else if (generalReport != null) {
|
||||
generator.generateGeneralReport(generalReport);
|
||||
} else if (fileReport != null) {
|
||||
generator.generateFileListReport(fileReport, (Map<FileReportDataTypes, Boolean>) wiz.getProperty("fileReportOptions")); //NON-NLS
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.report;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.prefs.Preferences;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.event.ChangeListener;
|
||||
@ -108,17 +107,17 @@ class ReportWizardPanel1 implements WizardDescriptor.FinishablePanel<WizardDescr
|
||||
|
||||
@Override
|
||||
public void storeSettings(WizardDescriptor wiz) {
|
||||
Map<TableReportModule, Boolean> tables = getComponent().getTableModuleStates();
|
||||
Map<GeneralReportModule, Boolean> generals = getComponent().getGeneralModuleStates();
|
||||
wiz.putProperty("tableModuleStates", tables); //NON-NLS
|
||||
wiz.putProperty("generalModuleStates", generals); //NON-NLS
|
||||
wiz.putProperty("fileModuleStates", getComponent().getFileModuleStates()); //NON-NLS
|
||||
TableReportModule module = getComponent().getTableModule();
|
||||
GeneralReportModule general = getComponent().getGeneralModule();
|
||||
wiz.putProperty("tableModule", module); //NON-NLS
|
||||
wiz.putProperty("generalModule", general); //NON-NLS
|
||||
wiz.putProperty("fileModule", getComponent().getFileModule()); //NON-NLS
|
||||
|
||||
// Store preferences that WizardIterator will use to determine what
|
||||
// panels need to be shown
|
||||
Preferences prefs = NbPreferences.forModule(ReportWizardPanel1.class);
|
||||
prefs.putBoolean("tableModule", any(tables.values())); //NON-NLS
|
||||
prefs.putBoolean("generalModule", any(generals.values())); //NON-NLS
|
||||
prefs.putBoolean("tableModule", module != null); //NON-NLS
|
||||
prefs.putBoolean("generalModule", general != null); //NON-NLS
|
||||
}
|
||||
|
||||
/**
|
||||
|
1680
Core/src/org/sleuthkit/autopsy/report/TableReportGenerator.java
Executable file
@ -18,8 +18,13 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline;
|
||||
|
||||
import java.awt.Component;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import org.openide.awt.ActionID;
|
||||
import org.openide.awt.ActionReference;
|
||||
import org.openide.awt.ActionReferences;
|
||||
@ -27,6 +32,7 @@ import org.openide.awt.ActionRegistration;
|
||||
import org.openide.util.HelpCtx;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.CallableSystemAction;
|
||||
import org.openide.util.actions.Presenter;
|
||||
import org.sleuthkit.autopsy.casemodule.Case;
|
||||
import org.sleuthkit.autopsy.core.Installer;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
@ -36,8 +42,9 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
|
||||
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
|
||||
@ActionReferences(value = {
|
||||
@ActionReference(path = "Menu/Tools", position = 100)})
|
||||
public class OpenTimelineAction extends CallableSystemAction {
|
||||
@ActionReference(path = "Menu/Tools", position = 100),
|
||||
@ActionReference(path = "Toolbars/Case", position = 102)})
|
||||
public class OpenTimelineAction extends CallableSystemAction implements Presenter.Toolbar {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OpenTimelineAction.class.getName());
|
||||
|
||||
@ -45,10 +52,22 @@ public class OpenTimelineAction extends CallableSystemAction {
|
||||
|
||||
private static TimeLineController timeLineController = null;
|
||||
|
||||
private JButton toolbarButton = new JButton();
|
||||
|
||||
synchronized static void invalidateController() {
|
||||
timeLineController = null;
|
||||
}
|
||||
|
||||
public OpenTimelineAction() {
|
||||
toolbarButton.addActionListener(new ActionListener() {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
performAction();
|
||||
}
|
||||
});
|
||||
this.setEnabled(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
/**
|
||||
@ -102,4 +121,29 @@ public class OpenTimelineAction extends CallableSystemAction {
|
||||
public boolean asynchronous() {
|
||||
return false; // run on edt
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this action to be enabled/disabled
|
||||
*
|
||||
* @param value whether to enable this action or not
|
||||
*/
|
||||
@Override
|
||||
public void setEnabled(boolean value) {
|
||||
super.setEnabled(value);
|
||||
toolbarButton.setEnabled(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the toolbar component of this action
|
||||
*
|
||||
* @return component the toolbar button
|
||||
*/
|
||||
@Override
|
||||
public Component getToolbarPresenter() {
|
||||
ImageIcon icon = new ImageIcon("Core/src/org/sleuthkit/autopsy/timeline/images/btn_icon_timeline_colorized_26.png"); //NON-NLS
|
||||
toolbarButton.setIcon(icon);
|
||||
toolbarButton.setText(this.getName());
|
||||
|
||||
return toolbarButton;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013 Basis Technology Corp.
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -19,10 +19,9 @@
|
||||
package org.sleuthkit.autopsy.timeline;
|
||||
|
||||
/**
|
||||
*
|
||||
* Enumeration of view modes.
|
||||
*/
|
||||
public enum ViewMode {
|
||||
|
||||
COUNTS,
|
||||
DETAIL,
|
||||
LIST;
|
||||
|
@ -597,7 +597,7 @@ public class EventsRepository {
|
||||
timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime());
|
||||
|
||||
/*
|
||||
* if there are no legitimate ( greater tan zero ) time stamps ( eg,
|
||||
* if there are no legitimate ( greater than zero ) time stamps ( eg,
|
||||
* logical/local files) skip the rest of the event generation: this
|
||||
* should result in droping logical files, since they do not have
|
||||
* legitimate time stamps.
|
||||
|
Before Width: | Height: | Size: 959 B After Width: | Height: | Size: 959 B |
After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 522 B |
After Width: | Height: | Size: 524 B |
After Width: | Height: | Size: 395 B |
After Width: | Height: | Size: 389 B |
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2016 Basis Technology Corp.
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,10 +18,8 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.logging.Level;
|
||||
import javafx.application.Platform;
|
||||
@ -40,9 +38,14 @@ import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.ViewMode;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
|
||||
|
||||
/**
|
||||
* Base class for views that can be hosted in the ViewFrame
|
||||
*
|
||||
*/
|
||||
public abstract class AbstractTimeLineView extends BorderPane {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(AbstractTimeLineView.class.getName());
|
||||
@ -60,12 +63,6 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
||||
*/
|
||||
private final ReadOnlyBooleanWrapper outOfDate = new ReadOnlyBooleanWrapper(false);
|
||||
|
||||
/**
|
||||
* List of Nodes to insert into the toolbar. This should be set in an
|
||||
* implementation's constructor.
|
||||
*/
|
||||
private List<Node> settingsNodes;
|
||||
|
||||
/**
|
||||
* Listener that is attached to various properties that should trigger a
|
||||
* view update when they change.
|
||||
@ -136,8 +133,8 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
||||
|
||||
/**
|
||||
* Refresh this view based on current state of zoom / filters. Primarily
|
||||
* this invokes the background ViewRefreshTask returned by
|
||||
* getUpdateTask(), which derived classes must implement.
|
||||
* this invokes the background ViewRefreshTask returned by getUpdateTask(),
|
||||
* which derived classes must implement.
|
||||
*
|
||||
* TODO: replace this logic with a javafx Service ? -jm
|
||||
*/
|
||||
@ -186,26 +183,35 @@ public abstract class AbstractTimeLineView extends BorderPane {
|
||||
protected abstract Task<Boolean> getNewUpdateTask();
|
||||
|
||||
/**
|
||||
* Get a List of Nodes containing settings widgets to insert into this
|
||||
* view's header.
|
||||
* Get the ViewMode for this view.
|
||||
*
|
||||
* @return The ViewMode for this view.
|
||||
*/
|
||||
protected abstract ViewMode getViewMode();
|
||||
|
||||
/**
|
||||
* Get a List of Nodes containing settings widgets to insert into top
|
||||
* ToolBar of the ViewFrame.
|
||||
*
|
||||
* @return The List of settings Nodes.
|
||||
*/
|
||||
protected List<Node> getSettingsNodes() {
|
||||
return Collections.unmodifiableList(settingsNodes);
|
||||
}
|
||||
abstract protected ImmutableList<Node> getSettingsControls();
|
||||
|
||||
/**
|
||||
* Set the List of Nodes containing settings widgets to insert into this
|
||||
* view's header.
|
||||
* Does this view have custom time navigation controls that should replace
|
||||
* the default ones from the ViewFrame?
|
||||
*
|
||||
*
|
||||
* @param settingsNodes The List of Nodes containing settings widgets to
|
||||
* insert into this view's header.
|
||||
* @return True if this view have custom time navigation controls.
|
||||
*/
|
||||
final protected void setSettingsNodes(List<Node> settingsNodes) {
|
||||
this.settingsNodes = new ArrayList<>(settingsNodes);
|
||||
}
|
||||
abstract protected boolean hasCustomTimeNavigationControls();
|
||||
|
||||
/**
|
||||
* Get a List of Nodes containing controls to insert into the lower time
|
||||
* range ToolBar of the ViewFrame.
|
||||
*
|
||||
* @return The List of Nodes.
|
||||
*/
|
||||
abstract protected ImmutableList<Node> getTimeNavigationControls();
|
||||
|
||||
/**
|
||||
* Dispose of this view and any resources it holds onto.
|
||||
|
@ -35,12 +35,6 @@ Timeline.ui.ZoomRanges.threeyears.text=Three Years
|
||||
Timeline.ui.ZoomRanges.fiveyears.text=Five Years
|
||||
Timeline.ui.ZoomRanges.tenyears.text=Ten Years
|
||||
Timeline.ui.ZoomRanges.all.text=All
|
||||
ViewFrame.histogramTask.title=Rebuild Histogram
|
||||
ViewFrame.histogramTask.preparing=preparing
|
||||
ViewFrame.histogramTask.resetUI=resetting ui
|
||||
ViewFrame.histogramTask.queryDb=querying db
|
||||
ViewFrame.histogramTask.updateUI2=updating ui
|
||||
ViewFrame.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.
|
||||
ViewFrame.zoomButton.text=Zoom to events
|
||||
TimeZonePanel.localRadio.text=Local Time Zone
|
||||
TimeZonePanel.otherRadio.text=GMT / UTC
|
||||
|
@ -125,7 +125,7 @@
|
||||
</children>
|
||||
</StackPane>
|
||||
<Separator />
|
||||
<ToolBar>
|
||||
<ToolBar fx:id="timeRangeToolBar">
|
||||
<items>
|
||||
<Label fx:id="startLabel" contentDisplay="RIGHT" minWidth="-Infinity">
|
||||
<graphic>
|
||||
@ -141,7 +141,7 @@
|
||||
<Insets left="10.0" right="10.0" />
|
||||
</HBox.margin>
|
||||
</Separator>
|
||||
<HBox>
|
||||
<HBox fx:id="zoomInOutHBox">
|
||||
<children>
|
||||
<Button fx:id="zoomOutButton" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false">
|
||||
<graphic>
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2013-16 Basis Technology Corp.
|
||||
* Copyright 2011-16 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,19 +18,24 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.eventbus.Subscribe;
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.InvalidationListener;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Insets;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.MenuButton;
|
||||
@ -69,6 +74,7 @@ import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.casemodule.events.DataSourceAddedEvent;
|
||||
import org.sleuthkit.autopsy.coreutils.LoggedTask;
|
||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
|
||||
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
@ -84,6 +90,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.events.DBUpdatedEvent;
|
||||
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
|
||||
import org.sleuthkit.autopsy.timeline.events.TagsUpdatedEvent;
|
||||
import static org.sleuthkit.autopsy.timeline.ui.Bundle.*;
|
||||
import org.sleuthkit.autopsy.timeline.ui.countsview.CountsViewPane;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane;
|
||||
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree;
|
||||
@ -93,7 +100,8 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
|
||||
/**
|
||||
* A container for an AbstractTimelineView. Has a Toolbar on top to hold
|
||||
* settings widgets supplied by contained AbstractTimelineView, and the
|
||||
* histogram / time selection on bottom.
|
||||
* histogram / time selection on bottom. The time selection Toolbar has default
|
||||
* controls that can be replaced by ones supplied by the current view.
|
||||
*
|
||||
* TODO: Refactor common code out of histogram and CountsView? -jm
|
||||
*/
|
||||
@ -101,14 +109,15 @@ final public class ViewFrame extends BorderPane {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ViewFrame.class.getName());
|
||||
|
||||
private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); // NON-NLS
|
||||
private static final Image WARNING = new Image("org/sleuthkit/autopsy/timeline/images/warning_triangle.png", 16, 16, true, true); // NON-NLS
|
||||
private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); // NON-NLS
|
||||
private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); //NON-NLS
|
||||
private static final Image WARNING = new Image("org/sleuthkit/autopsy/timeline/images/warning_triangle.png", 16, 16, true, true); //NON-NLS
|
||||
private static final Image REFRESH = new Image("org/sleuthkit/autopsy/timeline/images/arrow-circle-double-135.png"); //NON-NLS
|
||||
private static final Background GRAY_BACKGROUND = new Background(new BackgroundFill(Color.GREY, CornerRadii.EMPTY, Insets.EMPTY));
|
||||
|
||||
/**
|
||||
* Region that will be stacked in between the no-events "dialog" and the
|
||||
* hosted AbstractTimelineView in order to gray out the AbstractTimelineView.
|
||||
* hosted AbstractTimelineView in order to gray out the
|
||||
* AbstractTimelineView.
|
||||
*/
|
||||
private final static Region NO_EVENTS_BACKGROUND = new Region() {
|
||||
{
|
||||
@ -117,6 +126,18 @@ final public class ViewFrame extends BorderPane {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* The scene graph Nodes for the current view's settings will be inserted
|
||||
* into the toolbar at this index.
|
||||
*/
|
||||
private static final int SETTINGS_TOOLBAR_INSERTION_INDEX = 2;
|
||||
|
||||
/**
|
||||
* The scene graph Nodes for the current view's time navigation controls
|
||||
* will be inserted into the toolbar at this index.
|
||||
*/
|
||||
private static final int TIME_TOOLBAR_INSERTION_INDEX = 2;
|
||||
|
||||
@GuardedBy("this")
|
||||
private LoggedTask<Void> histogramTask;
|
||||
|
||||
@ -139,6 +160,19 @@ final public class ViewFrame extends BorderPane {
|
||||
|
||||
private final RangeSlider rangeSlider = new RangeSlider(0, 1.0, .25, .75);
|
||||
|
||||
/**
|
||||
* The lower tool bar that has controls to adjust the viewed timerange.
|
||||
*/
|
||||
@FXML
|
||||
private ToolBar timeRangeToolBar;
|
||||
|
||||
/**
|
||||
* Parent for the default zoom in/out buttons that can be replaced in some
|
||||
* views(eg List View)
|
||||
*/
|
||||
@FXML
|
||||
private HBox zoomInOutHBox;
|
||||
|
||||
//// time range selection components
|
||||
@FXML
|
||||
private MenuButton zoomMenuButton;
|
||||
@ -158,6 +192,8 @@ final public class ViewFrame extends BorderPane {
|
||||
//// header toolbar componenets
|
||||
@FXML
|
||||
private ToolBar toolBar;
|
||||
|
||||
private ToggleGroupValue<ViewMode> viewModeToggleGroup;
|
||||
@FXML
|
||||
private Label viewModeLabel;
|
||||
@FXML
|
||||
@ -168,6 +204,7 @@ final public class ViewFrame extends BorderPane {
|
||||
private ToggleButton detailsToggle;
|
||||
@FXML
|
||||
private ToggleButton listToggle;
|
||||
|
||||
@FXML
|
||||
private Button snapShotButton;
|
||||
@FXML
|
||||
@ -176,7 +213,27 @@ final public class ViewFrame extends BorderPane {
|
||||
private Button updateDBButton;
|
||||
|
||||
/*
|
||||
* Wraps contained AbstractTimelineView so that we can show notifications over it.
|
||||
* Default zoom in/out buttons provided by the ViewFrame, some views replace
|
||||
* these with other nodes (eg, list view)
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private ImmutableList<Node> defaultTimeNavigationNodes;
|
||||
|
||||
/*
|
||||
* The settings nodes for the current view.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private final ObservableList<Node> settingsNodes = FXCollections.observableArrayList();
|
||||
|
||||
/*
|
||||
* The time nagivation nodes for the current view.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private final ObservableList<Node> timeNavigationNodes = FXCollections.observableArrayList();
|
||||
|
||||
/**
|
||||
* Wraps the contained AbstractTimelineView so that we can show
|
||||
* notifications over it.
|
||||
*/
|
||||
private final NotificationPane notificationPane = new NotificationPane();
|
||||
|
||||
@ -252,7 +309,8 @@ final public class ViewFrame extends BorderPane {
|
||||
this.controller = controller;
|
||||
this.filteredEvents = controller.getEventsModel();
|
||||
this.eventsTree = eventsTree;
|
||||
FXMLConstructor.construct(this, "ViewFrame.fxml"); // NON-NLS
|
||||
FXMLConstructor.construct(this, "ViewFrame.fxml"); //NON-NLS
|
||||
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -267,12 +325,15 @@ final public class ViewFrame extends BorderPane {
|
||||
"ViewFrame.tagsAddedOrDeleted=Tags have been created and/or deleted. The view may not be up to date."
|
||||
})
|
||||
void initialize() {
|
||||
assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
|
||||
assert histogramBox != null : "fx:id=\"histogramBox\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
|
||||
assert startPicker != null : "fx:id=\"startPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
|
||||
assert rangeHistogramStack != null : "fx:id=\"rangeHistogramStack\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
|
||||
assert countsToggle != null : "fx:id=\"countsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; // NON-NLS
|
||||
assert detailsToggle != null : "fx:id=\"eventsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; // NON-NLS
|
||||
assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
|
||||
assert histogramBox != null : "fx:id=\"histogramBox\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
|
||||
assert startPicker != null : "fx:id=\"startPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
|
||||
assert rangeHistogramStack != null : "fx:id=\"rangeHistogramStack\" was not injected: check your FXML file 'ViewWrapper.fxml'."; //NON-NLS
|
||||
assert countsToggle != null : "fx:id=\"countsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; //NON-NLS
|
||||
assert detailsToggle != null : "fx:id=\"eventsToggle\" was not injected: check your FXML file 'VisToggle.fxml'."; //NON-NLS
|
||||
|
||||
defaultTimeNavigationNodes = ImmutableList.of(zoomInOutHBox, zoomMenuButton);
|
||||
timeNavigationNodes.setAll(defaultTimeNavigationNodes);
|
||||
|
||||
//configure notification pane
|
||||
notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
|
||||
@ -283,17 +344,14 @@ final public class ViewFrame extends BorderPane {
|
||||
countsToggle.setText(Bundle.ViewFrame_countsToggle_text());
|
||||
detailsToggle.setText(Bundle.ViewFrame_detailsToggle_text());
|
||||
listToggle.setText(Bundle.ViewFrame_listToggle_text());
|
||||
|
||||
ToggleGroupValue<ViewMode> visModeToggleGroup = new ToggleGroupValue<>();
|
||||
visModeToggleGroup.add(listToggle, ViewMode.LIST);
|
||||
visModeToggleGroup.add(detailsToggle, ViewMode.DETAIL);
|
||||
visModeToggleGroup.add(countsToggle, ViewMode.COUNTS);
|
||||
|
||||
modeSegButton.setToggleGroup(visModeToggleGroup);
|
||||
|
||||
visModeToggleGroup.valueProperty().addListener((observable, oldVisMode, newValue) -> {
|
||||
controller.setViewMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : ViewMode.COUNTS));
|
||||
});
|
||||
viewModeToggleGroup = new ToggleGroupValue<>();
|
||||
viewModeToggleGroup.add(listToggle, ViewMode.LIST);
|
||||
viewModeToggleGroup.add(detailsToggle, ViewMode.DETAIL);
|
||||
viewModeToggleGroup.add(countsToggle, ViewMode.COUNTS);
|
||||
modeSegButton.setToggleGroup(viewModeToggleGroup);
|
||||
viewModeToggleGroup.valueProperty().addListener((observable, oldViewMode, newViewVode) ->
|
||||
controller.setViewMode(newViewVode != null ? newViewVode : (oldViewMode != null ? oldViewMode : ViewMode.COUNTS))
|
||||
);
|
||||
|
||||
controller.viewModeProperty().addListener(viewMode -> syncViewMode());
|
||||
syncViewMode();
|
||||
@ -330,7 +388,7 @@ final public class ViewFrame extends BorderPane {
|
||||
* rangeslider track doesn't extend to edge of node,and so the
|
||||
* histrogram doesn't quite line up with the rangeslider
|
||||
*/
|
||||
histogramBox.setStyle(" -fx-padding: 0,0.5em,0,.5em; "); // NON-NLS
|
||||
histogramBox.setStyle(" -fx-padding: 0,0.5em,0,.5em; "); //NON-NLS
|
||||
|
||||
//configure zoom buttons
|
||||
zoomMenuButton.getItems().clear();
|
||||
@ -362,11 +420,10 @@ final public class ViewFrame extends BorderPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle TagsUpdatedEvents by marking that the view needs to be
|
||||
* refreshed.
|
||||
* Handle TagsUpdatedEvents by marking that the view needs to be refreshed.
|
||||
*
|
||||
* NOTE: This ViewFrame must be registered with the
|
||||
* filteredEventsModel's EventBus in order for this handler to be invoked.
|
||||
* NOTE: This ViewFrame must be registered with the filteredEventsModel's
|
||||
* EventBus in order for this handler to be invoked.
|
||||
*
|
||||
* @param event The TagsUpdatedEvent to handle.
|
||||
*/
|
||||
@ -385,8 +442,8 @@ final public class ViewFrame extends BorderPane {
|
||||
* Handle a RefreshRequestedEvent from the events model by clearing the
|
||||
* refresh notification.
|
||||
*
|
||||
* NOTE: This ViewFrame must be registered with the
|
||||
* filteredEventsModel's EventBus in order for this handler to be invoked.
|
||||
* NOTE: This ViewFrame must be registered with the filteredEventsModel's
|
||||
* EventBus in order for this handler to be invoked.
|
||||
*
|
||||
* @param event The RefreshRequestedEvent to handle.
|
||||
*/
|
||||
@ -400,11 +457,10 @@ final public class ViewFrame extends BorderPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a DBUpdatedEvent from the events model by refreshing the
|
||||
* view.
|
||||
* Handle a DBUpdatedEvent from the events model by refreshing the view.
|
||||
*
|
||||
* NOTE: This ViewFrame must be registered with the
|
||||
* filteredEventsModel's EventBus in order for this handler to be invoked.
|
||||
* NOTE: This ViewFrame must be registered with the filteredEventsModel's
|
||||
* EventBus in order for this handler to be invoked.
|
||||
*
|
||||
* @param event The DBUpdatedEvent to handle.
|
||||
*/
|
||||
@ -419,8 +475,8 @@ final public class ViewFrame extends BorderPane {
|
||||
* Handle a DataSourceAddedEvent from the events model by showing a
|
||||
* notification.
|
||||
*
|
||||
* NOTE: This ViewFrame must be registered with the
|
||||
* filteredEventsModel's EventBus in order for this handler to be invoked.
|
||||
* NOTE: This ViewFrame must be registered with the filteredEventsModel's
|
||||
* EventBus in order for this handler to be invoked.
|
||||
*
|
||||
* @param event The DataSourceAddedEvent to handle.
|
||||
*/
|
||||
@ -439,8 +495,8 @@ final public class ViewFrame extends BorderPane {
|
||||
* Handle a DataSourceAnalysisCompletedEvent from the events modelby showing
|
||||
* a notification.
|
||||
*
|
||||
* NOTE: This ViewFrame must be registered with the
|
||||
* filteredEventsModel's EventBus in order for this handler to be invoked.
|
||||
* NOTE: This ViewFrame must be registered with the filteredEventsModel's
|
||||
* EventBus in order for this handler to be invoked.
|
||||
*
|
||||
* @param event The DataSourceAnalysisCompletedEvent to handle.
|
||||
*/
|
||||
@ -458,19 +514,23 @@ final public class ViewFrame extends BorderPane {
|
||||
/**
|
||||
* Refresh the Histogram to represent the current state of the DB.
|
||||
*/
|
||||
@NbBundle.Messages({"ViewFrame.histogramTask.title=Rebuilding Histogram",
|
||||
"ViewFrame.histogramTask.preparing=Preparing",
|
||||
"ViewFrame.histogramTask.resetUI=Resetting UI",
|
||||
"ViewFrame.histogramTask.queryDb=Querying FB",
|
||||
"ViewFrame.histogramTask.updateUI2=Updating UI"})
|
||||
synchronized private void refreshHistorgram() {
|
||||
if (histogramTask != null) {
|
||||
histogramTask.cancel(true);
|
||||
}
|
||||
|
||||
histogramTask = new LoggedTask<Void>(
|
||||
NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.title"), true) { // NON-NLS
|
||||
histogramTask = new LoggedTask<Void>(Bundle.ViewFrame_histogramTask_title(), true) {
|
||||
private final Lighting lighting = new Lighting();
|
||||
|
||||
@Override
|
||||
protected Void call() throws Exception {
|
||||
|
||||
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.preparing")); // NON-NLS
|
||||
updateMessage(ViewFrame_histogramTask_preparing());
|
||||
|
||||
long max = 0;
|
||||
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval());
|
||||
@ -483,7 +543,7 @@ final public class ViewFrame extends BorderPane {
|
||||
|
||||
//clear old data, and reset ranges and series
|
||||
Platform.runLater(() -> {
|
||||
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.resetUI")); // NON-NLS
|
||||
updateMessage(ViewFrame_histogramTask_resetUI());
|
||||
|
||||
});
|
||||
|
||||
@ -500,7 +560,7 @@ final public class ViewFrame extends BorderPane {
|
||||
|
||||
start = end;
|
||||
|
||||
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.queryDb")); // NON-NLS
|
||||
updateMessage(ViewFrame_histogramTask_queryDb());
|
||||
//query for current range
|
||||
long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
|
||||
bins.add(count);
|
||||
@ -510,7 +570,7 @@ final public class ViewFrame extends BorderPane {
|
||||
final double fMax = Math.log(max);
|
||||
final ArrayList<Long> fbins = new ArrayList<>(bins);
|
||||
Platform.runLater(() -> {
|
||||
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.updateUI2")); // NON-NLS
|
||||
updateMessage(ViewFrame_histogramTask_updateUI2());
|
||||
|
||||
histogramBox.getChildren().clear();
|
||||
|
||||
@ -543,7 +603,7 @@ final public class ViewFrame extends BorderPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the time selection UI to match the current zoome paramaters.
|
||||
* Refresh the time selection UI to match the current zoom parameters.
|
||||
*/
|
||||
private void refreshTimeUI() {
|
||||
RangeDivisionInfo rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval());
|
||||
@ -576,57 +636,52 @@ final public class ViewFrame extends BorderPane {
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to the given ViewMode, by swapping out the hosted
|
||||
* AbstractTimelineView for one of the correct type.
|
||||
* Sync up the view shown in the UI to the one currently active according to
|
||||
* the controller. Swaps out the hosted AbstractTimelineView for a new one
|
||||
* of the correct type.
|
||||
*/
|
||||
private void syncViewMode() {
|
||||
AbstractTimeLineView view;
|
||||
ViewMode viewMode = controller.viewModeProperty().get();
|
||||
ViewMode newViewMode = controller.getViewMode();
|
||||
|
||||
//make new view.
|
||||
switch (viewMode) {
|
||||
case LIST:
|
||||
view = new ListViewPane(controller);
|
||||
Platform.runLater(() -> {
|
||||
listToggle.setSelected(true);
|
||||
//TODO: should remove listeners from events tree
|
||||
});
|
||||
break;
|
||||
case COUNTS:
|
||||
view = new CountsViewPane(controller);
|
||||
Platform.runLater(() -> {
|
||||
countsToggle.setSelected(true);
|
||||
//TODO: should remove listeners from events tree
|
||||
});
|
||||
break;
|
||||
case DETAIL:
|
||||
DetailViewPane detailViewPane = new DetailViewPane(controller);
|
||||
Platform.runLater(() -> {
|
||||
detailsToggle.setSelected(true);
|
||||
detailViewPane.setHighLightedEvents(eventsTree.getSelectedEvents());
|
||||
eventsTree.setDetailViewPane(detailViewPane);
|
||||
});
|
||||
view = detailViewPane;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown ViewMode: " + viewMode.toString());
|
||||
}
|
||||
|
||||
//Set the new AbstractTimeLineView as the one hosted by this ViewFrame.
|
||||
Platform.runLater(() -> {
|
||||
//clear out old view.
|
||||
if (hostedView != null) {
|
||||
toolBar.getItems().removeAll(hostedView.getSettingsNodes());
|
||||
hostedView.dispose();
|
||||
}
|
||||
|
||||
hostedView = view;
|
||||
//setup new view.
|
||||
//Set a new AbstractTimeLineView as the one hosted by this ViewFrame.
|
||||
switch (newViewMode) {
|
||||
case LIST:
|
||||
hostedView = new ListViewPane(controller);
|
||||
//TODO: should remove listeners from events tree
|
||||
break;
|
||||
case COUNTS:
|
||||
hostedView = new CountsViewPane(controller);
|
||||
//TODO: should remove listeners from events tree
|
||||
break;
|
||||
case DETAIL:
|
||||
DetailViewPane detailViewPane = new DetailViewPane(controller);
|
||||
//link events tree to detailview instance.
|
||||
detailViewPane.setHighLightedEvents(eventsTree.getSelectedEvents());
|
||||
eventsTree.setDetailViewPane(detailViewPane);
|
||||
hostedView = detailViewPane;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown ViewMode: " + newViewMode.toString());//NON-NLS
|
||||
}
|
||||
|
||||
viewModeToggleGroup.setValue(newViewMode); //this selects the right toggle automatically
|
||||
|
||||
//configure settings and time navigation nodes
|
||||
setViewSettingsControls(hostedView.getSettingsControls());
|
||||
setTimeNavigationControls(hostedView.hasCustomTimeNavigationControls()
|
||||
? hostedView.getTimeNavigationControls()
|
||||
: defaultTimeNavigationNodes);
|
||||
|
||||
//do further setup of new view.
|
||||
ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
|
||||
hostedView.refresh();
|
||||
toolBar.getItems().addAll(2, view.getSettingsNodes());
|
||||
notificationPane.setContent(hostedView);
|
||||
|
||||
//listen to has events property and show "dialog" if it is false.
|
||||
hostedView.hasVisibleEventsProperty().addListener(hasEvents -> {
|
||||
notificationPane.setContent(hostedView.hasVisibleEvents()
|
||||
@ -640,6 +695,32 @@ final public class ViewFrame extends BorderPane {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the given List of Nodes in the top ToolBar. Replaces any settings
|
||||
* Nodes that may have previously been set with the given List of Nodes.
|
||||
*
|
||||
* @param newSettingsNodes The Nodes to show in the ToolBar.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void setViewSettingsControls(List<Node> newSettingsNodes) {
|
||||
toolBar.getItems().removeAll(this.settingsNodes); //remove old nodes
|
||||
this.settingsNodes.setAll(newSettingsNodes);
|
||||
toolBar.getItems().addAll(SETTINGS_TOOLBAR_INSERTION_INDEX, settingsNodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the given List of Nodes in the time range ToolBar. Replaces any
|
||||
* Nodes that may have previously been set with the given List of Nodes.
|
||||
*
|
||||
* @param newSettingsNodes The Nodes to show in the time range ToolBar.
|
||||
*/
|
||||
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
|
||||
private void setTimeNavigationControls(List<Node> timeNavigationNodes) {
|
||||
timeRangeToolBar.getItems().removeAll(this.timeNavigationNodes); //remove old nodes
|
||||
this.timeNavigationNodes.setAll(timeNavigationNodes);
|
||||
timeRangeToolBar.getItems().addAll(TIME_TOOLBAR_INSERTION_INDEX, timeNavigationNodes);
|
||||
}
|
||||
|
||||
@NbBundle.Messages("NoEventsDialog.titledPane.text=No Visible Events")
|
||||
private class NoEventsDialog extends StackPane {
|
||||
|
||||
@ -660,17 +741,18 @@ final public class ViewFrame extends BorderPane {
|
||||
|
||||
private NoEventsDialog(Runnable closeCallback) {
|
||||
this.closeCallback = closeCallback;
|
||||
FXMLConstructor.construct(this, "NoEventsDialog.fxml"); // NON-NLS
|
||||
FXMLConstructor.construct(this, "NoEventsDialog.fxml"); //NON-NLS
|
||||
}
|
||||
|
||||
@FXML
|
||||
@NbBundle.Messages("ViewFrame.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.")
|
||||
void initialize() {
|
||||
assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS
|
||||
assert dismissButton != null : "fx:id=\"dismissButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS
|
||||
assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS
|
||||
assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
|
||||
assert dismissButton != null : "fx:id=\"dismissButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
|
||||
assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
|
||||
|
||||
titledPane.setText(Bundle.NoEventsDialog_titledPane_text());
|
||||
noEventsDialogLabel.setText(NbBundle.getMessage(NoEventsDialog.class, "ViewFrame.noEventsDialogLabel.text")); // NON-NLS
|
||||
noEventsDialogLabel.setText(Bundle.ViewFrame_noEventsDialogLabel_text());
|
||||
|
||||
dismissButton.setOnAction(actionEvent -> closeCallback.run());
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014-16 Basis Technology Corp.
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.countsview;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Lists;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -56,6 +57,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.ViewMode;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
|
||||
import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart;
|
||||
@ -112,14 +114,11 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
|
||||
"CountsViewPane.numberOfEvents=Number of Events ({0})"})
|
||||
public CountsViewPane(TimeLineController controller) {
|
||||
super(controller);
|
||||
|
||||
|
||||
setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes()));
|
||||
getChart().setData(dataSeries);
|
||||
Tooltip.install(getChart(), getDefaultTooltip());
|
||||
|
||||
setSettingsNodes(new CountsViewSettingsPane().getChildrenUnmodifiable());
|
||||
|
||||
dateAxis.getTickMarks().addListener((Observable tickMarks) -> layoutDateLabels());
|
||||
dateAxis.categorySpacingProperty().addListener((Observable spacing) -> layoutDateLabels());
|
||||
dateAxis.getCategories().addListener((Observable categories) -> layoutDateLabels());
|
||||
@ -167,6 +166,26 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
|
||||
createSeries();
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected ViewMode getViewMode() {
|
||||
return ViewMode.COUNTS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getSettingsControls() {
|
||||
return ImmutableList.copyOf(new CountsViewSettingsPane().getChildrenUnmodifiable());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasCustomTimeNavigationControls() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getTimeNavigationControls() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the appropriate label on the vertical axis, depending on the selected
|
||||
* scale.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Autopsy Forensic Browser
|
||||
*
|
||||
* Copyright 2014-16 Basis Technology Corp.
|
||||
* Copyright 2011-2016 Basis Technology Corp.
|
||||
* Contact: carrier <at> sleuthkit <dot> org
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -18,6 +18,7 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.detailview;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
@ -29,6 +30,7 @@ import javafx.beans.Observable;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.chart.Axis;
|
||||
import javafx.scene.control.Alert;
|
||||
import javafx.scene.control.ButtonBar;
|
||||
@ -51,6 +53,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
|
||||
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
|
||||
import org.sleuthkit.autopsy.timeline.FXMLConstructor;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.ViewMode;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
|
||||
@ -112,7 +115,6 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
|
||||
|
||||
//initialize chart;
|
||||
setChart(new DetailsChart(controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis, getSelectedNodes()));
|
||||
setSettingsNodes(new DetailViewSettingsPane(getChart().getLayoutSettings()).getChildrenUnmodifiable());
|
||||
|
||||
//bind layout fo axes and spacers
|
||||
detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels());
|
||||
@ -243,6 +245,26 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
final protected ViewMode getViewMode() {
|
||||
return ViewMode.DETAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getSettingsControls() {
|
||||
return ImmutableList.copyOf(new DetailViewSettingsPane(getChart().getLayoutSettings()).getChildrenUnmodifiable());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasCustomTimeNavigationControls() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getTimeNavigationControls() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Pane that contains widgets to adjust settings specific to a
|
||||
* DetailViewPane
|
||||
@ -286,7 +308,7 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
|
||||
|
||||
DetailViewSettingsPane(DetailsChartLayoutSettings layoutSettings) {
|
||||
this.layoutSettings = layoutSettings;
|
||||
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); // NON-NLS
|
||||
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); //NON-NLS
|
||||
}
|
||||
|
||||
@NbBundle.Messages({
|
||||
@ -300,11 +322,11 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
|
||||
"DetailViewPane.hiddenRadio.text=Hide Description"})
|
||||
@FXML
|
||||
void initialize() {
|
||||
assert bandByTypeBox != null : "fx:id=\"bandByTypeBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert oneEventPerRowBox != null : "fx:id=\"oneEventPerRowBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert truncateAllBox != null : "fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert truncateWidthSlider != null : "fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert pinnedEventsToggle != null : "fx:id=\"pinnedEventsToggle\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; // NON-NLS
|
||||
assert bandByTypeBox != null : "fx:id=\"bandByTypeBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
|
||||
assert oneEventPerRowBox != null : "fx:id=\"oneEventPerRowBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
|
||||
assert truncateAllBox != null : "fx:id=\"truncateAllBox\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
|
||||
assert truncateWidthSlider != null : "fx:id=\"truncateAllSlider\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
|
||||
assert pinnedEventsToggle != null : "fx:id=\"pinnedEventsToggle\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
|
||||
|
||||
//bind widgets to settings object properties
|
||||
bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty());
|
||||
|
@ -1,19 +1,70 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.Cursor?>
|
||||
<?import javafx.scene.control.Button?>
|
||||
<?import javafx.scene.control.ComboBox?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.control.TableColumn?>
|
||||
<?import javafx.scene.control.TableView?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.BorderPane?>
|
||||
<?import javafx.scene.layout.HBox?>
|
||||
<?import javafx.scene.layout.Region?>
|
||||
|
||||
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
|
||||
<top>
|
||||
<HBox alignment="CENTER" BorderPane.alignment="CENTER">
|
||||
<HBox alignment="CENTER_RIGHT" spacing="5.0" BorderPane.alignment="CENTER">
|
||||
<children>
|
||||
<Region HBox.hgrow="ALWAYS" />
|
||||
<Label fx:id="eventCountLabel" text=" # of events" />
|
||||
<HBox fx:id="navControls" alignment="CENTER_LEFT" minHeight="38.0" spacing="5.0">
|
||||
<children>
|
||||
<Label text="Jump By: " />
|
||||
<ComboBox fx:id="scrollInrementComboBox" prefHeight="32.0">
|
||||
<HBox.margin>
|
||||
<Insets right="10.0" />
|
||||
</HBox.margin></ComboBox>
|
||||
<Button fx:id="firstButton" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefHeight="32.0" prefWidth="32.0">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/resultset_first.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button fx:id="previousButton" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefHeight="32.0" prefWidth="32.0">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/resultset_previous.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button fx:id="nextButton" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefHeight="32.0" prefWidth="32.0">
|
||||
<cursor>
|
||||
<Cursor fx:constant="HAND" />
|
||||
</cursor>
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/resultset_next.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
<Button fx:id="lastButton" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" prefHeight="32.0" prefWidth="32.0">
|
||||
<graphic>
|
||||
<ImageView>
|
||||
<image>
|
||||
<Image url="@../../images/resultset_last.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</graphic>
|
||||
</Button>
|
||||
</children>
|
||||
</HBox>
|
||||
</children>
|
||||
<BorderPane.margin>
|
||||
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
|
||||
|
@ -19,20 +19,30 @@
|
||||
package org.sleuthkit.autopsy.timeline.ui.listvew;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import java.time.Instant;
|
||||
import java.time.ZoneId;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.temporal.ChronoField;
|
||||
import java.time.temporal.TemporalUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentSkipListSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.logging.Level;
|
||||
import java.util.stream.Collectors;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.beans.binding.Bindings;
|
||||
import javafx.beans.binding.IntegerBinding;
|
||||
import javafx.beans.binding.StringBinding;
|
||||
import javafx.beans.property.SimpleObjectProperty;
|
||||
import javafx.beans.value.ObservableValue;
|
||||
@ -40,8 +50,12 @@ import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.geometry.Pos;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.ComboBox;
|
||||
import javafx.scene.control.ContextMenu;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.control.ListCell;
|
||||
import javafx.scene.control.MenuItem;
|
||||
import javafx.scene.control.OverrunStyle;
|
||||
import javafx.scene.control.SelectionMode;
|
||||
@ -54,11 +68,14 @@ import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.BorderPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.util.Callback;
|
||||
import javax.swing.Action;
|
||||
import javax.swing.JMenuItem;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.controlsfx.control.Notifications;
|
||||
import org.controlsfx.control.action.ActionUtils;
|
||||
import org.openide.awt.Actions;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.openide.util.actions.Presenter;
|
||||
@ -88,11 +105,40 @@ class ListTimeline extends BorderPane {
|
||||
|
||||
private static final Image HASH_HIT = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NON-NLS
|
||||
private static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); //NON-NLS
|
||||
private static final Image FIRST = new Image("/org/sleuthkit/autopsy/timeline/images/resultset_first.png"); //NON-NLS
|
||||
private static final Image PREVIOUS = new Image("/org/sleuthkit/autopsy/timeline/images/resultset_previous.png"); //NON-NLS
|
||||
private static final Image NEXT = new Image("/org/sleuthkit/autopsy/timeline/images/resultset_next.png"); //NON-NLS
|
||||
private static final Image LAST = new Image("/org/sleuthkit/autopsy/timeline/images/resultset_last.png"); //NON-NLS
|
||||
|
||||
/**
|
||||
* call-back used to wrap the CombinedEvent in a ObservableValue
|
||||
*/
|
||||
private static final Callback<TableColumn.CellDataFeatures<CombinedEvent, CombinedEvent>, ObservableValue<CombinedEvent>> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue());
|
||||
private static final List<ChronoField> SCROLL_BY_UNITS = Arrays.asList(
|
||||
ChronoField.YEAR,
|
||||
ChronoField.MONTH_OF_YEAR,
|
||||
ChronoField.DAY_OF_MONTH,
|
||||
ChronoField.HOUR_OF_DAY,
|
||||
ChronoField.MINUTE_OF_HOUR,
|
||||
ChronoField.SECOND_OF_MINUTE);
|
||||
|
||||
@FXML
|
||||
private HBox navControls;
|
||||
|
||||
@FXML
|
||||
private ComboBox<ChronoField> scrollInrementComboBox;
|
||||
|
||||
@FXML
|
||||
private Button firstButton;
|
||||
|
||||
@FXML
|
||||
private Button previousButton;
|
||||
|
||||
@FXML
|
||||
private Button nextButton;
|
||||
|
||||
@FXML
|
||||
private Button lastButton;
|
||||
|
||||
@FXML
|
||||
private Label eventCountLabel;
|
||||
@ -118,6 +164,8 @@ class ListTimeline extends BorderPane {
|
||||
*/
|
||||
private final ObservableList<Long> selectedEventIDs = FXCollections.observableArrayList();
|
||||
|
||||
private final ConcurrentSkipListSet<CombinedEvent> visibleEvents;
|
||||
|
||||
private final TimeLineController controller;
|
||||
private final SleuthkitCase sleuthkitCase;
|
||||
private final TagsManager tagsManager;
|
||||
@ -128,10 +176,12 @@ class ListTimeline extends BorderPane {
|
||||
* @param controller The controller for this timeline
|
||||
*/
|
||||
ListTimeline(TimeLineController controller) {
|
||||
|
||||
this.controller = controller;
|
||||
sleuthkitCase = controller.getAutopsyCase().getSleuthkitCase();
|
||||
tagsManager = controller.getAutopsyCase().getServices().getTagsManager();
|
||||
FXMLConstructor.construct(this, ListTimeline.class, "ListTimeline.fxml"); //NON-NLS
|
||||
this.visibleEvents = new ConcurrentSkipListSet<>(Comparator.comparing(table.getItems()::indexOf));
|
||||
}
|
||||
|
||||
@FXML
|
||||
@ -147,6 +197,16 @@ class ListTimeline extends BorderPane {
|
||||
assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS
|
||||
assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'."; //NON-NLS
|
||||
|
||||
scrollInrementComboBox.setButtonCell(new ChronoFieldListCell());
|
||||
scrollInrementComboBox.setCellFactory(comboBox -> new ChronoFieldListCell());
|
||||
scrollInrementComboBox.getItems().setAll(SCROLL_BY_UNITS);
|
||||
scrollInrementComboBox.getSelectionModel().select(ChronoField.YEAR);
|
||||
|
||||
ActionUtils.configureButton(new ScrollToFirst(), firstButton);
|
||||
ActionUtils.configureButton(new ScrollToPrevious(), previousButton);
|
||||
ActionUtils.configureButton(new ScrollToNext(), nextButton);
|
||||
ActionUtils.configureButton(new ScrollToLast(), lastButton);
|
||||
|
||||
//override default row with one that provides context menus
|
||||
table.setRowFactory(tableView -> new EventRow());
|
||||
|
||||
@ -248,6 +308,18 @@ class ListTimeline extends BorderPane {
|
||||
table.requestFocus();
|
||||
}
|
||||
|
||||
List<Node> getNavControls() {
|
||||
return Collections.singletonList(navControls);
|
||||
}
|
||||
|
||||
private void scrollToAndFocus(Integer index) {
|
||||
table.requestFocus();
|
||||
if (visibleEvents.contains(table.getItems().get(index)) == false) {
|
||||
table.scrollTo(index);
|
||||
}
|
||||
table.getFocusModel().focus(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* TableCell to show the (sub) type of an event.
|
||||
*/
|
||||
@ -514,11 +586,16 @@ class ListTimeline extends BorderPane {
|
||||
"ListChart.errorMsg=There was a problem getting the content for the selected event."})
|
||||
@Override
|
||||
protected void updateItem(CombinedEvent item, boolean empty) {
|
||||
CombinedEvent oldItem = getItem();
|
||||
if (oldItem != null) {
|
||||
visibleEvents.remove(oldItem);
|
||||
}
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
event = null;
|
||||
} else {
|
||||
visibleEvents.add(item);
|
||||
event = controller.getEventsModel().getEventById(item.getRepresentativeEventID());
|
||||
|
||||
setOnContextMenuRequested(contextMenuEvent -> {
|
||||
@ -572,4 +649,122 @@ class ListTimeline extends BorderPane {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ChronoFieldListCell extends ListCell<ChronoField> {
|
||||
|
||||
@Override
|
||||
protected void updateItem(ChronoField item, boolean empty) {
|
||||
super.updateItem(item, empty);
|
||||
|
||||
if (empty || item == null) {
|
||||
setText(null);
|
||||
} else {
|
||||
String displayName = item.getDisplayName(Locale.getDefault());
|
||||
setText(String.join(" ", StringUtils.splitByCharacterTypeCamelCase(displayName)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class ScrollToFirst extends org.controlsfx.control.action.Action {
|
||||
|
||||
ScrollToFirst() {
|
||||
super("", actionEvent -> scrollToAndFocus(0));
|
||||
setGraphic(new ImageView(FIRST));
|
||||
disabledProperty().bind(table.getFocusModel().focusedIndexProperty().lessThan(1));
|
||||
}
|
||||
}
|
||||
|
||||
private class ScrollToLast extends org.controlsfx.control.action.Action {
|
||||
|
||||
ScrollToLast() {
|
||||
super("", actionEvent -> scrollToAndFocus(table.getItems().size() - 1));
|
||||
setGraphic(new ImageView(LAST));
|
||||
IntegerBinding size = Bindings.size(table.getItems());
|
||||
disabledProperty().bind(size.isEqualTo(0).or(
|
||||
table.getFocusModel().focusedIndexProperty().greaterThanOrEqualTo(size.subtract(1))));
|
||||
}
|
||||
}
|
||||
|
||||
private class ScrollToNext extends org.controlsfx.control.action.Action {
|
||||
|
||||
ScrollToNext() {
|
||||
super("", actionEvent -> {
|
||||
|
||||
ChronoField selectedChronoField = scrollInrementComboBox.getSelectionModel().getSelectedItem();
|
||||
ZoneId timeZoneID = TimeLineController.getTimeZoneID();
|
||||
TemporalUnit selectedUnit = selectedChronoField.getBaseUnit();
|
||||
|
||||
int focusedIndex = table.getFocusModel().getFocusedIndex();
|
||||
CombinedEvent focusedItem = table.getFocusModel().getFocusedItem();
|
||||
if (-1 == focusedIndex || null == focusedItem) {
|
||||
focusedItem = visibleEvents.first();
|
||||
focusedIndex = table.getItems().indexOf(focusedItem);
|
||||
}
|
||||
|
||||
ZonedDateTime focusedDateTime = Instant.ofEpochMilli(focusedItem.getStartMillis()).atZone(timeZoneID);
|
||||
ZonedDateTime nextDateTime = focusedDateTime.plus(1, selectedUnit);//
|
||||
for (ChronoField field : SCROLL_BY_UNITS) {
|
||||
if (field.getBaseUnit().getDuration().compareTo(selectedUnit.getDuration()) < 0) {
|
||||
nextDateTime = nextDateTime.with(field, field.rangeRefinedBy(nextDateTime).getMinimum());//
|
||||
}
|
||||
}
|
||||
long nextMillis = nextDateTime.toInstant().toEpochMilli();
|
||||
|
||||
int nextIndex = table.getItems().size() - 1;
|
||||
for (int i = focusedIndex; i < table.getItems().size(); i++) {
|
||||
if (table.getItems().get(i).getStartMillis() >= nextMillis) {
|
||||
nextIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
scrollToAndFocus(nextIndex);
|
||||
});
|
||||
setGraphic(new ImageView(NEXT));
|
||||
IntegerBinding size = Bindings.size(table.getItems());
|
||||
disabledProperty().bind(size.isEqualTo(0).or(
|
||||
table.getFocusModel().focusedIndexProperty().greaterThanOrEqualTo(size.subtract(1))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class ScrollToPrevious extends org.controlsfx.control.action.Action {
|
||||
|
||||
ScrollToPrevious() {
|
||||
super("", actionEvent -> {
|
||||
ZoneId timeZoneID = TimeLineController.getTimeZoneID();
|
||||
ChronoField selectedChronoField = scrollInrementComboBox.getSelectionModel().getSelectedItem();
|
||||
TemporalUnit selectedUnit = selectedChronoField.getBaseUnit();
|
||||
|
||||
int focusedIndex = table.getFocusModel().getFocusedIndex();
|
||||
CombinedEvent focusedItem = table.getFocusModel().getFocusedItem();
|
||||
if (-1 == focusedIndex || null == focusedItem) {
|
||||
focusedItem = visibleEvents.last();
|
||||
focusedIndex = table.getItems().indexOf(focusedItem);
|
||||
}
|
||||
|
||||
ZonedDateTime focusedDateTime = Instant.ofEpochMilli(focusedItem.getStartMillis()).atZone(timeZoneID);
|
||||
ZonedDateTime previousDateTime = focusedDateTime.minus(1, selectedUnit);//
|
||||
|
||||
for (ChronoField field : SCROLL_BY_UNITS) {
|
||||
if (field.getBaseUnit().getDuration().compareTo(selectedUnit.getDuration()) < 0) {
|
||||
previousDateTime = previousDateTime.with(field, field.rangeRefinedBy(previousDateTime).getMaximum());//
|
||||
}
|
||||
}
|
||||
long previousMillis = previousDateTime.toInstant().toEpochMilli();
|
||||
|
||||
int previousIndex = 0;
|
||||
for (int i = focusedIndex; i > 0; i--) {
|
||||
if (table.getItems().get(i).getStartMillis() <= previousMillis) {
|
||||
previousIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
scrollToAndFocus(previousIndex);
|
||||
});
|
||||
setGraphic(new ImageView(PREVIOUS));
|
||||
disabledProperty().bind(table.getFocusModel().focusedIndexProperty().lessThan(1));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,14 +18,17 @@
|
||||
*/
|
||||
package org.sleuthkit.autopsy.timeline.ui.listvew;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.Observable;
|
||||
import javafx.concurrent.Task;
|
||||
import javafx.scene.Parent;
|
||||
import javafx.scene.Node;
|
||||
import org.joda.time.Interval;
|
||||
import org.openide.util.NbBundle;
|
||||
import org.sleuthkit.autopsy.timeline.TimeLineController;
|
||||
import org.sleuthkit.autopsy.timeline.ViewMode;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.CombinedEvent;
|
||||
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
|
||||
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
|
||||
@ -48,7 +51,6 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
|
||||
//initialize chart;
|
||||
setCenter(listTimeline);
|
||||
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable());
|
||||
|
||||
//keep controller's list of selected event IDs in sync with this list's
|
||||
listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> {
|
||||
@ -66,13 +68,34 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
listTimeline.clear();
|
||||
}
|
||||
|
||||
private static class ListViewSettingsPane extends Parent {
|
||||
@Override
|
||||
final protected ViewMode getViewMode() {
|
||||
return ViewMode.LIST;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getSettingsControls() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ImmutableList<Node> getTimeNavigationControls() {
|
||||
return ImmutableList.copyOf(listTimeline.getNavControls());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean hasCustomTimeNavigationControls() {
|
||||
return true;
|
||||
}
|
||||
|
||||
private class ListUpdateTask extends ViewRefreshTask<Interval> {
|
||||
|
||||
@NbBundle.Messages({
|
||||
"ListViewPane.loggedTask.queryDb=Retreiving event data",
|
||||
"ListViewPane.loggedTask.name=Updating List View",
|
||||
"ListViewPane.loggedTask.updateUI=Populating view"})
|
||||
ListUpdateTask() {
|
||||
super("List update task", true);
|
||||
super(Bundle.ListViewPane_loggedTask_name(), true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -91,10 +114,10 @@ public class ListViewPane extends AbstractTimeLineView {
|
||||
resetView(eventsModel.getTimeRange());
|
||||
|
||||
//get the combined events to be displayed
|
||||
updateMessage("Querying DB for events");
|
||||
updateMessage(Bundle.ListViewPane_loggedTask_queryDb());
|
||||
List<CombinedEvent> combinedEvents = eventsModel.getCombinedEvents();
|
||||
|
||||
updateMessage("Updating UI");
|
||||
updateMessage(Bundle.ListViewPane_loggedTask_updateUI());
|
||||
Platform.runLater(() -> {
|
||||
//put the combined events into the table.
|
||||
listTimeline.setCombinedEvents(combinedEvents);
|
||||
|
@ -31,7 +31,7 @@ import org.openide.util.Lookup;
|
||||
*/
|
||||
@OptionsPanelController.TopLevelRegistration(
|
||||
categoryName = "#OptionsCategory_Name_Options",
|
||||
iconBase = "org/sleuthkit/autopsy/imagegallery/images/polaroid_32_silhouette.png",
|
||||
iconBase = "org/sleuthkit/autopsy/imagegallery/images/btn_icon_image_gallery_32.png",
|
||||
keywords = "#OptionsCategory_Keywords_Options",
|
||||
keywordsCategory = "Options",
|
||||
position = 10
|
||||
|
0
ImageGallery/src/org/sleuthkit/autopsy/imagegallery/actions/OpenAction.java
Normal file → Executable file
After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 5.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB |