Merge remote-tracking branch 'upstream/develop' into 2146-3

This commit is contained in:
Oliver Spohngellert 2016-06-13 10:56:09 -04:00
commit e21eb3de48
58 changed files with 2857 additions and 2297 deletions

View File

@ -99,7 +99,7 @@ class AddImageTask implements Runnable {
List<String> errorMessages = new ArrayList<>(); List<String> errorMessages = new ArrayList<>();
List<Content> newDataSources = new ArrayList<>(); List<Content> newDataSources = new ArrayList<>();
try { try {
currentCase.getSleuthkitCase().acquireExclusiveLock(); currentCase.getSleuthkitCase().acquireExclusiveLockForSQLite();
synchronized (tskAddImageProcessLock) { synchronized (tskAddImageProcessLock) {
tskAddImageProcess = currentCase.makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles); tskAddImageProcess = currentCase.makeAddImageProcess(timeZone, true, ignoreFatOrphanFiles);
} }
@ -112,7 +112,7 @@ class AddImageTask implements Runnable {
commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources); commitOrRevertAddImageProcess(currentCase, errorMessages, newDataSources);
progressMonitor.setProgress(100); progressMonitor.setProgress(100);
} finally { } finally {
currentCase.getSleuthkitCase().releaseExclusiveLock(); currentCase.getSleuthkitCase().releaseExclusiveLockForSQLite();
DataSourceProcessorCallback.DataSourceProcessorResult result; DataSourceProcessorCallback.DataSourceProcessorResult result;
if (criticalErrorOccurred) { if (criticalErrorOccurred) {
result = DataSourceProcessorResult.CRITICAL_ERRORS; result = DataSourceProcessorResult.CRITICAL_ERRORS;

View File

@ -46,7 +46,6 @@ import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
import org.apache.commons.io.FileUtils;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import org.openide.util.actions.CallableSystemAction; 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.events.AutopsyEventPublisher;
import org.sleuthkit.autopsy.ingest.IngestJob; import org.sleuthkit.autopsy.ingest.IngestJob;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.timeline.OpenTimelineAction;
import org.sleuthkit.datamodel.BlackboardArtifactTag; import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag; import org.sleuthkit.datamodel.ContentTag;
@ -1519,28 +1519,22 @@ public class Case implements SleuthkitCase.ErrorObserver {
if (RuntimeProperties.coreComponentsAreActive()) { if (RuntimeProperties.coreComponentsAreActive()) {
// enable these menus // 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(() -> { 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 { } else {
SwingUtilities.invokeLater(() -> { SwingUtilities.invokeLater(() -> {
Frame f = WindowManager.getDefault().getMainWindow(); Frame f = WindowManager.getDefault().getMainWindow();
@ -1549,9 +1543,9 @@ public class Case implements SleuthkitCase.ErrorObserver {
} }
} else { // case is closed } else { // case is closed
if (RuntimeProperties.coreComponentsAreActive()) { SwingUtilities.invokeLater(() -> {
if (RuntimeProperties.coreComponentsAreActive()) {
SwingUtilities.invokeLater(() -> {
// close all top components first // close all top components first
CoreComponentControl.closeCoreWindows(); CoreComponentControl.closeCoreWindows();
@ -1560,15 +1554,11 @@ public class Case implements SleuthkitCase.ErrorObserver {
CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu CallableSystemAction.get(CaseCloseAction.class).setEnabled(false); // Case Close menu
CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu CallableSystemAction.get(CasePropertiesAction.class).setEnabled(false); // Case Properties menu
CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu CallableSystemAction.get(CaseDeleteAction.class).setEnabled(false); // Delete Case menu
}); CallableSystemAction.get(OpenTimelineAction.class).setEnabled(false);
} }
//clear pending notifications //clear pending notifications
SwingUtilities.invokeLater(() -> {
MessageNotifyUtil.Notify.clear(); MessageNotifyUtil.Notify.clear();
});
SwingUtilities.invokeLater(() -> {
Frame f = WindowManager.getDefault().getMainWindow(); Frame f = WindowManager.getDefault().getMainWindow();
f.setTitle(Case.getAppName()); // set the window name to just application name 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. * Deletes reports from the case.
* *
* @param reports Collection of Report to be deleted 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 * @throws TskCoreException
* @deprecated Use deleteReports(Collection<? extends Report> reports) * @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 { public void deleteReports(Collection<? extends Report> reports, boolean deleteFromDisk) throws TskCoreException {
deleteReports(reports); deleteReports(reports);
} }
} }

View File

@ -40,10 +40,10 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbTransaction;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskFileRange; import org.sleuthkit.datamodel.TskFileRange;
import org.sleuthkit.datamodel.VirtualDirectory; import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.CarvedFileContainer;
import org.sleuthkit.datamodel.LocalFilesDataSource; import org.sleuthkit.datamodel.LocalFilesDataSource;
import org.sleuthkit.datamodel.TskDataException; import org.sleuthkit.datamodel.TskDataException;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.sleuthkit.datamodel.CarvingResult;
/** /**
* A manager that provides methods for retrieving files from the current case * 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 { public class FileManager implements Closeable {
private SleuthkitCase caseDb; private SleuthkitCase caseDb;
/** /**
* Constructs a manager that provides methods for retrieving files from the * Constructs a manager that provides methods for retrieving files from the
* current case and for adding local files, carved files, and derived files * 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, "', '"); String types = StringUtils.join(mimeTypes, "', '");
return "mime_type IN ('" + types + "')"; return "mime_type IN ('" + types + "')";
} }
/** /**
* Finds all files and directories with a given file name. The name search * 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 * 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 * Adds a carving result to the case database.
* source, volume or file system.
* *
* @param fileName The name of the file. * @param carvingResult The carving result (a set of carved files and their
* @param fileSize The size of the file. * parent) to be added.
* @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. * @return A list of LayoutFile representations of the carved files.
* *
* @throws TskCoreException if there is a problem adding the file to the * @throws TskCoreException If there is a problem completing a case database
* 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) { if (null == caseDb) {
throw new TskCoreException("File manager has been closed"); 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 * Interface for receiving a notification for each file or directory added
* to the case database by a FileManager add files operation. * 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 * Adds a set of local/logical files and/or directories to the case database
* as data source. * 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 @Deprecated
public synchronized void close() throws IOException { public synchronized LayoutFile addCarvedFile(String fileName, long fileSize, long parentObjId, List<TskFileRange> layout) throws TskCoreException {
caseDb = null; 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);
} }
} }

View File

@ -387,11 +387,11 @@
<file name="org-sleuthkit-autopsy-report-ReportWizardAction.shadow"> <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="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="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-report-ReportWizardAction.instance"/>
<attr name="position" intvalue="102"/> <attr name="position" intvalue="103"/>
</file> </file>
</folder> </folder>
<folder name="Ingest"> <folder name="Ingest">
<attr intvalue="103" name="position"/> <attr intvalue="104" name="position"/>
<file name="org-sleuthkit-autopsy-ingest-IngestMessagesAction.shadow"> <file name="org-sleuthkit-autopsy-ingest-IngestMessagesAction.shadow">
<attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-ingest-IngestMessagesAction.instance"/> <attr name="originalFile" stringvalue="Actions/Tools/org-sleuthkit-autopsy-ingest-IngestMessagesAction.instance"/>
</file> </file>

View File

@ -3,7 +3,7 @@
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo"> <Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties> <Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 5]"/> <Dimension value="[0, 5]"/>
</Property> </Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 5]"/> <Dimension value="[5, 5]"/>
@ -56,6 +56,9 @@
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultPanel.directoryTablePath.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 14]"/>
</Property>
</Properties> </Properties>
</Component> </Component>
<Component class="javax.swing.JLabel" name="numberMatchLabel"> <Component class="javax.swing.JLabel" name="numberMatchLabel">
@ -73,6 +76,11 @@
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JTabbedPane" name="dataResultTabbedPanel"> <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"/> <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
</Container> </Container>

View File

@ -544,26 +544,29 @@ public class DataResultPanel extends javax.swing.JPanel implements DataResult, C
matchLabel = new javax.swing.JLabel(); matchLabel = new javax.swing.JLabel();
dataResultTabbedPanel = new javax.swing.JTabbedPane(); 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)); setPreferredSize(new java.awt.Dimension(5, 5));
org.openide.awt.Mnemonics.setLocalizedText(directoryTablePath, org.openide.util.NbBundle.getMessage(DataResultPanel.class, "DataResultPanel.directoryTablePath.text")); // NOI18N 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(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 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); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
layout.setHorizontalGroup( layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup() .addGroup(layout.createSequentialGroup()
.addComponent(directoryTablePath) .addComponent(directoryTablePath, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 518, Short.MAX_VALUE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(numberMatchLabel) .addComponent(numberMatchLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(matchLabel)) .addComponent(matchLabel))
.addComponent(dataResultTabbedPanel) .addComponent(dataResultTabbedPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
); );
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) 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) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(numberMatchLabel) .addComponent(numberMatchLabel)
.addComponent(matchLabel)) .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) .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 }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -147,7 +147,6 @@ HashsetHits.createSheet.name.name=Name
HashsetHits.createSheet.name.displayName=Name HashsetHits.createSheet.name.displayName=Name
HashsetHits.createSheet.name.desc=no description HashsetHits.createSheet.name.desc=no description
ImageNode.getActions.viewInNewWin.text=View in New Window 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.name=Name
ImageNode.createSheet.name.displayName=Name ImageNode.createSheet.name.displayName=Name
ImageNode.createSheet.name.desc=no description ImageNode.createSheet.name.desc=no description

View File

@ -18,11 +18,14 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.awt.event.ActionEvent;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; 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.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.FileSearchAction; import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; 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.Image;
import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery; import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -76,15 +81,25 @@ public class ImageNode extends AbstractContentNode<Image> {
* @return * @return
*/ */
@Override @Override
@Messages({"ImageNode.action.runIngestMods.text=Run Ingest Modules",
"ImageNode.getActions.openFileSearchByAttr.text=Open File Search by Attributes",})
public Action[] getActions(boolean context) { public Action[] getActions(boolean context) {
List<Action> actionsList = new ArrayList<Action>(); 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( actionsList.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "ImageNode.getActions.viewInNewWin.text"), this)); 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]); return actionsList.toArray(new Action[0]);
} }

View File

@ -18,13 +18,16 @@
*/ */
package org.sleuthkit.autopsy.datamodel; package org.sleuthkit.autopsy.datamodel;
import java.awt.event.ActionEvent;
import java.sql.ResultSet; import java.sql.ResultSet;
import java.sql.SQLException; import java.sql.SQLException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.Action; import javax.swing.Action;
import org.openide.nodes.Sheet; import org.openide.nodes.Sheet;
import org.openide.util.NbBundle; 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.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExtractAction; import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction; 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.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData; import org.sleuthkit.datamodel.TskData;
@ -80,13 +86,25 @@ public class VirtualDirectoryNode extends AbstractAbstractFileNode<VirtualDirect
* @return * @return
*/ */
@Override @Override
@NbBundle.Messages({"VirtualDirectoryNode.action.runIngestMods.text=Run Ingest Modules"})
public Action[] getActions(boolean popup) { public Action[] getActions(boolean popup) {
List<Action> actions = new ArrayList<>(); List<Action> actions = new ArrayList<>();
actions.add(new NewWindowViewAction( actions.add(new NewWindowViewAction(
NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.getActions.viewInNewWin.text"), this)); NbBundle.getMessage(this.getClass(), "VirtualDirectoryNode.getActions.viewInNewWin.text"), this));
actions.add(null); // creates a menu separator actions.add(null); // creates a menu separator
actions.add(ExtractAction.getInstance()); actions.add(ExtractAction.getInstance());
actions.add(null); // creates a menu separator 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()); actions.addAll(ContextMenuExtensionPoint.getActions());
return actions.toArray(new Action[0]); return actions.toArray(new Action[0]);
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent; import javax.swing.JComponent;
/** /**
@ -39,4 +40,9 @@ abstract class AbstractFileSearchFilter<T extends JComponent> implements FileSea
public T getComponent() { public T getComponent() {
return this.component; return this.component;
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener listener) {
this.getComponent().addPropertyChangeListener(listener);
}
} }

View File

@ -205,6 +205,11 @@ class DateSearchFilter extends AbstractFileSearchFilter<DateSearchPanel> {
getComponent().addActionListener(l); getComponent().addActionListener(l);
} }
@Override
public boolean isValid() {
return this.getComponent().isValidSearch();
}
/** /**
* Inner class to put the separator inside the combo box. * Inner class to put the separator inside the combo box.
*/ */

View File

@ -236,6 +236,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.modifiedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.modifiedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="modifiedCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="changedCheckBox"> <Component class="javax.swing.JCheckBox" name="changedCheckBox">
<Properties> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.changedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="changedCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="accessedCheckBox"> <Component class="javax.swing.JCheckBox" name="accessedCheckBox">
<Properties> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.accessedCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="accessedCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="createdCheckBox"> <Component class="javax.swing.JCheckBox" name="createdCheckBox">
<Properties> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="DateSearchPanel.createdCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="createdCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="org.jbundle.thin.base.screen.jcalendarbutton.JCalendarButton" name="dateFromButtonCalendar"> <Component class="org.jbundle.thin.base.screen.jcalendarbutton.JCalendarButton" name="dateFromButtonCalendar">
<Properties> <Properties>

View File

@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.DateFormat; import java.text.DateFormat;
import java.text.ParseException; import java.text.ParseException;
import java.util.Date; import java.util.Date;
@ -37,6 +39,7 @@ class DateSearchPanel extends javax.swing.JPanel {
DateFormat dateFormat; DateFormat dateFormat;
List<String> timeZones; List<String> timeZones;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
DateSearchPanel(DateFormat dateFormat, List<String> timeZones) { DateSearchPanel(DateFormat dateFormat, List<String> timeZones) {
this.dateFormat = dateFormat; this.dateFormat = dateFormat;
@ -133,6 +136,16 @@ class DateSearchPanel extends javax.swing.JPanel {
this.changedCheckBox.setEnabled(enable); this.changedCheckBox.setEnabled(enable);
this.createdCheckBox.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. * 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.setSelected(true);
modifiedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.modifiedCheckBox.text")); // NOI18N 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.setSelected(true);
changedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.changedCheckBox.text")); // NOI18N 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.setSelected(true);
accessedCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.accessedCheckBox.text")); // NOI18N 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.setSelected(true);
createdCheckBox.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.createdCheckBox.text")); // NOI18N 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.setText(org.openide.util.NbBundle.getMessage(DateSearchPanel.class, "DateSearchPanel.dateFromButtonCalendar.text")); // NOI18N
dateFromButtonCalendar.addPropertyChangeListener(new java.beans.PropertyChangeListener() { 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 private void dateCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dateCheckBoxActionPerformed
this.setComponentsEnabled(); this.setComponentsEnabled();
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_dateCheckBoxActionPerformed }//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 * Validate and set the datetime field on the screen given a datetime
* string. * string.
@ -379,6 +429,13 @@ class DateSearchPanel extends javax.swing.JPanel {
dateToTextField.setText(dateStringResult); dateToTextField.setText(dateStringResult);
dateToButtonCalendar.setTargetDate(date); 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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox accessedCheckBox; private javax.swing.JCheckBox accessedCheckBox;
private javax.swing.JCheckBox changedCheckBox; private javax.swing.JCheckBox changedCheckBox;

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import javax.swing.JComponent; import javax.swing.JComponent;
/** /**
@ -40,6 +41,13 @@ interface FileSearchFilter {
*/ */
boolean isEnabled(); 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 * Gets predicate expression to include in the SQL filter expression
* *
@ -56,6 +64,13 @@ interface FileSearchFilter {
*/ */
void addActionListener(ActionListener l); 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 * Thrown if a filter's inputs are invalid
*/ */

View File

@ -17,38 +17,35 @@
* limitations under the License. * limitations under the License.
*/ */
/* /*
* FileSearchPanel.java * FileSearchPanel.java
* *
* Created on Mar 5, 2012, 1:51:50 PM * Created on Mar 5, 2012, 1:51:50 PM
*/ */
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.awt.BorderLayout;
import java.awt.Component; import java.awt.Component;
import java.awt.Cursor; import java.awt.Cursor;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.logging.Level; 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.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder; import javax.swing.border.EmptyBorder;
import org.openide.DialogDisplayer; import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor; import org.openide.NotifyDescriptor;
import org.openide.util.NbBundle;
import org.openide.windows.TopComponent; import org.openide.windows.TopComponent;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent; import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.corecomponents.TableFilterNode;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
import org.sleuthkit.datamodel.AbstractFile; 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 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"); private static final String EMPTY_WHERE_CLAUSE = NbBundle.getMessage(DateSearchFilter.class, "FileSearchPanel.emptyWhereClause.text");
enum EVENT {
CHECKED
}
/** /**
* Creates new form FileSearchPanel * Creates new form FileSearchPanel
*/ */
@ -100,25 +101,39 @@ class FileSearchPanel extends javax.swing.JPanel {
filterPanel.add(fa); filterPanel.add(fa);
} }
for (FileSearchFilter filter : this.getFilters()) {
filter.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
searchButton.setEnabled(isValidSearch());
}
});
}
addListenerToAll(new ActionListener() { addListenerToAll(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
search(); search();
} }
}); });
searchButton.setEnabled(isValidSearch());
} }
/** /**
* @return true if any of the filters in the panel are enabled (checked) * @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()) { for (FileSearchFilter filter : this.getFilters()) {
if (filter.isEnabled()) { 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 // change the cursor to "waiting cursor" for this operation
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try { try {
if (this.anyFiltersEnabled()) { if (this.isValidSearch()) {
String title = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.title", ++resultWindowCount); String title = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.title", ++resultWindowCount);
String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText"); String pathText = NbBundle.getMessage(this.getClass(), "FileSearchPanel.search.results.pathText");
@ -290,6 +305,7 @@ class FileSearchPanel extends javax.swing.JPanel {
.addContainerGap()) .addContainerGap())
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel filterPanel; private javax.swing.JPanel filterPanel;
private javax.swing.JButton searchButton; private javax.swing.JButton searchButton;

View File

@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.datamodel.TskData.FileKnown; import org.sleuthkit.datamodel.TskData.FileKnown;
@ -42,6 +41,7 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter<KnownStatusSearch
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return this.getComponent().getKnownCheckBox().isSelected(); return this.getComponent().getKnownCheckBox().isSelected();
} }
@Override @Override
@ -83,4 +83,9 @@ class KnownStatusSearchFilter extends AbstractFileSearchFilter<KnownStatusSearch
@Override @Override
public void addActionListener(ActionListener l) { public void addActionListener(ActionListener l) {
} }
@Override
public boolean isValid() {
return this.getComponent().isValidSearch();
}
} }

View File

@ -64,6 +64,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="KnownStatusSearchPanel.unknownOptionCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="KnownStatusSearchPanel.unknownOptionCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unknownOptionCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="knownOptionCheckBox"> <Component class="javax.swing.JCheckBox" name="knownOptionCheckBox">
<Properties> <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, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/filesearch/Bundle.properties" key="KnownStatusSearchPanel.knownBadOptionCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="knownBadOptionCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
</SubComponents> </SubComponents>
</Form> </Form>

View File

@ -17,13 +17,15 @@
* limitations under the License. * limitations under the License.
*/ */
/* /*
* KnownStatusSearchPanel.java * KnownStatusSearchPanel.java
* *
* Created on Oct 19, 2011, 11:45:44 AM * Created on Oct 19, 2011, 11:45:44 AM
*/ */
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
/** /**
@ -32,6 +34,8 @@ import javax.swing.JCheckBox;
*/ */
class KnownStatusSearchPanel extends javax.swing.JPanel { class KnownStatusSearchPanel extends javax.swing.JPanel {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form KnownStatusSearchPanel * Creates new form KnownStatusSearchPanel
*/ */
@ -55,7 +59,7 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
JCheckBox getUnknownOptionCheckBox() { JCheckBox getUnknownOptionCheckBox() {
return unknownOptionCheckBox; return unknownOptionCheckBox;
} }
private void setComponentsEnabled() { private void setComponentsEnabled() {
boolean enabled = this.knownCheckBox.isSelected(); boolean enabled = this.knownCheckBox.isSelected();
this.unknownOptionCheckBox.setEnabled(enabled); this.unknownOptionCheckBox.setEnabled(enabled);
@ -63,6 +67,20 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
this.knownBadOptionCheckBox.setEnabled(enabled); 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. * 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 * 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.setSelected(true);
unknownOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.unknownOptionCheckBox.text")); // NOI18N 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.setSelected(true);
knownOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.knownOptionCheckBox.text")); // NOI18N 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.setSelected(true);
knownBadOptionCheckBox.setText(org.openide.util.NbBundle.getMessage(KnownStatusSearchPanel.class, "KnownStatusSearchPanel.knownBadOptionCheckBox.text")); // NOI18N 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); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
@ -127,13 +155,22 @@ class KnownStatusSearchPanel extends javax.swing.JPanel {
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void knownOptionCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownOptionCheckBoxActionPerformed 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 }//GEN-LAST:event_knownOptionCheckBoxActionPerformed
private void knownCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownCheckBoxActionPerformed private void knownCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_knownCheckBoxActionPerformed
setComponentsEnabled(); setComponentsEnabled();
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_knownCheckBoxActionPerformed }//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 // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox knownBadOptionCheckBox; private javax.swing.JCheckBox knownBadOptionCheckBox;
private javax.swing.JCheckBox knownCheckBox; private javax.swing.JCheckBox knownCheckBox;

View File

@ -10,28 +10,28 @@ import java.awt.event.ActionListener;
/** /**
* Filter by mime type used in filter areas of file search by attribute. * 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) { public MimeTypeFilter(MimeTypePanel component) {
super(component); super(component);
} }
public MimeTypeFilter() { public MimeTypeFilter() {
this(new MimeTypePanel()); this(new MimeTypePanel());
} }
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return this.getComponent().isSelected() && return this.getComponent().isSelected();
!this.getComponent().getMimeTypesSelected().isEmpty();
} }
@Override @Override
public String getPredicate() throws FilterValidationException { public String getPredicate() throws FilterValidationException {
String predicate = ""; String predicate = "";
for(String mimeType : this.getComponent().getMimeTypesSelected()) { for (String mimeType : this.getComponent().getMimeTypesSelected()) {
predicate += "mime_type = '" + mimeType + "' OR "; predicate += "mime_type = '" + mimeType + "' OR ";
} }
if(predicate.length() > 3) { if (predicate.length() > 3) {
predicate = predicate.substring(0, predicate.length() - 3); predicate = predicate.substring(0, predicate.length() - 3);
} }
return predicate; return predicate;
@ -40,5 +40,9 @@ class MimeTypeFilter extends AbstractFileSearchFilter<MimeTypePanel> {
@Override @Override
public void addActionListener(ActionListener l) { public void addActionListener(ActionListener l) {
} }
@Override
public boolean isValid() {
return !this.getComponent().getMimeTypesSelected().isEmpty();
}
} }

View File

@ -31,7 +31,7 @@
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" 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"> <Group type="102" alignment="0" attributes="0">
<Component id="jLabel1" min="-2" pref="246" max="-2" attributes="0"/> <Component id="jLabel1" min="-2" pref="246" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>

View File

@ -5,12 +5,16 @@
*/ */
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.logging.Level; 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.MediaType;
import org.apache.tika.mime.MimeTypes; import org.apache.tika.mime.MimeTypes;
import org.sleuthkit.autopsy.coreutils.Logger; 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 SortedSet<MediaType> mediaTypes = MimeTypes.getDefaultMimeTypes().getMediaTypeRegistry().getTypes();
private static final Logger logger = Logger.getLogger(MimeTypePanel.class.getName()); private static final Logger logger = Logger.getLogger(MimeTypePanel.class.getName());
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form MimeTypePanel * Creates new form MimeTypePanel
@ -32,6 +37,12 @@ public class MimeTypePanel extends javax.swing.JPanel {
public MimeTypePanel() { public MimeTypePanel() {
initComponents(); initComponents();
setComponentsEnabled(); setComponentsEnabled();
this.mimeTypeList.addListSelectionListener(new ListSelectionListener() {
@Override
public void valueChanged(ListSelectionEvent e) {
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}
});
} }
private String[] getMimeTypeArray() { private String[] getMimeTypeArray() {
@ -75,6 +86,16 @@ public class MimeTypePanel extends javax.swing.JPanel {
this.mimeTypeList.setEnabled(enabled); this.mimeTypeList.setEnabled(enabled);
this.jLabel1.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. * 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 private void mimeTypeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mimeTypeCheckBoxActionPerformed
setComponentsEnabled(); setComponentsEnabled();
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
this.mimeTypeList.setSelectedIndices(new int[0]);
}//GEN-LAST:event_mimeTypeCheckBoxActionPerformed }//GEN-LAST:event_mimeTypeCheckBoxActionPerformed

View File

@ -19,7 +19,6 @@
package org.sleuthkit.autopsy.filesearch; package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
@ -63,4 +62,9 @@ class NameSearchFilter extends AbstractFileSearchFilter<NameSearchPanel> {
public void addActionListener(ActionListener l) { public void addActionListener(ActionListener l) {
getComponent().addActionListener(l); getComponent().addActionListener(l);
} }
@Override
public boolean isValid() {
return !this.getComponent().getSearchTextField().getText().isEmpty();
}
} }

View File

@ -17,7 +17,7 @@
* limitations under the License. * limitations under the License.
*/ */
/* /*
* NameSearchPanel.java * NameSearchPanel.java
* *
* Created on Oct 19, 2011, 11:58:53 AM * 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.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import javax.swing.JTextField; 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 { class NameSearchPanel extends javax.swing.JPanel {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form NameSearchPanel * Creates new form NameSearchPanel
*/ */
@ -67,6 +73,22 @@ class NameSearchPanel extends javax.swing.JPanel {
copyMenuItem.addActionListener(actList); copyMenuItem.addActionListener(actList);
pasteMenuItem.addActionListener(actList); pasteMenuItem.addActionListener(actList);
selectAllMenuItem.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() { JTextField getSearchTextField() {
return searchTextField; return searchTextField;
} }
void setComponentsEnabled() { void setComponentsEnabled() {
boolean enabled = nameCheckBox.isSelected(); boolean enabled = nameCheckBox.isSelected();
this.searchTextField.setEnabled(enabled); this.searchTextField.setEnabled(enabled);
this.noteNameLabel.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. * 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 private void nameCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nameCheckBoxActionPerformed
setComponentsEnabled(); setComponentsEnabled();
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_nameCheckBoxActionPerformed }//GEN-LAST:event_nameCheckBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -20,7 +20,6 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import javax.swing.JComboBox; import javax.swing.JComboBox;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException; import org.sleuthkit.autopsy.filesearch.FileSearchFilter.FilterValidationException;
@ -73,4 +72,9 @@ class SizeSearchFilter extends AbstractFileSearchFilter<SizeSearchPanel> {
public void addActionListener(ActionListener l) { public void addActionListener(ActionListener l) {
getComponent().addActionListener(l); getComponent().addActionListener(l);
} }
@Override
public boolean isValid() {
return true;
}
} }

View File

@ -20,6 +20,8 @@ package org.sleuthkit.autopsy.filesearch;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.NumberFormat; import java.text.NumberFormat;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import javax.swing.JComboBox; import javax.swing.JComboBox;
@ -32,6 +34,8 @@ import javax.swing.JMenuItem;
*/ */
class SizeSearchPanel extends javax.swing.JPanel { class SizeSearchPanel extends javax.swing.JPanel {
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form SizeSearchPanel * Creates new form SizeSearchPanel
*/ */
@ -81,13 +85,23 @@ class SizeSearchPanel extends javax.swing.JPanel {
JComboBox<String> getSizeUnitComboBox() { JComboBox<String> getSizeUnitComboBox() {
return sizeUnitComboBox; return sizeUnitComboBox;
} }
void setComponentsEnabled() { void setComponentsEnabled() {
boolean enabled = this.sizeCheckBox.isSelected(); boolean enabled = this.sizeCheckBox.isSelected();
this.sizeCompareComboBox.setEnabled(enabled); this.sizeCompareComboBox.setEnabled(enabled);
this.sizeUnitComboBox.setEnabled(enabled); this.sizeUnitComboBox.setEnabled(enabled);
this.sizeTextField.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. * 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 private void sizeCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sizeCheckBoxActionPerformed
setComponentsEnabled(); setComponentsEnabled();
pcs.firePropertyChange(FileSearchPanel.EVENT.CHECKED.toString(), null, null);
}//GEN-LAST:event_sizeCheckBoxActionPerformed }//GEN-LAST:event_sizeCheckBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -182,12 +182,6 @@ final class PhotoRecCarverFileIngestModule implements FileIngestModule {
Path tempFilePath = null; Path tempFilePath = null;
try { try {
long id = getRootId(file);
// make sure we have a valid systemID
if (id == -1) {
return IngestModule.ProcessResult.ERROR;
}
// Verify initialization succeeded. // Verify initialization succeeded.
if (null == this.executableFile) { if (null == this.executableFile) {
logger.log(Level.SEVERE, "PhotoRec carver called after failed start up"); // NON-NLS 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 // 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(); long calcstart = System.currentTimeMillis();
PhotoRecCarverOutputParser parser = new PhotoRecCarverOutputParser(outputDirPath); 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); long calcdelta = (System.currentTimeMillis() - calcstart);
totals.totalParsetime.addAndGet(calcdelta); totals.totalParsetime.addAndGet(calcdelta);
if (carvedItems != null) { // if there were any results from carving, add the unallocated carving event to the reports list. 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; 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. * Finds and returns the path to the executable, if able.
* *

View File

@ -32,8 +32,8 @@ import org.sleuthkit.autopsy.casemodule.services.FileManager;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.XMLUtil; import org.sleuthkit.autopsy.coreutils.XMLUtil;
import org.sleuthkit.datamodel.AbstractFile; import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.CarvingResult;
import org.sleuthkit.datamodel.LayoutFile; import org.sleuthkit.datamodel.LayoutFile;
import org.sleuthkit.datamodel.CarvedFileContainer;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskFileRange; import org.sleuthkit.datamodel.TskFileRange;
import org.w3c.dom.Document; import org.w3c.dom.Document;
@ -70,7 +70,7 @@ class PhotoRecCarverOutputParser {
* @throws FileNotFoundException * @throws FileNotFoundException
* @throws IOException * @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 { try {
final Document doc = XMLUtil.loadDoc(PhotoRecCarverOutputParser.class, xmlInputFile.toString()); final Document doc = XMLUtil.loadDoc(PhotoRecCarverOutputParser.class, xmlInputFile.toString());
if (doc == null) { if (doc == null) {
@ -99,8 +99,7 @@ class PhotoRecCarverOutputParser {
FileManager fileManager = Case.getCurrentCase().getServices().getFileManager(); FileManager fileManager = Case.getCurrentCase().getServices().getFileManager();
// create and initialize the list to put into the database // 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) { for (int fileIndex = 0; fileIndex < numberOfFiles; ++fileIndex) {
entry = (Element) fileObjects.item(fileIndex); entry = (Element) fileObjects.item(fileIndex);
fileNames = entry.getElementsByTagName("filename"); //NON-NLS fileNames = entry.getElementsByTagName("filename"); //NON-NLS
@ -133,7 +132,7 @@ class PhotoRecCarverOutputParser {
if (fileByteEnd > af.getSize()) { if (fileByteEnd > af.getSize()) {
long overshoot = fileByteEnd - af.getSize(); long overshoot = fileByteEnd - af.getSize();
if (fileSize > overshoot) { if (fileSize > overshoot) {
fileSize = fileSize - overshoot; fileSize -= overshoot;
} else { } else {
// This better never happen... Data for this file is corrupted. Skip it. // This better never happen... Data for this file is corrupted. Skip it.
continue; continue;
@ -144,10 +143,10 @@ class PhotoRecCarverOutputParser {
} }
if (!tskRanges.isEmpty()) { 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) { } catch (NumberFormatException | TskCoreException ex) {
logger.log(Level.SEVERE, "Error parsing PhotoRec output and inserting it into the database: {0}", ex); //NON-NLS logger.log(Level.SEVERE, "Error parsing PhotoRec output and inserting it into the database: {0}", ex); //NON-NLS
} }

View File

@ -18,48 +18,45 @@
*/ */
package org.sleuthkit.autopsy.modules.stix; 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.BufferedWriter;
import java.io.File;
import java.io.FileWriter; import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays; 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.swing.JPanel;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller; import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName; import javax.xml.namespace.QName;
import org.mitre.cybox.cybox_2.ObjectType; import org.mitre.cybox.cybox_2.ObjectType;
import org.mitre.cybox.cybox_2.Observable; import org.mitre.cybox.cybox_2.Observable;
import org.mitre.cybox.cybox_2.ObservableCompositionType; 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.common_1.IndicatorBaseType;
import org.mitre.stix.indicator_2.Indicator; import org.mitre.stix.indicator_2.Indicator;
import org.mitre.stix.stix_1.STIXPackage; 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.openide.util.NbBundle;
import org.sleuthkit.autopsy.report.ReportProgressPanel; import org.sleuthkit.autopsy.coreutils.Logger;
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.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ModuleSettings; 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.autopsy.report.ReportProgressPanel.ReportStatus;
import org.sleuthkit.datamodel.TskCoreException;
/** /**
* *
@ -180,6 +177,9 @@ public class STIXReportModule implements GeneralReportModule {
// Process each STIX file // Process each STIX file
for (File file : stixFiles) { for (File file : stixFiles) {
if (progressPanel.getStatus() == ReportStatus.CANCELED) {
return;
}
try { try {
processFile(file.getAbsolutePath(), progressPanel); processFile(file.getAbsolutePath(), progressPanel);
} catch (TskCoreException ex) { } catch (TskCoreException ex) {

File diff suppressed because it is too large Load Diff

0
Core/src/org/sleuthkit/autopsy/report/ReportKML.java Normal file → Executable file
View File

View File

@ -24,9 +24,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import static java.util.Collections.swap; import static java.util.Collections.swap;
import java.util.Comparator; import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.JList; import javax.swing.JList;
import javax.swing.JPanel; 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. // Make sure that the report module has a valid non-null name.
private boolean moduleIsValid(ReportModule module) { private boolean moduleIsValid(ReportModule module) {
return module.getName() != null && !module.getName().isEmpty() return module.getName() != null && !module.getName().isEmpty()
&& module.getRelativeFilePath() != null; && module.getRelativeFilePath() != null;
} }
private void popupWarning(ReportModule module) { private void popupWarning(ReportModule module) {
@ -163,13 +161,12 @@ final class ReportVisualPanel1 extends JPanel implements ListSelectionListener {
* *
* @return * @return
*/ */
Map<TableReportModule, Boolean> getTableModuleStates() { TableReportModule getTableModule() {
Map<TableReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
ReportModule mod = getSelectedModule(); ReportModule mod = getSelectedModule();
if (tableModules.contains(mod)) { 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 * @return
*/ */
Map<GeneralReportModule, Boolean> getGeneralModuleStates() { GeneralReportModule getGeneralModule() {
Map<GeneralReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
ReportModule mod = getSelectedModule(); ReportModule mod = getSelectedModule();
if (generalModules.contains(mod)) { 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 * @return
*/ */
Map<FileReportModule, Boolean> getFileModuleStates() { FileReportModule getFileModule() {
Map<FileReportModule, Boolean> reportModuleStates = new LinkedHashMap<>();
ReportModule mod = getSelectedModule(); ReportModule mod = getSelectedModule();
if (fileModules.contains(mod)) { if (fileModules.contains(mod)) {
reportModuleStates.put((FileReportModule) mod, Boolean.TRUE); return (FileReportModule) mod;
} }
return reportModuleStates; return null;
} }
/** /**

View File

@ -1,4 +1,4 @@
/* /*
* *
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
@ -65,13 +65,17 @@ public final class ReportWizardAction extends CallableSystemAction implements Pr
wiz.setTitle(NbBundle.getMessage(ReportWizardAction.class, "ReportWizardAction.reportWiz.title")); wiz.setTitle(NbBundle.getMessage(ReportWizardAction.class, "ReportWizardAction.reportWiz.title"));
if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) { if (DialogDisplayer.getDefault().notify(wiz) == WizardDescriptor.FINISH_OPTION) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
ReportGenerator generator = new ReportGenerator((Map<TableReportModule, Boolean>) wiz.getProperty("tableModuleStates"), //NON-NLS ReportGenerator generator = new ReportGenerator(); //NON-NLS
(Map<GeneralReportModule, Boolean>) wiz.getProperty("generalModuleStates"), //NON-NLS TableReportModule tableReport = (TableReportModule) wiz.getProperty("tableModule");
(Map<FileReportModule, Boolean>) wiz.getProperty("fileModuleStates")); //NON-NLS GeneralReportModule generalReport = (GeneralReportModule) wiz.getProperty("generalModule");
generator.generateTableReports((Map<BlackboardArtifact.Type, Boolean>) wiz.getProperty("artifactStates"), (Map<String, Boolean>) wiz.getProperty("tagStates")); //NON-NLS FileReportModule fileReport = (FileReportModule) wiz.getProperty("fileModule");
generator.generateFileListReports((Map<FileReportDataTypes, Boolean>) wiz.getProperty("fileReportOptions")); //NON-NLS if (tableReport != null) {
generator.generateGeneralReports(); generator.generateTableReport(tableReport, (Map<BlackboardArtifact.Type, Boolean>) wiz.getProperty("artifactStates"), (Map<String, Boolean>) wiz.getProperty("tagStates")); //NON-NLS
generator.displayProgressPanels(); } else if (generalReport != null) {
generator.generateGeneralReport(generalReport);
} else if (fileReport != null) {
generator.generateFileListReport(fileReport, (Map<FileReportDataTypes, Boolean>) wiz.getProperty("fileReportOptions")); //NON-NLS
}
} }
} }

View File

@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.report;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.util.Collection; import java.util.Collection;
import java.util.Map;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.event.ChangeListener; import javax.swing.event.ChangeListener;
@ -108,17 +107,17 @@ class ReportWizardPanel1 implements WizardDescriptor.FinishablePanel<WizardDescr
@Override @Override
public void storeSettings(WizardDescriptor wiz) { public void storeSettings(WizardDescriptor wiz) {
Map<TableReportModule, Boolean> tables = getComponent().getTableModuleStates(); TableReportModule module = getComponent().getTableModule();
Map<GeneralReportModule, Boolean> generals = getComponent().getGeneralModuleStates(); GeneralReportModule general = getComponent().getGeneralModule();
wiz.putProperty("tableModuleStates", tables); //NON-NLS wiz.putProperty("tableModule", module); //NON-NLS
wiz.putProperty("generalModuleStates", generals); //NON-NLS wiz.putProperty("generalModule", general); //NON-NLS
wiz.putProperty("fileModuleStates", getComponent().getFileModuleStates()); //NON-NLS wiz.putProperty("fileModule", getComponent().getFileModule()); //NON-NLS
// Store preferences that WizardIterator will use to determine what // Store preferences that WizardIterator will use to determine what
// panels need to be shown // panels need to be shown
Preferences prefs = NbPreferences.forModule(ReportWizardPanel1.class); Preferences prefs = NbPreferences.forModule(ReportWizardPanel1.class);
prefs.putBoolean("tableModule", any(tables.values())); //NON-NLS prefs.putBoolean("tableModule", module != null); //NON-NLS
prefs.putBoolean("generalModule", any(generals.values())); //NON-NLS prefs.putBoolean("generalModule", general != null); //NON-NLS
} }
/** /**

File diff suppressed because it is too large Load Diff

View File

@ -18,8 +18,13 @@
*/ */
package org.sleuthkit.autopsy.timeline; 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.io.IOException;
import java.util.logging.Level; import java.util.logging.Level;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import org.openide.awt.ActionID; import org.openide.awt.ActionID;
import org.openide.awt.ActionReference; import org.openide.awt.ActionReference;
import org.openide.awt.ActionReferences; import org.openide.awt.ActionReferences;
@ -27,6 +32,7 @@ import org.openide.awt.ActionRegistration;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.CallableSystemAction; import org.openide.util.actions.CallableSystemAction;
import org.openide.util.actions.Presenter;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.core.Installer; import org.sleuthkit.autopsy.core.Installer;
import org.sleuthkit.autopsy.coreutils.Logger; 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") @ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
@ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false) @ActionRegistration(displayName = "#CTL_MakeTimeline", lazy = false)
@ActionReferences(value = { @ActionReferences(value = {
@ActionReference(path = "Menu/Tools", position = 100)}) @ActionReference(path = "Menu/Tools", position = 100),
public class OpenTimelineAction extends CallableSystemAction { @ActionReference(path = "Toolbars/Case", position = 102)})
public class OpenTimelineAction extends CallableSystemAction implements Presenter.Toolbar {
private static final Logger LOGGER = Logger.getLogger(OpenTimelineAction.class.getName()); 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 static TimeLineController timeLineController = null;
private JButton toolbarButton = new JButton();
synchronized static void invalidateController() { synchronized static void invalidateController() {
timeLineController = null; timeLineController = null;
} }
public OpenTimelineAction() {
toolbarButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
performAction();
}
});
this.setEnabled(false);
}
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
/** /**
@ -102,4 +121,29 @@ public class OpenTimelineAction extends CallableSystemAction {
public boolean asynchronous() { public boolean asynchronous() {
return false; // run on edt 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;
}
} }

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,10 +19,9 @@
package org.sleuthkit.autopsy.timeline; package org.sleuthkit.autopsy.timeline;
/** /**
* * Enumeration of view modes.
*/ */
public enum ViewMode { public enum ViewMode {
COUNTS, COUNTS,
DETAIL, DETAIL,
LIST; LIST;

View File

@ -597,7 +597,7 @@ public class EventsRepository {
timeMap.put(FileSystemTypes.FILE_MODIFIED, f.getMtime()); 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 * logical/local files) skip the rest of the event generation: this
* should result in droping logical files, since they do not have * should result in droping logical files, since they do not have
* legitimate time stamps. * legitimate time stamps.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 522 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2016 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,10 +18,8 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui; package org.sleuthkit.autopsy.timeline.ui;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe; 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.concurrent.ExecutionException;
import java.util.logging.Level; import java.util.logging.Level;
import javafx.application.Platform; 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.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
/**
* Base class for views that can be hosted in the ViewFrame
*
*/
public abstract class AbstractTimeLineView extends BorderPane { public abstract class AbstractTimeLineView extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(AbstractTimeLineView.class.getName()); 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); 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 * Listener that is attached to various properties that should trigger a
* view update when they change. * 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 * Refresh this view based on current state of zoom / filters. Primarily
* this invokes the background ViewRefreshTask returned by * this invokes the background ViewRefreshTask returned by getUpdateTask(),
* getUpdateTask(), which derived classes must implement. * which derived classes must implement.
* *
* TODO: replace this logic with a javafx Service ? -jm * TODO: replace this logic with a javafx Service ? -jm
*/ */
@ -186,26 +183,35 @@ public abstract class AbstractTimeLineView extends BorderPane {
protected abstract Task<Boolean> getNewUpdateTask(); protected abstract Task<Boolean> getNewUpdateTask();
/** /**
* Get a List of Nodes containing settings widgets to insert into this * Get the ViewMode for this view.
* view's header. *
* @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. * @return The List of settings Nodes.
*/ */
protected List<Node> getSettingsNodes() { abstract protected ImmutableList<Node> getSettingsControls();
return Collections.unmodifiableList(settingsNodes);
}
/** /**
* Set the List of Nodes containing settings widgets to insert into this * Does this view have custom time navigation controls that should replace
* view's header. * the default ones from the ViewFrame?
* *
* * @return True if this view have custom time navigation controls.
* @param settingsNodes The List of Nodes containing settings widgets to
* insert into this view's header.
*/ */
final protected void setSettingsNodes(List<Node> settingsNodes) { abstract protected boolean hasCustomTimeNavigationControls();
this.settingsNodes = new ArrayList<>(settingsNodes);
} /**
* 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. * Dispose of this view and any resources it holds onto.

View File

@ -35,12 +35,6 @@ Timeline.ui.ZoomRanges.threeyears.text=Three Years
Timeline.ui.ZoomRanges.fiveyears.text=Five Years Timeline.ui.ZoomRanges.fiveyears.text=Five Years
Timeline.ui.ZoomRanges.tenyears.text=Ten Years Timeline.ui.ZoomRanges.tenyears.text=Ten Years
Timeline.ui.ZoomRanges.all.text=All 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 ViewFrame.zoomButton.text=Zoom to events
TimeZonePanel.localRadio.text=Local Time Zone TimeZonePanel.localRadio.text=Local Time Zone
TimeZonePanel.otherRadio.text=GMT / UTC TimeZonePanel.otherRadio.text=GMT / UTC

View File

@ -125,7 +125,7 @@
</children> </children>
</StackPane> </StackPane>
<Separator /> <Separator />
<ToolBar> <ToolBar fx:id="timeRangeToolBar">
<items> <items>
<Label fx:id="startLabel" contentDisplay="RIGHT" minWidth="-Infinity"> <Label fx:id="startLabel" contentDisplay="RIGHT" minWidth="-Infinity">
<graphic> <graphic>
@ -141,7 +141,7 @@
<Insets left="10.0" right="10.0" /> <Insets left="10.0" right="10.0" />
</HBox.margin> </HBox.margin>
</Separator> </Separator>
<HBox> <HBox fx:id="zoomInOutHBox">
<children> <children>
<Button fx:id="zoomOutButton" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false"> <Button fx:id="zoomOutButton" contentDisplay="GRAPHIC_ONLY" mnemonicParsing="false">
<graphic> <graphic>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2013-16 Basis Technology Corp. * Copyright 2011-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,19 +18,24 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui; package org.sleuthkit.autopsy.timeline.ui;
import com.google.common.collect.ImmutableList;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Supplier; import java.util.function.Supplier;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.InvalidationListener; import javafx.beans.InvalidationListener;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Insets; import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.control.Button; import javafx.scene.control.Button;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.MenuButton; 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.casemodule.events.DataSourceAddedEvent;
import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent; import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.DBUpdatedEvent;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
import org.sleuthkit.autopsy.timeline.events.TagsUpdatedEvent; 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.countsview.CountsViewPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree; 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 * A container for an AbstractTimelineView. Has a Toolbar on top to hold
* settings widgets supplied by contained AbstractTimelineView, and the * 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 * 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 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 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 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 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)); 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 * 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() { 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") @GuardedBy("this")
private LoggedTask<Void> histogramTask; 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); 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 //// time range selection components
@FXML @FXML
private MenuButton zoomMenuButton; private MenuButton zoomMenuButton;
@ -158,6 +192,8 @@ final public class ViewFrame extends BorderPane {
//// header toolbar componenets //// header toolbar componenets
@FXML @FXML
private ToolBar toolBar; private ToolBar toolBar;
private ToggleGroupValue<ViewMode> viewModeToggleGroup;
@FXML @FXML
private Label viewModeLabel; private Label viewModeLabel;
@FXML @FXML
@ -168,6 +204,7 @@ final public class ViewFrame extends BorderPane {
private ToggleButton detailsToggle; private ToggleButton detailsToggle;
@FXML @FXML
private ToggleButton listToggle; private ToggleButton listToggle;
@FXML @FXML
private Button snapShotButton; private Button snapShotButton;
@FXML @FXML
@ -176,7 +213,27 @@ final public class ViewFrame extends BorderPane {
private Button updateDBButton; 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(); private final NotificationPane notificationPane = new NotificationPane();
@ -252,7 +309,8 @@ final public class ViewFrame extends BorderPane {
this.controller = controller; this.controller = controller;
this.filteredEvents = controller.getEventsModel(); this.filteredEvents = controller.getEventsModel();
this.eventsTree = eventsTree; this.eventsTree = eventsTree;
FXMLConstructor.construct(this, "ViewFrame.fxml"); // NON-NLS FXMLConstructor.construct(this, "ViewFrame.fxml"); //NON-NLS
} }
@FXML @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." "ViewFrame.tagsAddedOrDeleted=Tags have been created and/or deleted. The view may not be up to date."
}) })
void initialize() { void initialize() {
assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.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 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 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 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 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 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 //configure notification pane
notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK); notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
@ -283,17 +344,14 @@ final public class ViewFrame extends BorderPane {
countsToggle.setText(Bundle.ViewFrame_countsToggle_text()); countsToggle.setText(Bundle.ViewFrame_countsToggle_text());
detailsToggle.setText(Bundle.ViewFrame_detailsToggle_text()); detailsToggle.setText(Bundle.ViewFrame_detailsToggle_text());
listToggle.setText(Bundle.ViewFrame_listToggle_text()); listToggle.setText(Bundle.ViewFrame_listToggle_text());
viewModeToggleGroup = new ToggleGroupValue<>();
ToggleGroupValue<ViewMode> visModeToggleGroup = new ToggleGroupValue<>(); viewModeToggleGroup.add(listToggle, ViewMode.LIST);
visModeToggleGroup.add(listToggle, ViewMode.LIST); viewModeToggleGroup.add(detailsToggle, ViewMode.DETAIL);
visModeToggleGroup.add(detailsToggle, ViewMode.DETAIL); viewModeToggleGroup.add(countsToggle, ViewMode.COUNTS);
visModeToggleGroup.add(countsToggle, ViewMode.COUNTS); modeSegButton.setToggleGroup(viewModeToggleGroup);
viewModeToggleGroup.valueProperty().addListener((observable, oldViewMode, newViewVode) ->
modeSegButton.setToggleGroup(visModeToggleGroup); controller.setViewMode(newViewVode != null ? newViewVode : (oldViewMode != null ? oldViewMode : ViewMode.COUNTS))
);
visModeToggleGroup.valueProperty().addListener((observable, oldVisMode, newValue) -> {
controller.setViewMode(newValue != null ? newValue : (oldVisMode != null ? oldVisMode : ViewMode.COUNTS));
});
controller.viewModeProperty().addListener(viewMode -> syncViewMode()); controller.viewModeProperty().addListener(viewMode -> syncViewMode());
syncViewMode(); syncViewMode();
@ -330,7 +388,7 @@ final public class ViewFrame extends BorderPane {
* rangeslider track doesn't extend to edge of node,and so the * rangeslider track doesn't extend to edge of node,and so the
* histrogram doesn't quite line up with the rangeslider * 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 //configure zoom buttons
zoomMenuButton.getItems().clear(); zoomMenuButton.getItems().clear();
@ -362,11 +420,10 @@ final public class ViewFrame extends BorderPane {
} }
/** /**
* Handle TagsUpdatedEvents by marking that the view needs to be * Handle TagsUpdatedEvents by marking that the view needs to be refreshed.
* refreshed.
* *
* NOTE: This ViewFrame must be registered with the * NOTE: This ViewFrame must be registered with the filteredEventsModel's
* filteredEventsModel's EventBus in order for this handler to be invoked. * EventBus in order for this handler to be invoked.
* *
* @param event The TagsUpdatedEvent to handle. * @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 * Handle a RefreshRequestedEvent from the events model by clearing the
* refresh notification. * refresh notification.
* *
* NOTE: This ViewFrame must be registered with the * NOTE: This ViewFrame must be registered with the filteredEventsModel's
* filteredEventsModel's EventBus in order for this handler to be invoked. * EventBus in order for this handler to be invoked.
* *
* @param event The RefreshRequestedEvent to handle. * @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 * Handle a DBUpdatedEvent from the events model by refreshing the view.
* view.
* *
* NOTE: This ViewFrame must be registered with the * NOTE: This ViewFrame must be registered with the filteredEventsModel's
* filteredEventsModel's EventBus in order for this handler to be invoked. * EventBus in order for this handler to be invoked.
* *
* @param event The DBUpdatedEvent to handle. * @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 * Handle a DataSourceAddedEvent from the events model by showing a
* notification. * notification.
* *
* NOTE: This ViewFrame must be registered with the * NOTE: This ViewFrame must be registered with the filteredEventsModel's
* filteredEventsModel's EventBus in order for this handler to be invoked. * EventBus in order for this handler to be invoked.
* *
* @param event The DataSourceAddedEvent to handle. * @param event The DataSourceAddedEvent to handle.
*/ */
@ -439,8 +495,8 @@ final public class ViewFrame extends BorderPane {
* Handle a DataSourceAnalysisCompletedEvent from the events modelby showing * Handle a DataSourceAnalysisCompletedEvent from the events modelby showing
* a notification. * a notification.
* *
* NOTE: This ViewFrame must be registered with the * NOTE: This ViewFrame must be registered with the filteredEventsModel's
* filteredEventsModel's EventBus in order for this handler to be invoked. * EventBus in order for this handler to be invoked.
* *
* @param event The DataSourceAnalysisCompletedEvent to handle. * @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. * 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() { synchronized private void refreshHistorgram() {
if (histogramTask != null) { if (histogramTask != null) {
histogramTask.cancel(true); histogramTask.cancel(true);
} }
histogramTask = new LoggedTask<Void>( histogramTask = new LoggedTask<Void>(Bundle.ViewFrame_histogramTask_title(), true) {
NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.title"), true) { // NON-NLS
private final Lighting lighting = new Lighting(); private final Lighting lighting = new Lighting();
@Override @Override
protected Void call() throws Exception { protected Void call() throws Exception {
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.preparing")); // NON-NLS updateMessage(ViewFrame_histogramTask_preparing());
long max = 0; long max = 0;
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval()); 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 //clear old data, and reset ranges and series
Platform.runLater(() -> { 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; start = end;
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.queryDb")); // NON-NLS updateMessage(ViewFrame_histogramTask_queryDb());
//query for current range //query for current range
long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum(); long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
bins.add(count); bins.add(count);
@ -510,7 +570,7 @@ final public class ViewFrame extends BorderPane {
final double fMax = Math.log(max); final double fMax = Math.log(max);
final ArrayList<Long> fbins = new ArrayList<>(bins); final ArrayList<Long> fbins = new ArrayList<>(bins);
Platform.runLater(() -> { Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.updateUI2")); // NON-NLS updateMessage(ViewFrame_histogramTask_updateUI2());
histogramBox.getChildren().clear(); 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() { private void refreshTimeUI() {
RangeDivisionInfo rangeDivisionInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval()); 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 * Sync up the view shown in the UI to the one currently active according to
* AbstractTimelineView for one of the correct type. * the controller. Swaps out the hosted AbstractTimelineView for a new one
* of the correct type.
*/ */
private void syncViewMode() { private void syncViewMode() {
AbstractTimeLineView view; ViewMode newViewMode = controller.getViewMode();
ViewMode viewMode = controller.viewModeProperty().get();
//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(() -> { Platform.runLater(() -> {
//clear out old view. //clear out old view.
if (hostedView != null) { if (hostedView != null) {
toolBar.getItems().removeAll(hostedView.getSettingsNodes());
hostedView.dispose(); hostedView.dispose();
} }
hostedView = view; //Set a new AbstractTimeLineView as the one hosted by this ViewFrame.
//setup new view. 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 ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
hostedView.refresh(); hostedView.refresh();
toolBar.getItems().addAll(2, view.getSettingsNodes());
notificationPane.setContent(hostedView); notificationPane.setContent(hostedView);
//listen to has events property and show "dialog" if it is false. //listen to has events property and show "dialog" if it is false.
hostedView.hasVisibleEventsProperty().addListener(hasEvents -> { hostedView.hasVisibleEventsProperty().addListener(hasEvents -> {
notificationPane.setContent(hostedView.hasVisibleEvents() 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") @NbBundle.Messages("NoEventsDialog.titledPane.text=No Visible Events")
private class NoEventsDialog extends StackPane { private class NoEventsDialog extends StackPane {
@ -660,17 +741,18 @@ final public class ViewFrame extends BorderPane {
private NoEventsDialog(Runnable closeCallback) { private NoEventsDialog(Runnable closeCallback) {
this.closeCallback = closeCallback; this.closeCallback = closeCallback;
FXMLConstructor.construct(this, "NoEventsDialog.fxml"); // NON-NLS FXMLConstructor.construct(this, "NoEventsDialog.fxml"); //NON-NLS
} }
@FXML @FXML
@NbBundle.Messages("ViewFrame.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.")
void initialize() { void initialize() {
assert resetFiltersButton != null : "fx:id=\"resetFiltersButton\" 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 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 zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; //NON-NLS
titledPane.setText(Bundle.NoEventsDialog_titledPane_text()); 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()); dismissButton.setOnAction(actionEvent -> closeCallback.run());

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-16 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.countsview; package org.sleuthkit.autopsy.timeline.ui.countsview;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -56,6 +57,7 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ThreadConfined; import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart; 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})"}) "CountsViewPane.numberOfEvents=Number of Events ({0})"})
public CountsViewPane(TimeLineController controller) { public CountsViewPane(TimeLineController controller) {
super(controller); super(controller);
setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes())); setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes()));
getChart().setData(dataSeries); getChart().setData(dataSeries);
Tooltip.install(getChart(), getDefaultTooltip()); Tooltip.install(getChart(), getDefaultTooltip());
setSettingsNodes(new CountsViewSettingsPane().getChildrenUnmodifiable());
dateAxis.getTickMarks().addListener((Observable tickMarks) -> layoutDateLabels()); dateAxis.getTickMarks().addListener((Observable tickMarks) -> layoutDateLabels());
dateAxis.categorySpacingProperty().addListener((Observable spacing) -> layoutDateLabels()); dateAxis.categorySpacingProperty().addListener((Observable spacing) -> layoutDateLabels());
dateAxis.getCategories().addListener((Observable categories) -> layoutDateLabels()); dateAxis.getCategories().addListener((Observable categories) -> layoutDateLabels());
@ -167,6 +166,26 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
createSeries(); 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 * Set the appropriate label on the vertical axis, depending on the selected
* scale. * scale.

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-16 Basis Technology Corp. * Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.detailview; package org.sleuthkit.autopsy.timeline.ui.detailview;
import com.google.common.collect.ImmutableList;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
@ -29,6 +30,7 @@ import javafx.beans.Observable;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.scene.Node;
import javafx.scene.chart.Axis; import javafx.scene.chart.Axis;
import javafx.scene.control.Alert; import javafx.scene.control.Alert;
import javafx.scene.control.ButtonBar; 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.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
@ -112,7 +115,6 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
//initialize chart; //initialize chart;
setChart(new DetailsChart(controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis, getSelectedNodes())); setChart(new DetailsChart(controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis, getSelectedNodes()));
setSettingsNodes(new DetailViewSettingsPane(getChart().getLayoutSettings()).getChildrenUnmodifiable());
//bind layout fo axes and spacers //bind layout fo axes and spacers
detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels()); detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels());
@ -243,6 +245,26 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
return 0; 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 * A Pane that contains widgets to adjust settings specific to a
* DetailViewPane * DetailViewPane
@ -286,7 +308,7 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
DetailViewSettingsPane(DetailsChartLayoutSettings layoutSettings) { DetailViewSettingsPane(DetailsChartLayoutSettings layoutSettings) {
this.layoutSettings = layoutSettings; this.layoutSettings = layoutSettings;
FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); // NON-NLS FXMLConstructor.construct(DetailViewSettingsPane.this, "DetailViewSettingsPane.fxml"); //NON-NLS
} }
@NbBundle.Messages({ @NbBundle.Messages({
@ -300,11 +322,11 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
"DetailViewPane.hiddenRadio.text=Hide Description"}) "DetailViewPane.hiddenRadio.text=Hide Description"})
@FXML @FXML
void initialize() { void initialize() {
assert bandByTypeBox != null : "fx:id=\"bandByTypeBox\" 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 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 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 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 pinnedEventsToggle != null : "fx:id=\"pinnedEventsToggle\" was not injected: check your FXML file 'DetailViewSettings.fxml'."; //NON-NLS
//bind widgets to settings object properties //bind widgets to settings object properties
bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty()); bandByTypeBox.selectedProperty().bindBidirectional(layoutSettings.bandByTypeProperty());

View File

@ -1,19 +1,70 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?> <?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.Label?>
<?import javafx.scene.control.TableColumn?> <?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.TableView?> <?import javafx.scene.control.TableView?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.BorderPane?> <?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?> <?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"> <fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<top> <top>
<HBox alignment="CENTER" BorderPane.alignment="CENTER"> <HBox alignment="CENTER_RIGHT" spacing="5.0" BorderPane.alignment="CENTER">
<children> <children>
<Region HBox.hgrow="ALWAYS" />
<Label fx:id="eventCountLabel" text=" # of events" /> <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> </children>
<BorderPane.margin> <BorderPane.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />

View File

@ -19,20 +19,30 @@
package org.sleuthkit.autopsy.timeline.ui.listvew; package org.sleuthkit.autopsy.timeline.ui.listvew;
import com.google.common.collect.Iterables; 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.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.function.Function; import java.util.function.Function;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.binding.StringBinding; import javafx.beans.binding.StringBinding;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.value.ObservableValue; import javafx.beans.value.ObservableValue;
@ -40,8 +50,12 @@ import javafx.collections.FXCollections;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Pos; 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.ContextMenu;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.MenuItem; import javafx.scene.control.MenuItem;
import javafx.scene.control.OverrunStyle; import javafx.scene.control.OverrunStyle;
import javafx.scene.control.SelectionMode; import javafx.scene.control.SelectionMode;
@ -54,11 +68,14 @@ import javafx.scene.control.Tooltip;
import javafx.scene.image.Image; import javafx.scene.image.Image;
import javafx.scene.image.ImageView; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.util.Callback; import javafx.util.Callback;
import javax.swing.Action; import javax.swing.Action;
import javax.swing.JMenuItem; import javax.swing.JMenuItem;
import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.Notifications; import org.controlsfx.control.Notifications;
import org.controlsfx.control.action.ActionUtils;
import org.openide.awt.Actions; import org.openide.awt.Actions;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.actions.Presenter; 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 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 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 * 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 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 @FXML
private Label eventCountLabel; private Label eventCountLabel;
@ -118,6 +164,8 @@ class ListTimeline extends BorderPane {
*/ */
private final ObservableList<Long> selectedEventIDs = FXCollections.observableArrayList(); private final ObservableList<Long> selectedEventIDs = FXCollections.observableArrayList();
private final ConcurrentSkipListSet<CombinedEvent> visibleEvents;
private final TimeLineController controller; private final TimeLineController controller;
private final SleuthkitCase sleuthkitCase; private final SleuthkitCase sleuthkitCase;
private final TagsManager tagsManager; private final TagsManager tagsManager;
@ -128,10 +176,12 @@ class ListTimeline extends BorderPane {
* @param controller The controller for this timeline * @param controller The controller for this timeline
*/ */
ListTimeline(TimeLineController controller) { ListTimeline(TimeLineController controller) {
this.controller = controller; this.controller = controller;
sleuthkitCase = controller.getAutopsyCase().getSleuthkitCase(); sleuthkitCase = controller.getAutopsyCase().getSleuthkitCase();
tagsManager = controller.getAutopsyCase().getServices().getTagsManager(); tagsManager = controller.getAutopsyCase().getServices().getTagsManager();
FXMLConstructor.construct(this, ListTimeline.class, "ListTimeline.fxml"); //NON-NLS FXMLConstructor.construct(this, ListTimeline.class, "ListTimeline.fxml"); //NON-NLS
this.visibleEvents = new ConcurrentSkipListSet<>(Comparator.comparing(table.getItems()::indexOf));
} }
@FXML @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 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 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 //override default row with one that provides context menus
table.setRowFactory(tableView -> new EventRow()); table.setRowFactory(tableView -> new EventRow());
@ -248,6 +308,18 @@ class ListTimeline extends BorderPane {
table.requestFocus(); 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. * 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."}) "ListChart.errorMsg=There was a problem getting the content for the selected event."})
@Override @Override
protected void updateItem(CombinedEvent item, boolean empty) { protected void updateItem(CombinedEvent item, boolean empty) {
CombinedEvent oldItem = getItem();
if (oldItem != null) {
visibleEvents.remove(oldItem);
}
super.updateItem(item, empty); super.updateItem(item, empty);
if (empty || item == null) { if (empty || item == null) {
event = null; event = null;
} else { } else {
visibleEvents.add(item);
event = controller.getEventsModel().getEventById(item.getRepresentativeEventID()); event = controller.getEventsModel().getEventById(item.getRepresentativeEventID());
setOnContextMenuRequested(contextMenuEvent -> { 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));
}
}
} }

View File

@ -18,14 +18,17 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.listvew; package org.sleuthkit.autopsy.timeline.ui.listvew;
import com.google.common.collect.ImmutableList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.scene.Parent; import javafx.scene.Node;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.timeline.TimeLineController; 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.CombinedEvent;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView; import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
@ -48,7 +51,6 @@ public class ListViewPane extends AbstractTimeLineView {
//initialize chart; //initialize chart;
setCenter(listTimeline); setCenter(listTimeline);
setSettingsNodes(new ListViewPane.ListViewSettingsPane().getChildrenUnmodifiable());
//keep controller's list of selected event IDs in sync with this list's //keep controller's list of selected event IDs in sync with this list's
listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> { listTimeline.getSelectedEventIDs().addListener((Observable selectedIDs) -> {
@ -66,13 +68,34 @@ public class ListViewPane extends AbstractTimeLineView {
listTimeline.clear(); 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> { 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() { ListUpdateTask() {
super("List update task", true); super(Bundle.ListViewPane_loggedTask_name(), true);
} }
@Override @Override
@ -91,10 +114,10 @@ public class ListViewPane extends AbstractTimeLineView {
resetView(eventsModel.getTimeRange()); resetView(eventsModel.getTimeRange());
//get the combined events to be displayed //get the combined events to be displayed
updateMessage("Querying DB for events"); updateMessage(Bundle.ListViewPane_loggedTask_queryDb());
List<CombinedEvent> combinedEvents = eventsModel.getCombinedEvents(); List<CombinedEvent> combinedEvents = eventsModel.getCombinedEvents();
updateMessage("Updating UI"); updateMessage(Bundle.ListViewPane_loggedTask_updateUI());
Platform.runLater(() -> { Platform.runLater(() -> {
//put the combined events into the table. //put the combined events into the table.
listTimeline.setCombinedEvents(combinedEvents); listTimeline.setCombinedEvents(combinedEvents);

View File

@ -31,7 +31,7 @@ import org.openide.util.Lookup;
*/ */
@OptionsPanelController.TopLevelRegistration( @OptionsPanelController.TopLevelRegistration(
categoryName = "#OptionsCategory_Name_Options", 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", keywords = "#OptionsCategory_Keywords_Options",
keywordsCategory = "Options", keywordsCategory = "Options",
position = 10 position = 10

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB