Merge remote-tracking branch 'upstream/release-4.12.0' into 5035-for-PR

This commit is contained in:
Joe Ho 2019-06-14 11:59:32 -04:00
commit 557fc43ee0
126 changed files with 6855 additions and 2248 deletions

View File

@ -45,6 +45,9 @@
<dependency conf="core->default" org="commons-validator" name="commons-validator" rev="1.6"/>
<dependency conf="core->default" org="net.htmlparser.jericho" name="jericho-html" rev="3.3"/>
<dependency org="com.squareup.okhttp" name="okhttp" rev="2.7.5"/>
<!-- https://mvnrepository.com/artifact/javax.ws.rs/javax.ws.rs-api -->
<dependency org="javax.ws.rs" name="javax.ws.rs-api" rev="2.0"/>
<override org="jakarta.ws.rs" module="jakarta.ws.rs-api" rev="2.1.5"/>
</dependencies>
</ivy-module>

View File

@ -111,6 +111,10 @@ file.reference.grpc-context-1.19.0.jar=release/modules/ext/grpc-context-1.19.0.j
file.reference.opencensus-api-0.19.2.jar=release/modules/ext/opencensus-api-0.19.2.jar
file.reference.opencensus-contrib-http-util-0.19.2.jar=release/modules/ext/opencensus-contrib-http-util-0.19.2.jar
file.reference.threetenbp-1.3.3.jar=release/modules/ext/threetenbp-1.3.3.jar
file.reference.okhttp-2.7.5-javadoc.jar=release/modules/ext/okhttp-2.7.5-javadoc.jar
file.reference.okhttp-2.7.5-sources.jar=release/modules/ext/okhttp-2.7.5-sources.jar
file.reference.okhttp-2.7.5.jar=release/modules/ext/okhttp-2.7.5.jar
file.reference.okio-1.6.0.jar=release/modules/ext/okio-1.6.0.jar
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
license.file=../LICENSE-2.0.txt

View File

@ -773,6 +773,14 @@
<runtime-relative-path>ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/google-api-services-translate-v2-rev20170525-1.27.0.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/okhttp-2.7.5.jar</runtime-relative-path>
<binary-origin>release/modules/ext/okhttp-2.7.5.jar</binary-origin>
</class-path-extension>
<class-path-extension>
<runtime-relative-path>ext/okio-1.6.0.jar</runtime-relative-path>
<binary-origin>release/modules/ext/okio-1.6.0.jar</binary-origin>
</class-path-extension>
</data>
</configuration>
</project>

View File

@ -238,13 +238,7 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
LogicalImagerPanel.jLabel1.text=Insert external drive
LogicalImagerPanel.scanButton.text=Scan
LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder
LogicalImagerPanel.browseButton.text=Browse
LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results
LogicalImagerPanel.selectDriveLabel.text=Select Drive
LogicalImagerPanel.messageLabel.text=Error/Status message
UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module.
UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the
UnpackagePortableCaseDialog.exitButton.text=Exit
@ -259,4 +253,13 @@ UnpackagePortableCaseProgressDialog.cancelButton.text=Cancel
UnpackagePortableCaseProgressDialog.okButton.text=OK
UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel
UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to:
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
LogicalImagerPanel.importRadioButton.text=Import From External Drive
LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder
LogicalImagerPanel.importRadioButton.toolTipText=
LogicalImagerPanel.pathTextField.text=
LogicalImagerPanel.selectFolderLabel.text=Selected Folder:
LogicalImagerPanel.refreshButton.text=Refresh
LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive
LogicalImagerPanel.selectDriveLabel.text=Select Drive
LogicalImagerPanel.messageTextArea.text=

View File

@ -191,7 +191,6 @@ LogicalImagerDSProcessor.failToCreateDirectory=Failed to create directory {0}
LogicalImagerDSProcessor.imageDirPathNotFound={0} not found.\nUSB drive has been ejected.
LogicalImagerPanel.imageTable.columnModel.title0=Hostname
LogicalImagerPanel.imageTable.columnModel.title1=Extracted Date
LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images
# {0} - sparseImageDirectory
# {1} - image
LogicalImagerPanel.messageLabel.directoryDoesNotContainSparseImage=Directory {0} does not contain {1}
@ -201,7 +200,6 @@ LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images
LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found
LogicalImagerPanel.messageLabel.noImageSelected=No image selected
LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...
LogicalImagerPanel.messageLabel.selectedImage=Selected folder
LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive
Menu/Case/OpenRecentCase=Open Recent Case
CTL_CaseDeleteAction=Delete Case
@ -475,13 +473,7 @@ OpenMultiUserCasePanel.cancelButton.text=Cancel
OpenMultiUserCasePanel.openSingleUserCaseButton.text=Open Single-User Case...
OpenMultiUserCasePanel.openSelectedCaseButton.text=Open Selected Case
OpenMultiUserCasePanel.searchLabel.text=Select any case and start typing to search by case name
LogicalImagerPanel.jLabel1.text=Insert external drive
LogicalImagerPanel.scanButton.text=Scan
LogicalImagerPanel.jLabel6.text=Or, pick a Logical Imager folder
LogicalImagerPanel.browseButton.text=Browse
LogicalImagerPanel.topLabel.text=Import Autopsy Imager Results
LogicalImagerPanel.selectDriveLabel.text=Select Drive
LogicalImagerPanel.messageLabel.text=Error/Status message
UnpackagePortableCaseDialog.desc2Label.text=Portable Case Report Module.
UnpackagePortableCaseDialog.desc1Label.text=Unpackage a portable case so it can be opened in Autopsy. Portable cases are created through the
UnpackagePortableCaseDialog.exitButton.text=Exit
@ -497,3 +489,12 @@ UnpackagePortableCaseProgressDialog.okButton.text=OK
UnpackagePortableCaseProgressDialog.resultLabel.text=resultLabel
UnpackagePortableCaseDialog.extractLabel.text=Folder to extract to:
UnpackagePortableCaseDialog.caseLabel.text=Portable Case:
LogicalImagerPanel.importRadioButton.text=Import From External Drive
LogicalImagerPanel.manualRadioButton.text=Manually Choose Folder
LogicalImagerPanel.importRadioButton.toolTipText=
LogicalImagerPanel.pathTextField.text=
LogicalImagerPanel.selectFolderLabel.text=Selected Folder:
LogicalImagerPanel.refreshButton.text=Refresh
LogicalImagerPanel.selectFromDriveLabel.text=Select Acquisition From Drive
LogicalImagerPanel.selectDriveLabel.text=Select Drive
LogicalImagerPanel.messageTextArea.text=

View File

@ -1,6 +1,10 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.6" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="buttonGroup1">
</Component>
</NonVisualComponents>
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 65]"/>
@ -25,118 +29,146 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="238" max="-2" attributes="0"/>
<Component id="topLabel" min="-2" pref="163" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="messageLabel" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="jSeparator1" alignment="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="selectDriveLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="289" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="scanButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="126" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="selectFolderLabel" min="-2" pref="81" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
<Component id="pathTextField" min="-2" pref="474" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="jSeparator2" alignment="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="41" pref="41" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="driveListScrollPane" alignment="0" min="-2" pref="160" max="-2" attributes="0"/>
<Component id="refreshButton" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="imageScrollPane" min="-2" pref="377" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="20" pref="20" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="manualRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="36" max="-2" attributes="0"/>
<Component id="browseButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="driveListScrollPane" min="-2" pref="211" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="28" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="selectAcquisitionFromDriveLabel" min="-2" pref="305" max="-2" attributes="0"/>
<Component id="imageScrollPane" min="-2" pref="346" max="-2" attributes="0"/>
<Component id="importRadioButton" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="21" pref="21" max="-2" attributes="0"/>
<Component id="selectDriveLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="113" max="-2" attributes="0"/>
<Component id="selectFromDriveLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="346" max="-2" attributes="0"/>
<Component id="jLabel6" min="-2" pref="154" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="144" max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" pref="116" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace pref="48" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="568" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="14" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="topLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel6" alignment="3" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="16" max="-2" attributes="0"/>
<Component id="importRadioButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="selectDriveLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="selectFromDriveLabel" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="scanButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="jSeparator1" min="-2" pref="4" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="selectDriveLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="selectAcquisitionFromDriveLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="imageScrollPane" pref="0" max="32767" attributes="0"/>
<Component id="driveListScrollPane" pref="194" max="32767" attributes="0"/>
<Component id="driveListScrollPane" pref="186" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
<Component id="messageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="154" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="browseButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="manualRadioButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="separate" min="-2" max="-2" attributes="0"/>
<Component id="jSeparator2" min="-2" pref="16" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="selectFolderLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="pathTextField" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="jScrollPane1" min="-2" pref="61" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="topLabel">
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.topLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Component class="javax.swing.JRadioButton" name="importRadioButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.jLabel1.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup1"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="scanButton">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.scanButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.importRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.importRadioButton.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="scanButtonActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="importRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="messageLabel">
<Component class="javax.swing.JRadioButton" name="manualRadioButton">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="buttonGroup1"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.manualRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="manualRadioButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JTextField" name="pathTextField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.messageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.pathTextField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="disabledTextColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" id="black" palette="1" red="0" type="palette"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="selectFolderLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.selectFolderLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
@ -147,6 +179,13 @@
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="selectFromDriveLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.selectFromDriveLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JScrollPane" name="driveListScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
@ -162,7 +201,7 @@
<Property name="selectionMode" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="driveListMouseClicked"/>
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="driveListMouseReleased"/>
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="driveListKeyReleased"/>
</Events>
<AuxValues>
@ -171,28 +210,14 @@
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="selectAcquisitionFromDriveLabel">
<Component class="javax.swing.JButton" name="refreshButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.selectAcquisitionFromDriveLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel6">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.jLabel6.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="browseButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.browseButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseButtonActionPerformed"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="refreshButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="imageScrollPane">
@ -224,13 +249,56 @@
<Property name="updateSelectionOnSort" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="imageTableMouseClicked"/>
<EventHandler event="mouseReleased" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="imageTableMouseReleased"/>
<EventHandler event="keyReleased" listener="java.awt.event.KeyListener" parameters="java.awt.event.KeyEvent" handler="imageTableKeyReleased"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JSeparator" name="jSeparator1">
<Component class="javax.swing.JSeparator" name="jSeparator2">
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTextArea" name="messageTextArea">
<Properties>
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/>
</Property>
<Property name="columns" type="int" value="20"/>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
</Property>
<Property name="lineWrap" type="boolean" value="true"/>
<Property name="rows" type="int" value="3"/>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/casemodule/Bundle.properties" key="LogicalImagerPanel.messageTextArea.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="disabledTextColor" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" id="red" palette="1" red="ff" type="palette"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 0, 0, 0]"/>
</Property>
</Properties>
<BindingProperties>
<BindingProperty name="editable" source="messageTextArea" sourcePath="false" target="messageTextArea" targetPath="editable" updateStrategy="0" immediately="false"/>
</BindingProperties>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.casemodule;
import java.awt.Color;
import java.awt.Component;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileStore;
@ -46,7 +47,6 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataSourceProcessor;
* select a file.
*/
@Messages({
"LogicalImagerPanel.messageLabel.selectedImage=Selected folder",
"LogicalImagerPanel.messageLabel.noImageSelected=No image selected",
"LogicalImagerPanel.messageLabel.driveHasNoImages=Drive has no images",
"LogicalImagerPanel.selectAcquisitionFromDriveLabel.text=Select acquisition from Drive",})
@ -55,7 +55,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
private static final long serialVersionUID = 1L;
private static final String SPARSE_IMAGE_VHD = "sparse_image.vhd"; //NON-NLS
private static final String SELECTED_IMAGE = Bundle.LogicalImagerPanel_messageLabel_selectedImage();
private static final String NO_IMAGE_SELECTED = Bundle.LogicalImagerPanel_messageLabel_noImageSelected();
private static final String DRIVE_HAS_NO_IMAGES = Bundle.LogicalImagerPanel_messageLabel_driveHasNoImages();
private static final String[] EMPTY_LIST_DATA = {};
@ -75,6 +74,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
private LogicalImagerPanel(String context) {
this.contextName = context;
initComponents();
jScrollPane1.setBorder(null);
clearImageTable();
}
@ -86,13 +86,9 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
*
* @return instance of the LogicalImagerPanel
*/
@Messages({
"LogicalImagerPanel.messageLabel.clickScanOrBrowse=Click SCAN or BROWSE button to find images"
})
public static synchronized LogicalImagerPanel createInstance(String context) {
LogicalImagerPanel instance = new LogicalImagerPanel(context);
// post-constructor initialization of listener support without leaking references of uninitialized objects
instance.messageLabel.setText(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse());
instance.imageTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
return instance;
}
@ -104,43 +100,68 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
bindingGroup = new org.jdesktop.beansbinding.BindingGroup();
topLabel = new javax.swing.JLabel();
jLabel1 = new javax.swing.JLabel();
scanButton = new javax.swing.JButton();
messageLabel = new javax.swing.JLabel();
buttonGroup1 = new javax.swing.ButtonGroup();
browseButton = new javax.swing.JButton();
importRadioButton = new javax.swing.JRadioButton();
manualRadioButton = new javax.swing.JRadioButton();
pathTextField = new javax.swing.JTextField();
selectFolderLabel = new javax.swing.JLabel();
selectDriveLabel = new javax.swing.JLabel();
selectFromDriveLabel = new javax.swing.JLabel();
driveListScrollPane = new javax.swing.JScrollPane();
driveList = new javax.swing.JList<>();
selectAcquisitionFromDriveLabel = new javax.swing.JLabel();
jLabel6 = new javax.swing.JLabel();
browseButton = new javax.swing.JButton();
refreshButton = new javax.swing.JButton();
imageScrollPane = new javax.swing.JScrollPane();
imageTable = new javax.swing.JTable();
jSeparator1 = new javax.swing.JSeparator();
jSeparator2 = new javax.swing.JSeparator();
jScrollPane1 = new javax.swing.JScrollPane();
messageTextArea = new javax.swing.JTextArea();
setMinimumSize(new java.awt.Dimension(0, 65));
setPreferredSize(new java.awt.Dimension(403, 65));
org.openide.awt.Mnemonics.setLocalizedText(topLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.topLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel1, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel1.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(scanButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.scanButton.text")); // NOI18N
scanButton.addActionListener(new java.awt.event.ActionListener() {
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.browseButton.text")); // NOI18N
browseButton.setEnabled(false);
browseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
scanButtonActionPerformed(evt);
browseButtonActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(messageLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageLabel.text")); // NOI18N
buttonGroup1.add(importRadioButton);
importRadioButton.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(importRadioButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.text")); // NOI18N
importRadioButton.setToolTipText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.importRadioButton.toolTipText")); // NOI18N
importRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
importRadioButtonActionPerformed(evt);
}
});
buttonGroup1.add(manualRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(manualRadioButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.manualRadioButton.text")); // NOI18N
manualRadioButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
manualRadioButtonActionPerformed(evt);
}
});
pathTextField.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.pathTextField.text")); // NOI18N
pathTextField.setDisabledTextColor(java.awt.Color.black);
pathTextField.setEnabled(false);
org.openide.awt.Mnemonics.setLocalizedText(selectFolderLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectFolderLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(selectDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectDriveLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(selectFromDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectFromDriveLabel.text")); // NOI18N
driveList.setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
driveList.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
driveListMouseClicked(evt);
public void mouseReleased(java.awt.event.MouseEvent evt) {
driveListMouseReleased(evt);
}
});
driveList.addKeyListener(new java.awt.event.KeyAdapter() {
@ -150,14 +171,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
});
driveListScrollPane.setViewportView(driveList);
org.openide.awt.Mnemonics.setLocalizedText(selectAcquisitionFromDriveLabel, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.selectAcquisitionFromDriveLabel.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(jLabel6, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.jLabel6.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(browseButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.browseButton.text")); // NOI18N
browseButton.addActionListener(new java.awt.event.ActionListener() {
org.openide.awt.Mnemonics.setLocalizedText(refreshButton, org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.refreshButton.text")); // NOI18N
refreshButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
browseButtonActionPerformed(evt);
refreshButtonActionPerformed(evt);
}
});
@ -178,8 +195,8 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
imageTable.getTableHeader().setReorderingAllowed(false);
imageTable.setUpdateSelectionOnSort(false);
imageTable.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
imageTableMouseClicked(evt);
public void mouseReleased(java.awt.event.MouseEvent evt) {
imageTableMouseReleased(evt);
}
});
imageTable.addKeyListener(new java.awt.event.KeyAdapter() {
@ -190,72 +207,94 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
imageScrollPane.setViewportView(imageTable);
imageTable.getColumnModel().getSelectionModel().setSelectionMode(javax.swing.ListSelectionModel.SINGLE_SELECTION);
jScrollPane1.setBorder(null);
messageTextArea.setBackground(new java.awt.Color(240, 240, 240));
messageTextArea.setColumns(20);
messageTextArea.setForeground(java.awt.Color.red);
messageTextArea.setLineWrap(true);
messageTextArea.setRows(3);
messageTextArea.setText(org.openide.util.NbBundle.getMessage(LogicalImagerPanel.class, "LogicalImagerPanel.messageTextArea.text")); // NOI18N
messageTextArea.setBorder(null);
messageTextArea.setDisabledTextColor(java.awt.Color.red);
messageTextArea.setEnabled(false);
messageTextArea.setMargin(new java.awt.Insets(0, 0, 0, 0));
org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, messageTextArea, org.jdesktop.beansbinding.ELProperty.create("false"), messageTextArea, org.jdesktop.beansbinding.BeanProperty.create("editable"));
bindingGroup.addBinding(binding);
jScrollPane1.setViewportView(messageTextArea);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(238, 238, 238)
.addComponent(topLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 163, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGap(28, 28, 28)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(messageLabel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(selectDriveLabel)
.addGap(289, 289, 289))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(scanButton)
.addGap(126, 126, 126)))
.addGap(36, 36, 36)
.addComponent(browseButton))
.addGroup(layout.createSequentialGroup()
.addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(28, 28, 28)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(selectAcquisitionFromDriveLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 305, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 346, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(layout.createSequentialGroup()
.addGap(346, 346, 346)
.addComponent(jLabel6, javax.swing.GroupLayout.PREFERRED_SIZE, 154, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGap(144, 144, 144)
.addComponent(jLabel1, javax.swing.GroupLayout.PREFERRED_SIZE, 116, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap(48, Short.MAX_VALUE))))
.addGap(10, 10, 10)
.addComponent(selectFolderLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 81, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(13, 13, 13)
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 474, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(41, 41, 41)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(driveListScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 160, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(refreshButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 377, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGap(20, 20, 20)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(manualRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(browseButton))
.addComponent(importRadioButton)
.addGroup(layout.createSequentialGroup()
.addGap(21, 21, 21)
.addComponent(selectDriveLabel)
.addGap(113, 113, 113)
.addComponent(selectFromDriveLabel))))))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 568, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(14, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(topLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(jLabel6))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(scanButton)
.addComponent(browseButton))
.addGap(16, 16, 16)
.addComponent(importRadioButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 4, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(selectDriveLabel)
.addComponent(selectAcquisitionFromDriveLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(selectFromDriveLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(imageScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 194, Short.MAX_VALUE))
.addGap(26, 26, 26)
.addComponent(messageLabel)
.addGap(154, 154, 154))
.addComponent(driveListScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 186, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(refreshButton)
.addGap(18, 18, 18)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(browseButton)
.addComponent(manualRadioButton))
.addGap(18, 18, 18)
.addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(selectFolderLabel)
.addComponent(pathTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 61, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(6, 6, 6))
);
bindingGroup.bind();
}// </editor-fold>//GEN-END:initComponents
public static String humanReadableByteCount(long bytes, boolean si) {
@ -268,47 +307,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
return String.format("%.1f %sB", bytes / Math.pow(unit, exp), pre); //NON-NLS
}
@Messages({
"LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...",
"LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found"
})
private void scanButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_scanButtonActionPerformed
// Scan external drives for sparse_image.vhd
clearImageTable();
setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives());
List<String> listData = new ArrayList<>();
File[] roots = File.listRoots();
int firstRemovableDrive = -1;
int i = 0;
for (File root : roots) {
String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
long spaceInBytes = root.getTotalSpace();
String sizeWithUnit = humanReadableByteCount(spaceInBytes, false);
listData.add(root + " (" + description + ") (" + sizeWithUnit + ")");
if (firstRemovableDrive == -1) {
try {
FileStore fileStore = Files.getFileStore(root.toPath());
if ((boolean) fileStore.getAttribute("volume:isRemovable")) { //NON-NLS
firstRemovableDrive = i;
}
} catch (IOException ex) {
; // skip
}
}
i++;
}
driveList.setListData(listData.toArray(new String[0]));
if (!listData.isEmpty()) {
// auto-select the first external drive, if any
driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
driveListMouseClicked(null);
driveList.requestFocusInWindow();
} else {
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_noExternalDriveFound());
}
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
}//GEN-LAST:event_scanButtonActionPerformed
@Messages({
"# {0} - sparseImageDirectory",
"# {1} - image",
@ -333,7 +331,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
return;
}
choosenImageDirPath = Paths.get(path);
setNormalMessage(SELECTED_IMAGE + " " + path);
setNormalMessage(path);
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
} else {
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_directoryFormatInvalid(path));
@ -348,7 +346,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
int index = imageTable.getSelectedRow();
if (index != -1) {
choosenImageDirPath = Paths.get((String) imageTableModel.getValueAt(index, 2));
setNormalMessage(SELECTED_IMAGE + " " + choosenImageDirPath.toString());
setNormalMessage(choosenImageDirPath.toString());
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), false, true);
} else {
choosenImageDirPath = null;
@ -357,10 +355,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
}
}
private void imageTableMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseClicked
imageTableSelect();
}//GEN-LAST:event_imageTableMouseClicked
private void driveListSelect() {
String selectedStr = driveList.getSelectedValue();
if (selectedStr == null) {
@ -398,7 +392,7 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
}
}
}
selectAcquisitionFromDriveLabel.setText(Bundle.LogicalImagerPanel_selectAcquisitionFromDriveLabel_text()
selectFromDriveLabel.setText(Bundle.LogicalImagerPanel_selectAcquisitionFromDriveLabel_text()
+ " " + driveLetter);
imageTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
imageTable.setModel(imageTableModel);
@ -411,6 +405,10 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
choosenImageDirPath = null;
setErrorMessage(DRIVE_HAS_NO_IMAGES);
}
} else {
clearImageTable();
choosenImageDirPath = null;
setErrorMessage(DRIVE_HAS_NO_IMAGES);
}
}
@ -421,13 +419,14 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
}
private void setErrorMessage(String msg) {
messageLabel.setForeground(Color.red);
messageLabel.setText(msg);
messageTextArea.setForeground(Color.red);
messageTextArea.setText(msg);
pathTextField.setText("");
}
private void setNormalMessage(String msg) {
messageLabel.setForeground(Color.black);
messageLabel.setText(msg);
pathTextField.setText(msg);
messageTextArea.setText("");
}
private void clearImageTable() {
@ -436,43 +435,138 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
fixImageTableColumnWidth();
}
private void driveListMouseClicked(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseClicked
driveListSelect();
private void toggleMouseAndKeyListeners(Component component, boolean isEnable) {
component.setEnabled(isEnable);
}
private void manualRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_manualRadioButtonActionPerformed
browseButton.setEnabled(true);
// disable import panel
toggleMouseAndKeyListeners(driveList, false);
toggleMouseAndKeyListeners(driveListScrollPane, false);
toggleMouseAndKeyListeners(imageScrollPane, false);
toggleMouseAndKeyListeners(imageTable, false);
refreshButton.setEnabled(false);
choosenImageDirPath = null;
setNormalMessage("");
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
}//GEN-LAST:event_driveListMouseClicked
}//GEN-LAST:event_manualRadioButtonActionPerformed
private void importRadioButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importRadioButtonActionPerformed
browseButton.setEnabled(false);
toggleMouseAndKeyListeners(driveList, true);
toggleMouseAndKeyListeners(driveListScrollPane, true);
toggleMouseAndKeyListeners(imageScrollPane, true);
toggleMouseAndKeyListeners(imageTable, true);
refreshButton.setEnabled(true);
choosenImageDirPath = null;
setNormalMessage("");
refreshButton.doClick();
}//GEN-LAST:event_importRadioButtonActionPerformed
@Messages({
"LogicalImagerPanel.messageLabel.scanningExternalDrives=Scanning external drives for sparse_image.vhd ...",
"LogicalImagerPanel.messageLabel.noExternalDriveFound=No drive found"
})
private void refreshButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_refreshButtonActionPerformed
// Scan external drives for sparse_image.vhd
clearImageTable();
setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_scanningExternalDrives());
List<String> listData = new ArrayList<>();
File[] roots = File.listRoots();
int firstRemovableDrive = -1;
int i = 0;
for (File root : roots) {
String description = FileSystemView.getFileSystemView().getSystemTypeDescription(root);
long spaceInBytes = root.getTotalSpace();
String sizeWithUnit = humanReadableByteCount(spaceInBytes, false);
listData.add(root + " (" + description + ") (" + sizeWithUnit + ")");
if (firstRemovableDrive == -1) {
try {
FileStore fileStore = Files.getFileStore(root.toPath());
if ((boolean) fileStore.getAttribute("volume:isRemovable")) { //NON-NLS
firstRemovableDrive = i;
}
} catch (IOException ex) {
; // skip
}
}
i++;
}
driveList.setListData(listData.toArray(new String[0]));
if (!listData.isEmpty()) {
// auto-select the first external drive, if any
driveList.setSelectedIndex(firstRemovableDrive == -1 ? 0 : firstRemovableDrive);
driveListMouseReleased(null);
driveList.requestFocusInWindow();
} else {
setErrorMessage(Bundle.LogicalImagerPanel_messageLabel_noExternalDriveFound());
}
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
}//GEN-LAST:event_refreshButtonActionPerformed
private void driveListKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_driveListKeyReleased
driveListSelect();
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
if (importRadioButton.isSelected()) {
driveListSelect();
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
}
}//GEN-LAST:event_driveListKeyReleased
private void imageTableKeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_imageTableKeyReleased
imageTableSelect();
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
if (importRadioButton.isSelected()) {
imageTableSelect();
}
}//GEN-LAST:event_imageTableKeyReleased
private void imageTableMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_imageTableMouseReleased
if (importRadioButton.isSelected()) {
imageTableSelect();
}
}//GEN-LAST:event_imageTableMouseReleased
private void driveListMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_driveListMouseReleased
if (importRadioButton.isSelected()) {
driveListSelect();
firePropertyChange(DataSourceProcessor.DSP_PANEL_EVENT.UPDATE_UI.toString(), true, false);
}
}//GEN-LAST:event_driveListMouseReleased
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton browseButton;
private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.JList<String> driveList;
private javax.swing.JScrollPane driveListScrollPane;
private javax.swing.JScrollPane imageScrollPane;
private javax.swing.JTable imageTable;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel6;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JLabel messageLabel;
private javax.swing.JButton scanButton;
private javax.swing.JLabel selectAcquisitionFromDriveLabel;
private javax.swing.JRadioButton importRadioButton;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JSeparator jSeparator2;
private javax.swing.JRadioButton manualRadioButton;
private javax.swing.JTextArea messageTextArea;
private javax.swing.JTextField pathTextField;
private javax.swing.JButton refreshButton;
private javax.swing.JLabel selectDriveLabel;
private javax.swing.JLabel topLabel;
private javax.swing.JLabel selectFolderLabel;
private javax.swing.JLabel selectFromDriveLabel;
private org.jdesktop.beansbinding.BindingGroup bindingGroup;
// End of variables declaration//GEN-END:variables
public void reset() {
//reset the UI elements to default
choosenImageDirPath = null;
setNormalMessage("");
driveList.setListData(EMPTY_LIST_DATA);
clearImageTable();
setNormalMessage(Bundle.LogicalImagerPanel_messageLabel_clickScanOrBrowse());
if (importRadioButton.isSelected()) {
refreshButton.doClick();
}
}
/**
@ -488,10 +582,6 @@ public class LogicalImagerPanel extends JPanel implements DocumentListener {
return choosenImageDirPath;
}
public void setMessageLabel(String message) {
messageLabel.setText(message);
}
@Override
public void insertUpdate(DocumentEvent e) {
}

View File

@ -88,7 +88,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
* @return caseName the case name from the case name text field
*/
String getCaseName() {
return this.caseNameTextField.getText();
return this.caseNameTextField.getText().trim();
}
/**
@ -109,7 +109,7 @@ final class NewCaseVisualPanel1 extends JPanel implements DocumentListener {
* @return baseDirectory the base directory from the case dir text field
*/
String getCaseParentDir() {
String parentDir = this.caseParentDirTextField.getText();
String parentDir = this.caseParentDirTextField.getText().trim();
if (parentDir.endsWith(File.separator) == false) {
parentDir = parentDir + File.separator;
}

View File

@ -66,6 +66,7 @@ DataSourceSummaryDialog.window.title=Data Sources Summary
DataSourceSummaryNode.column.dataSourceName.header=Data Source Name
DataSourceSummaryNode.column.files.header=Files
DataSourceSummaryNode.column.results.header=Results
DataSourceSummaryNode.column.status.header=Ingest Status
DataSourceSummaryNode.column.tags.header=Tags
DataSourceSummaryNode.column.type.header=Type
DataSourceSummaryNode.viewDataSourceAction.text=Go to Data Source

View File

@ -37,8 +37,10 @@ import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.DataSourceSummaryNode.DataSourceSummaryEntryNode;
import static javax.swing.SwingConstants.RIGHT;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TskCoreException;
@ -50,9 +52,10 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(DataSourceBrowser.class.getName());
private static final int COUNT_COLUMN_WIDTH = 25;
private static final int USAGE_COLUMN_WIDTH = 120;
private static final int DATA_SOURCE_COLUMN_WIDTH = 325;
private static final int COUNT_COLUMN_WIDTH = 20;
private static final int INGEST_STATUS_WIDTH = 50;
private static final int USAGE_COLUMN_WIDTH = 110;
private static final int DATA_SOURCE_COLUMN_WIDTH = 280;
private final Outline outline;
private final org.openide.explorer.view.OutlineView outlineView;
private final ExplorerManager explorerManager;
@ -69,6 +72,7 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
outlineView = new org.openide.explorer.view.OutlineView();
this.setVisible(true);
outlineView.setPropertyColumns(
Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(),
Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(),
Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(),
Bundle.DataSourceSummaryNode_column_results_header(), Bundle.DataSourceSummaryNode_column_results_header(),
@ -90,6 +94,8 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
column.setPreferredWidth(COUNT_COLUMN_WIDTH);
} else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_type_header())) {
column.setPreferredWidth(USAGE_COLUMN_WIDTH);
} else if (column.getHeaderValue().toString().equals(Bundle.DataSourceSummaryNode_column_status_header())) {
column.setPreferredWidth(INGEST_STATUS_WIDTH);
} else {
column.setPreferredWidth(DATA_SOURCE_COLUMN_WIDTH);
}
@ -182,6 +188,47 @@ final class DataSourceBrowser extends javax.swing.JPanel implements ExplorerMana
return null;
}
/**
* Update the DataSourceBrowser to display up to date status information for
* the data sources.
*
* @param dataSourceId the ID of the data source which should be updated
* @param newStatus the new status which the data source should have
*/
void refresh(long dataSourceId, IngestJobInfo.IngestJobStatusType newStatus) {
//attempt to update the status of any datasources that had status which was STARTED
for (DataSourceSummary summary : dataSourceSummaryList) {
if (summary.getDataSource().getId() == dataSourceId) {
summary.setIngestStatus(newStatus);
}
}
//figure out which nodes were previously selected
Node[] selectedNodes = explorerManager.getSelectedNodes();
SwingUtilities.invokeLater(() -> {
explorerManager.setRootContext(new DataSourceSummaryNode(dataSourceSummaryList));
List<Node> nodesToSelect = new ArrayList<>();
for (Node node : explorerManager.getRootContext().getChildren().getNodes()) {
if (node instanceof DataSourceSummaryEntryNode) {
//there should only be one selected node as multi-select is disabled
for (Node selectedNode : selectedNodes) {
if (((DataSourceSummaryEntryNode) node).getDataSource().equals(((DataSourceSummaryEntryNode) selectedNode).getDataSource())) {
nodesToSelect.add(node);
}
}
}
}
//reselect the previously selected Nodes
try {
explorerManager.setSelectedNodes(nodesToSelect.toArray(new Node[nodesToSelect.size()]));
} catch (PropertyVetoException ex) {
logger.log(Level.WARNING, "Error selecting previously selected nodes", ex);
}
});
}
/**
* 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

View File

@ -18,15 +18,27 @@
*/
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.CaseDbAccessManager;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo.IngestJobStatusType;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Wrapper object for a DataSource and the information associated with it.
*
*/
class DataSourceSummary {
private static final Logger logger = Logger.getLogger(DataSourceSummary.class.getName());
private static final String INGEST_JOB_STATUS_QUERY = "status_id FROM ingest_jobs WHERE obj_id=";
private final DataSource dataSource;
private IngestJobStatusType status = null;
private final String type;
private final long filesCount;
private final long resultsCount;
@ -45,12 +57,26 @@ class DataSourceSummary {
*/
DataSourceSummary(DataSource dSource, String typeValue, Long numberOfFiles, Long numberOfResults, Long numberOfTags) {
dataSource = dSource;
getStatusFromDatabase();
type = typeValue == null ? "" : typeValue;
filesCount = numberOfFiles == null ? 0 : numberOfFiles;
resultsCount = numberOfResults == null ? 0 : numberOfResults;
tagsCount = numberOfTags == null ? 0 : numberOfTags;
}
/**
* Get the status of the ingest job from the case database
*/
private void getStatusFromDatabase() {
try {
IngestJobQueryCallback callback = new IngestJobQueryCallback();
Case.getCurrentCaseThrows().getSleuthkitCase().getCaseDbAccessManager().select(INGEST_JOB_STATUS_QUERY + dataSource.getId(), callback);
status = callback.getStatus();
} catch (NoCurrentCaseException | TskCoreException ex) {
logger.log(Level.WARNING, "Error getting status for data source from case database", ex);
}
}
/**
* Get the DataSource
*
@ -60,6 +86,16 @@ class DataSourceSummary {
return dataSource;
}
/**
* Manually set the ingest job status
*
* @param ingestStatus the status which the ingest job should have
* currently, null to display empty string
*/
void setIngestStatus(IngestJobStatusType ingestStatus) {
status = ingestStatus;
}
/**
* Get the type of this DataSource
*
@ -87,6 +123,16 @@ class DataSourceSummary {
return resultsCount;
}
/**
* Get the IngestJobStatusType associated with this data source.
*
* @return the IngestJobStatusType associated with this data source. Can be
* null if the IngestJobStatusType is not STARTED or COMPLETED.
*/
IngestJobStatusType getIngestStatus() {
return status;
}
/**
* Get the number of tagged content objects in this DataSource
*
@ -95,4 +141,40 @@ class DataSourceSummary {
long getTagsCount() {
return tagsCount;
}
/**
* Callback to parse result set, getting the status to be associated with
* this data source
*/
class IngestJobQueryCallback implements CaseDbAccessManager.CaseDbAccessQueryCallback {
private IngestJobStatusType jobStatus = null;
@Override
public void process(ResultSet rs) {
try {
while (rs.next()) {
IngestJobStatusType currentStatus = IngestJobStatusType.fromID(rs.getInt("status_id"));
if (currentStatus == IngestJobStatusType.COMPLETED) {
jobStatus = currentStatus;
} else if (currentStatus == IngestJobStatusType.STARTED) {
jobStatus = currentStatus;
return;
}
}
} catch (SQLException ex) {
logger.log(Level.WARNING, "Error getting status for ingest job", ex);
}
}
/**
* Get the status which was determined for this callback
*
* @return the status of the data source which was queried for
*/
IngestJobStatusType getStatus() {
return jobStatus;
}
}
}

View File

@ -19,14 +19,18 @@
package org.sleuthkit.autopsy.casemodule.datasourcesummary;
import java.awt.Frame;
import java.beans.PropertyChangeEvent;
import java.util.Map;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.Logger;
import javax.swing.event.ListSelectionEvent;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.IngestJobInfoPanel;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent;
import org.sleuthkit.autopsy.ingest.events.DataSourceAnalysisCompletedEvent.Reason;
import org.sleuthkit.datamodel.DataSource;
import org.sleuthkit.datamodel.IngestJobInfo;
/**
* Dialog for displaying the Data Sources Summary information
@ -38,7 +42,6 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
private final DataSourceSummaryDetailsPanel detailsPanel;
private final DataSourceBrowser dataSourcesPanel;
private final IngestJobInfoPanel ingestHistoryPanel;
private static final Logger logger = Logger.getLogger(DataSourceSummaryDialog.class.getName());
/**
* Creates new form DataSourceSummaryDialog for displaying a summary of the
@ -73,6 +76,17 @@ final class DataSourceSummaryDialog extends javax.swing.JDialog implements Obser
this.repaint();
}
});
//add listener to refresh jobs with Started status when they complete
IngestManager.getInstance().addIngestJobEventListener((PropertyChangeEvent evt) -> {
if (evt instanceof DataSourceAnalysisCompletedEvent) {
DataSourceAnalysisCompletedEvent dsEvent = (DataSourceAnalysisCompletedEvent) evt;
if (dsEvent.getResult() == Reason.ANALYSIS_COMPLETED) {
dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), IngestJobInfo.IngestJobStatusType.COMPLETED);
} else if (dsEvent.getResult() == Reason.ANALYSIS_CANCELLED) {
dataSourcesPanel.refresh(dsEvent.getDataSource().getId(), null);
}
}
});
this.pack();
}

View File

@ -109,6 +109,7 @@ final class DataSourceSummaryNode extends AbstractNode {
static final class DataSourceSummaryEntryNode extends AbstractNode {
private final DataSource dataSource;
private final String status;
private final String type;
private final long filesCount;
private final long resultsCount;
@ -124,6 +125,7 @@ final class DataSourceSummaryNode extends AbstractNode {
DataSourceSummaryEntryNode(DataSourceSummary dataSourceSummary) {
super(Children.LEAF);
dataSource = dataSourceSummary.getDataSource();
status = dataSourceSummary.getIngestStatus() == null ? "" : dataSourceSummary.getIngestStatus().getDisplayName();
type = dataSourceSummary.getType();
filesCount = dataSourceSummary.getFilesCount();
resultsCount = dataSourceSummary.getResultsCount();
@ -143,6 +145,7 @@ final class DataSourceSummaryNode extends AbstractNode {
}
@Messages({"DataSourceSummaryNode.column.dataSourceName.header=Data Source Name",
"DataSourceSummaryNode.column.status.header=Ingest Status",
"DataSourceSummaryNode.column.type.header=Type",
"DataSourceSummaryNode.column.files.header=Files",
"DataSourceSummaryNode.column.results.header=Results",
@ -157,6 +160,7 @@ final class DataSourceSummaryNode extends AbstractNode {
}
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(), Bundle.DataSourceSummaryNode_column_dataSourceName_header(),
dataSource));
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), Bundle.DataSourceSummaryNode_column_status_header(), status));
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(), Bundle.DataSourceSummaryNode_column_type_header(),
type));
sheetSet.put(new NodeProperty<>(Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(), Bundle.DataSourceSummaryNode_column_files_header(),

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.casemodule.services;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -30,15 +31,18 @@ import java.util.logging.Level;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifactTag;
import org.sleuthkit.datamodel.CaseDbAccessManager;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskData.DbType;
/**
* A per case Autopsy service that manages the addition of content and artifact
@ -48,6 +52,32 @@ public class TagsManager implements Closeable {
private static final Logger LOGGER = Logger.getLogger(TagsManager.class.getName());
private final SleuthkitCase caseDb;
static {
//Create the contentviewer tags table (beta) if the current case does not
//have the table present
Case.addEventTypeSubscriber(EnumSet.of(Case.Events.CURRENT_CASE), evt -> {
if (evt.getNewValue() != null) {
Case currentCase = (Case) evt.getNewValue();
try {
CaseDbAccessManager caseDb = currentCase.getSleuthkitCase().getCaseDbAccessManager();
if (caseDb.tableExists(ContentViewerTagManager.TABLE_NAME)) {
return;
}
if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.SQLITE)) {
caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_SQLITE);
} else if (currentCase.getSleuthkitCase().getDatabaseType().equals(DbType.POSTGRESQL)) {
caseDb.createTable(ContentViewerTagManager.TABLE_NAME, ContentViewerTagManager.TABLE_SCHEMA_POSTGRESQL);
}
} catch (TskCoreException ex) {
LOGGER.log(Level.SEVERE,
String.format("Unable to create the %s table for image tag storage.",
ContentViewerTagManager.TABLE_NAME), ex);
}
}
});
}
/**
* Tests whether or not a given tag display name contains an illegal

View File

@ -0,0 +1,276 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.casemodule.services.contentviewertags;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
/**
* A per case Autopsy service that manages the addition of content viewer tags
* to the case database. This manager is also responsible for serializing and
* deserializing instances of your tag data objects for persistence and
* retrieval.
*/
public class ContentViewerTagManager {
//Used to convert Java beans into the physical representation that will be stored
//in the database.
private static final ObjectMapper SERIALIZER = new ObjectMapper();
public static final String TABLE_NAME = "beta_tag_app_data";
public static final String TABLE_SCHEMA_SQLITE = "(app_data_id INTEGER PRIMARY KEY, "
+ "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, "
+ "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))";
public static final String TABLE_SCHEMA_POSTGRESQL = "(app_data_id BIGSERIAL PRIMARY KEY, "
+ "content_tag_id INTEGER NOT NULL, app_data TEXT NOT NULL, "
+ "FOREIGN KEY(content_tag_id) REFERENCES content_tags(tag_id))";
private static final String INSERT_TAG_DATA = "(content_tag_id, app_data) VALUES (%d, '%s')";
private static final String UPDATE_TAG_DATA = "SET content_tag_id = %d, app_data = '%s' WHERE app_data_id = %d";
private static final String SELECT_TAG_DATA = "* FROM " + TABLE_NAME + " WHERE content_tag_id = %d";
private static final String DELETE_TAG_DATA = "WHERE app_data_id = %d";
/**
* Creates and saves a new ContentViewerTag in the case database. The
* generic tag data instance T will be automatically serialized into a
* storable format.
*
* @param <T> Generic class type that will be serialized into a storable
* format for persistence.
* @param contentTag ContentTag that this ContentViewerTag is associated
* with (1:1).
* @param tagDataBean Data instance that contains the tag information to be
* persisted.
* @return An instance of a ContentViewerTag of type T, which contains all
* the stored information.
*
* @throws SerializationException Thrown if the tag data instance T could
* not be serialized into a storable format.
* @throws TskCoreException Thrown if this operation did not successfully
* persist in the case database.
* @throws NoCurrentCaseException Thrown if invocation of this method occurs
* when no case is open.
*/
public static <T> ContentViewerTag<T> saveTag(ContentTag contentTag, T tagDataBean)
throws SerializationException, TskCoreException, NoCurrentCaseException {
try {
long contentTagId = contentTag.getId();
String serialAppData = SERIALIZER.writeValueAsString(tagDataBean);
String insertTemplateInstance = String.format(INSERT_TAG_DATA,
contentTagId, serialAppData);
long insertId = Case.getCurrentCaseThrows()
.getSleuthkitCase()
.getCaseDbAccessManager()
.insert(TABLE_NAME, insertTemplateInstance);
return new ContentViewerTag<>(insertId, contentTag, tagDataBean);
} catch (JsonProcessingException ex) {
throw new SerializationException("Unable to convert object instance into a storable format", ex);
}
}
/**
* Updates the ContentViewerTag instance with the new tag data T and
* persists the changes to the case database.
*
* @param <T> Generic class type that will be serialized into a storable
* format.
* @param oldTag ContentViewerTag instance to be updated
* @param tagDataBean Data instance that contains the updated information to
* be persisted.
*
* @throws SerializationException Thrown if the tag data instance T could
* not be serialized into a storable format.
* @throws TskCoreException Thrown if this operation did not successfully
* persist in the case database.
* @throws NoCurrentCaseException Thrown if invocation of this method occurs
* when no case is open.
*/
public static <T> ContentViewerTag<T> updateTag(ContentViewerTag<T> oldTag, T tagDataBean)
throws SerializationException, TskCoreException, NoCurrentCaseException {
try {
String serialAppData = SERIALIZER.writeValueAsString(tagDataBean);
String updateTemplateInstance = String.format(UPDATE_TAG_DATA,
oldTag.getContentTag().getId(), serialAppData, oldTag.getId());
Case.getCurrentCaseThrows()
.getSleuthkitCase()
.getCaseDbAccessManager()
.update(TABLE_NAME, updateTemplateInstance);
return new ContentViewerTag<>(oldTag.getId(), oldTag.getContentTag(), tagDataBean);
} catch (JsonProcessingException ex) {
throw new SerializationException("Unable to convert object instance into a storable format", ex);
}
}
/**
* Retrieves a ContentViewerTag instance that is associated with the
* specified ContentTag. The Java class T that represents the technical
* details of the tag should be passed so that automatic binding can take
* place.
*
* @param <T> Generic class type that will be instantiated and filled in
* with data.
* @param contentTag ContentTag that this ContentViewerTag is associated
* with (1:1)
* @param clazz Generic class that will be instantiated and filled in with
* data.
* @return ContentViewerTag with an instance of T as a member variable or
* null if the content tag does not have an associated ContentViewerTag of
* type T.
*
* @throws TskCoreException Thrown if this operation did not successfully
* persist in the case database.
* @throws NoCurrentCaseException Thrown if invocation of this method occurs
* when no case is open.
*/
public static <T> ContentViewerTag<T> getTag(ContentTag contentTag, Class<T> clazz) throws TskCoreException, NoCurrentCaseException {
String selectTemplateInstance = String.format(SELECT_TAG_DATA, contentTag.getId());
final ResultWrapper<ContentViewerTag<T>> result = new ResultWrapper<>();
Case.getCurrentCaseThrows()
.getSleuthkitCase()
.getCaseDbAccessManager()
.select(selectTemplateInstance, (ResultSet rs) -> {
try {
if (rs.next()) {
long tagId = rs.getLong(1);
String appDetails = rs.getString(3);
try {
T instance = SERIALIZER.readValue(appDetails, clazz);
result.setResult(new ContentViewerTag<>(tagId, contentTag, instance));
} catch (IOException ex) {
//Databind for type T failed. Not a system error
//but rather a logic error on the part of the caller.
result.setResult(null);
}
}
} catch (SQLException ex) {
result.setException(ex);
}
});
if (result.hasException()) {
throw new TskCoreException("Unable to select tag from case db", result.getException());
}
return result.getResult();
}
/**
* Wrapper for holding state in the CaseDbAccessQueryCallback.
* CaseDbAccessQueryCallback has no support for exception handling.
*
* @param <T>
*/
private static class ResultWrapper<T> {
private T result;
private SQLException ex = null;
public void setResult(T result) {
this.result = result;
}
public void setException(SQLException ex) {
this.ex = ex;
}
public boolean hasException() {
return this.ex != null;
}
public SQLException getException() {
return ex;
}
public T getResult() {
return result;
}
}
/**
* Deletes the content viewer tag with the specified id.
*
* @param contentViewerTag ContentViewerTag to delete
* @throws TskCoreException Thrown if this operation did not successfully
* persist in the case database.
* @throws NoCurrentCaseException Thrown if invocation of this method occurs
* when no case is open.
*/
public static <T> void deleteTag(ContentViewerTag<T> contentViewerTag) throws TskCoreException, NoCurrentCaseException {
String deleteTemplateInstance = String.format(DELETE_TAG_DATA, contentViewerTag.getId());
Case.getCurrentCaseThrows()
.getSleuthkitCase()
.getCaseDbAccessManager()
.delete(TABLE_NAME, deleteTemplateInstance);
}
/**
* This class represents a stored tag in the case database. It is a wrapper
* for the tag id, the attached Content tag object, and the Java bean
* instance that describes the technical details for reconstructing the tag.
*
* @param <T> Generic class type that will be instantiated and filled in
* with data.
*/
public static class ContentViewerTag<T> {
private final long id;
private final ContentTag contentTag;
private final T details;
private ContentViewerTag(long id, ContentTag contentTag, T details) {
this.id = id;
this.contentTag = contentTag;
this.details = details;
}
public long getId() {
return id;
}
public ContentTag getContentTag() {
return contentTag;
}
public T getDetails() {
return details;
}
}
/**
* System exception thrown in the event that class instance T could not be
* properly serialized.
*/
public static class SerializationException extends Exception {
public SerializationException(String message, Exception source) {
super(message, source);
}
}
//Prevent this class from being instantiated.
private ContentViewerTagManager() {
}
}

View File

@ -75,6 +75,10 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
public AccountsBrowser() {
initComponents();
jSplitPane1.setResizeWeight(0.5);
jSplitPane1.setDividerLocation(0.75);
outline = outlineView.getOutline();
outlineView.setPropertyColumns(
"device", Bundle.AccountNode_device(),
@ -118,7 +122,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro
final int rows = Math.min(100, outline.getRowCount());
for (int column = 0; column < outline.getModel().getColumnCount(); column++) {
for (int column = 0; column < outline.getColumnCount(); column++) {
int columnWidthLimit = 500;
int columnWidth = 0;

View File

@ -11,34 +11,10 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="filtersPane" min="-2" pref="265" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="browseVisualizeTabPane" pref="786" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="filtersPane" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="browseVisualizeTabPane" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="browseVisualizeTabPane">
<Properties>
@ -51,6 +27,11 @@
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</AccessibilityProperties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="0" insetsBottom="15" insetsRight="15" anchor="18" weightX="0.75" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
<SubComponents>
@ -85,11 +66,11 @@
</SubComponents>
</Container>
<Component class="org.sleuthkit.autopsy.communications.FiltersPanel" name="filtersPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[256, 495]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="15" insetsBottom="15" insetsRight="5" anchor="18" weightX="0.25" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -22,12 +22,13 @@ import com.google.common.eventbus.Subscribe;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.List;
import java.util.stream.Collectors;
import javax.swing.GroupLayout;
import javax.swing.ImageIcon;
import javax.swing.JTabbedPane;
import javax.swing.LayoutStyle;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.windows.Mode;
@ -93,39 +94,39 @@ public final class CVTTopComponent extends TopComponent {
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
GridBagConstraints gridBagConstraints;
browseVisualizeTabPane = new JTabbedPane();
accountsBrowser = new AccountsBrowser();
vizPanel = new VisualizationPanel();
filtersPane = new FiltersPanel();
setLayout(new GridBagLayout());
browseVisualizeTabPane.setFont(new Font("Tahoma", 0, 18)); // NOI18N
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N
browseVisualizeTabPane.addTab(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N
filtersPane.setMinimumSize(new Dimension(256, 495));
GroupLayout layout = new GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(filtersPane, GroupLayout.PREFERRED_SIZE, 265, GroupLayout.PREFERRED_SIZE)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(browseVisualizeTabPane, GroupLayout.PREFERRED_SIZE, 786, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(filtersPane, GroupLayout.DEFAULT_SIZE, GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGap(5, 5, 5))
.addGroup(GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(browseVisualizeTabPane)
.addContainerGap())
);
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 0.75;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new Insets(15, 0, 15, 15);
add(browseVisualizeTabPane, gridBagConstraints);
browseVisualizeTabPane.getAccessibleContext().setAccessibleName(NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N
gridBagConstraints = new GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagConstraints.anchor = GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 0.25;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new Insets(15, 15, 15, 5);
add(filtersPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -11,493 +11,487 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="18"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,2,-54,0,0,1,-40"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="devicesPane" max="32767" attributes="0"/>
<Component id="accountTypesPane" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="filtersTitleLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="applyFiltersButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="refreshButton" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="dateRangePane" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="needsRefreshLabel" max="32767" attributes="0"/>
</Group>
<Component id="limitPane" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="filtersTitleLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="applyFiltersButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="refreshButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="needsRefreshLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="4" max="-2" attributes="0"/>
<Component id="devicesPane" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountTypesPane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="dateRangePane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="limitPane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="applyFiltersButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/tick.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.applyFiltersButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="null"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="filtersTitleLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/funnel.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.filtersTitleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="accountTypesPane">
<Container class="javax.swing.JPanel" name="topPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="24" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Component id="accountTypesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="accountTypeRequiredLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="unCheckAllAccountTypesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="checkAllAccountTypesButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="accountTypesScrollPane" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="accountTypesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="accountTypeRequiredLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="accountTypesScrollPane" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="checkAllAccountTypesButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="unCheckAllAccountTypesButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="unCheckAllAccountTypesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllAccountTypesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="accountTypesLabel">
<Component class="javax.swing.JLabel" name="filtersTitleLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/accounts.png"/>
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/funnel.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.accountTypesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.filtersTitleLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="checkAllAccountTypesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllAccountTypesButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="accountTypesScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2, 200]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="accountTypeListPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="accountTypeRequiredLabel">
<Component class="javax.swing.JButton" name="refreshButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.accountTypeRequiredLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="applyFiltersButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/tick.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.applyFiltersButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="needsRefreshLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.needsRefreshLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="2"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="devicesPane">
<Container class="javax.swing.JScrollPane" name="scrollPane">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="devicesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="deviceRequiredLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="unCheckAllDevicesButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="checkAllDevicesButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Component id="devicesScrollPane" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="devicesLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="deviceRequiredLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="devicesScrollPane" pref="94" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="checkAllDevicesButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="unCheckAllDevicesButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="5" pref="5" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="unCheckAllDevicesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllDevicesButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="devicesLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/image.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.devicesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="checkAllDevicesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllDevicesButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="devicesScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 75]"/>
</Property>
</Properties>
<Container class="javax.swing.JPanel" name="mainPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="devicesListPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[4, 100]"/>
</Property>
</Properties>
<Container class="javax.swing.JPanel" name="limitPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="0" insetsBottom="15" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="mostRecentLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.mostRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="9" insetsBottom="0" insetsRight="9" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JComboBox" name="limitComboBox">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="6">
<StringItem index="0" value="All"/>
<StringItem index="1" value="10000"/>
<StringItem index="2" value="5000"/>
<StringItem index="3" value="1000"/>
<StringItem index="4" value="500"/>
<StringItem index="5" value="100"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="limitComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JPanel" name="limitTitlePanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="9" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="limitHeaderLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.limitHeaderLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="limitErrorMsgLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.limitErrorMsgLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="deviceRequiredLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.deviceRequiredLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="2"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="dateRangePane">
<Container class="javax.swing.JPanel" name="dateRangePane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="endCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="endDatePicker" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="startCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="startDatePicker" min="-2" max="-2" attributes="0"/>
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="endCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="endDatePicker" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="startCheckBox" linkSize="1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="startDatePicker" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="startDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="startCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="endDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="endCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="dateRangeLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="startDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="startCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="endDatePicker" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="endCheckBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="startDatePicker">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="dateRangeLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/calendar.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.dateRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="startCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.startCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="startCheckBoxStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="endCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.endCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="endCheckBoxStateChanged"/>
</Events>
</Component>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="endDatePicker">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="refreshButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.refreshButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="needsRefreshLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.needsRefreshLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="limitPane">
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="startDatePicker">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="dateRangeLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/calendar.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.dateRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="startCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.startCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="startCheckBoxStateChanged"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="endCheckBox">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.endCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="endCheckBoxStateChanged"/>
</Events>
</Component>
<Component class="com.github.lgooddatepicker.components.DatePicker" name="endDatePicker">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="devicesPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="100" insetsTop="15" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="limitHeaderLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="limitErrorMsgLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="mostRecentLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="limitComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="limitHeaderLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="limitErrorMsgLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="mostRecentLabel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="limitComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="32" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="limitHeaderLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.limitHeaderLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="mostRecentLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.mostRecentLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="limitComboBox">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="6">
<StringItem index="0" value="All"/>
<StringItem index="1" value="10000"/>
<StringItem index="2" value="5000"/>
<StringItem index="3" value="1000"/>
<StringItem index="4" value="500"/>
<StringItem index="5" value="100"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="limitComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="limitErrorMsgLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.limitErrorMsgLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="10"/>
</Properties>
</Component>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="unCheckAllDevicesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllDevicesButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="12" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="devicesLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/image.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.devicesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="9" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="checkAllDevicesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllDevicesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllDevicesButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JScrollPane" name="devicesScrollPane">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="31"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 75]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="devicesListPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[4, 100]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="deviceRequiredLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.deviceRequiredLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="2"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="9" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="accountTypesPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="15" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="unCheckAllAccountTypesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.unCheckAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="unCheckAllAccountTypesButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="12" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="accountTypesLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/accounts.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.accountTypesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="checkAllAccountTypesButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.checkAllAccountTypesButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="checkAllAccountTypesButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Container class="javax.swing.JScrollPane" name="accountTypesScrollPane">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[2, 200]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="9" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="accountTypeListPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="accountTypeRequiredLabel">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/images/error-icon-16.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/communications/Bundle.properties" key="FiltersPanel.accountTypeRequiredLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="0" green="0" red="ff" type="rgb"/>
</Property>
<Property name="horizontalTextPosition" type="int" value="2"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="12" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>
</Container>
</SubComponents>

View File

@ -394,135 +394,123 @@ final public class FiltersPanel extends JPanel {
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N
applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N
applyFiltersButton.setPreferredSize(null);
setLayout(new java.awt.GridBagLayout());
topPane.setLayout(new java.awt.GridBagLayout());
filtersTitleLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/funnel.png"))); // NOI18N
filtersTitleLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.filtersTitleLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
topPane.add(filtersTitleLabel, gridBagConstraints);
unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N
unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
topPane.add(refreshButton, gridBagConstraints);
applyFiltersButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/tick.png"))); // NOI18N
applyFiltersButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.applyFiltersButton.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
topPane.add(applyFiltersButton, gridBagConstraints);
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
topPane.add(needsRefreshLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_END;
gridBagConstraints.weightx = 1.0;
add(topPane, gridBagConstraints);
scrollPane.setBorder(null);
mainPanel.setLayout(new java.awt.GridBagLayout());
limitPane.setLayout(new java.awt.GridBagLayout());
mostRecentLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.mostRecentLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 9, 0, 9);
limitPane.add(mostRecentLabel, gridBagConstraints);
limitComboBox.setEditable(true);
limitComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "All", "10000", "5000", "1000", "500", "100" }));
limitComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
unCheckAllAccountTypesButtonActionPerformed(evt);
limitComboBoxActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
limitPane.add(limitComboBox, gridBagConstraints);
accountTypesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/accounts.png"))); // NOI18N
accountTypesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypesLabel.text")); // NOI18N
limitTitlePanel.setLayout(new java.awt.GridBagLayout());
checkAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllAccountTypesButton.text")); // NOI18N
checkAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkAllAccountTypesButtonActionPerformed(evt);
}
});
limitHeaderLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitHeaderLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
limitTitlePanel.add(limitHeaderLabel, gridBagConstraints);
accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200));
limitErrorMsgLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
limitErrorMsgLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitErrorMsgLabel.text")); // NOI18N
limitErrorMsgLabel.setForeground(new java.awt.Color(255, 0, 0));
limitErrorMsgLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
limitTitlePanel.add(limitErrorMsgLabel, gridBagConstraints);
accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS));
accountTypesScrollPane.setViewportView(accountTypeListPane);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0);
limitPane.add(limitTitlePanel, gridBagConstraints);
accountTypeRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
accountTypeRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypeRequiredLabel.text")); // NOI18N
accountTypeRequiredLabel.setForeground(new java.awt.Color(255, 0, 0));
accountTypeRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
javax.swing.GroupLayout accountTypesPaneLayout = new javax.swing.GroupLayout(accountTypesPane);
accountTypesPane.setLayout(accountTypesPaneLayout);
accountTypesPaneLayout.setHorizontalGroup(
accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, accountTypesPaneLayout.createSequentialGroup()
.addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(accountTypesPaneLayout.createSequentialGroup()
.addComponent(accountTypesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(accountTypeRequiredLabel))
.addGroup(accountTypesPaneLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(unCheckAllAccountTypesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(checkAllAccountTypesButton))
.addGroup(accountTypesPaneLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(accountTypesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addGap(0, 0, 0))
);
accountTypesPaneLayout.setVerticalGroup(
accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(accountTypesPaneLayout.createSequentialGroup()
.addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(accountTypesLabel)
.addComponent(accountTypeRequiredLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(accountTypesScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(accountTypesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(checkAllAccountTypesButton)
.addComponent(unCheckAllAccountTypesButton)))
);
unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N
unCheckAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
unCheckAllDevicesButtonActionPerformed(evt);
}
});
devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); // NOI18N
devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N
checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N
checkAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkAllDevicesButtonActionPerformed(evt);
}
});
devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 75));
devicesListPane.setMinimumSize(new java.awt.Dimension(4, 100));
devicesListPane.setLayout(new javax.swing.BoxLayout(devicesListPane, javax.swing.BoxLayout.Y_AXIS));
devicesScrollPane.setViewportView(devicesListPane);
deviceRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
deviceRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.deviceRequiredLabel.text")); // NOI18N
deviceRequiredLabel.setForeground(new java.awt.Color(255, 0, 0));
deviceRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
javax.swing.GroupLayout devicesPaneLayout = new javax.swing.GroupLayout(devicesPane);
devicesPane.setLayout(devicesPaneLayout);
devicesPaneLayout.setHorizontalGroup(
devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(devicesPaneLayout.createSequentialGroup()
.addComponent(devicesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(deviceRequiredLabel))
.addGroup(devicesPaneLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(unCheckAllDevicesButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(checkAllDevicesButton))
.addGroup(devicesPaneLayout.createSequentialGroup()
.addGap(10, 10, 10)
.addComponent(devicesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
devicesPaneLayout.setVerticalGroup(
devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(devicesPaneLayout.createSequentialGroup()
.addGroup(devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(devicesLabel)
.addComponent(deviceRequiredLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(devicesScrollPane, javax.swing.GroupLayout.DEFAULT_SIZE, 94, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(devicesPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(checkAllDevicesButton)
.addComponent(unCheckAllDevicesButton))
.addGap(5, 5, 5))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 0, 15, 0);
mainPanel.add(limitPane, gridBagConstraints);
startDatePicker.setEnabled(false);
@ -582,97 +570,177 @@ final public class FiltersPanel extends JPanel {
.addComponent(endCheckBox)))
);
refreshButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/arrow-circle-double-135.png"))); // NOI18N
refreshButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.refreshButton.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0);
mainPanel.add(dateRangePane, gridBagConstraints);
needsRefreshLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.needsRefreshLabel.text")); // NOI18N
needsRefreshLabel.setForeground(new java.awt.Color(255, 0, 0));
devicesPane.setLayout(new java.awt.GridBagLayout());
limitHeaderLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitHeaderLabel.text")); // NOI18N
mostRecentLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.mostRecentLabel.text")); // NOI18N
limitComboBox.setEditable(true);
limitComboBox.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "All", "10000", "5000", "1000", "500", "100" }));
limitComboBox.addActionListener(new java.awt.event.ActionListener() {
unCheckAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllDevicesButton.text")); // NOI18N
unCheckAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
limitComboBoxActionPerformed(evt);
unCheckAllDevicesButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 9);
devicesPane.add(unCheckAllDevicesButton, gridBagConstraints);
limitErrorMsgLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
limitErrorMsgLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.limitErrorMsgLabel.text")); // NOI18N
limitErrorMsgLabel.setForeground(new java.awt.Color(255, 0, 0));
limitErrorMsgLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEADING);
devicesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/image.png"))); // NOI18N
devicesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.devicesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0);
devicesPane.add(devicesLabel, gridBagConstraints);
javax.swing.GroupLayout limitPaneLayout = new javax.swing.GroupLayout(limitPane);
limitPane.setLayout(limitPaneLayout);
limitPaneLayout.setHorizontalGroup(
limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(limitPaneLayout.createSequentialGroup()
.addComponent(limitHeaderLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(limitErrorMsgLabel)
.addContainerGap())
.addGroup(limitPaneLayout.createSequentialGroup()
.addContainerGap()
.addComponent(mostRecentLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(limitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
limitPaneLayout.setVerticalGroup(
limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(limitPaneLayout.createSequentialGroup()
.addContainerGap()
.addGroup(limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(limitHeaderLabel)
.addComponent(limitErrorMsgLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(limitPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(mostRecentLabel)
.addComponent(limitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(0, 32, Short.MAX_VALUE))
);
checkAllDevicesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllDevicesButton.text")); // NOI18N
checkAllDevicesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkAllDevicesButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
devicesPane.add(checkAllDevicesButton, gridBagConstraints);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(devicesPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(accountTypesPane, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(filtersTitleLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(refreshButton))
.addComponent(dateRangePane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(needsRefreshLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(limitPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, 0)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(filtersTitleLabel)
.addComponent(applyFiltersButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(refreshButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(needsRefreshLabel)
.addGap(4, 4, 4)
.addComponent(devicesPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(accountTypesPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(dateRangePane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(limitPane, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
devicesScrollPane.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
devicesScrollPane.setMinimumSize(new java.awt.Dimension(27, 75));
devicesListPane.setMinimumSize(new java.awt.Dimension(4, 100));
devicesListPane.setLayout(new javax.swing.BoxLayout(devicesListPane, javax.swing.BoxLayout.Y_AXIS));
devicesScrollPane.setViewportView(devicesListPane);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
devicesPane.add(devicesScrollPane, gridBagConstraints);
deviceRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
deviceRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.deviceRequiredLabel.text")); // NOI18N
deviceRequiredLabel.setForeground(new java.awt.Color(255, 0, 0));
deviceRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 9, 0);
devicesPane.add(deviceRequiredLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.ipady = 100;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0);
mainPanel.add(devicesPane, gridBagConstraints);
accountTypesPane.setLayout(new java.awt.GridBagLayout());
unCheckAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.unCheckAllAccountTypesButton.text")); // NOI18N
unCheckAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
unCheckAllAccountTypesButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 9);
accountTypesPane.add(unCheckAllAccountTypesButton, gridBagConstraints);
accountTypesLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/accounts.png"))); // NOI18N
accountTypesLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
accountTypesPane.add(accountTypesLabel, gridBagConstraints);
checkAllAccountTypesButton.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.checkAllAccountTypesButton.text")); // NOI18N
checkAllAccountTypesButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
checkAllAccountTypesButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
accountTypesPane.add(checkAllAccountTypesButton, gridBagConstraints);
accountTypesScrollPane.setPreferredSize(new java.awt.Dimension(2, 200));
accountTypeListPane.setLayout(new javax.swing.BoxLayout(accountTypeListPane, javax.swing.BoxLayout.Y_AXIS));
accountTypesScrollPane.setViewportView(accountTypeListPane);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.gridwidth = 3;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(9, 0, 0, 0);
accountTypesPane.add(accountTypesScrollPane, gridBagConstraints);
accountTypeRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/images/error-icon-16.png"))); // NOI18N
accountTypeRequiredLabel.setText(org.openide.util.NbBundle.getMessage(FiltersPanel.class, "FiltersPanel.accountTypeRequiredLabel.text")); // NOI18N
accountTypeRequiredLabel.setForeground(new java.awt.Color(255, 0, 0));
accountTypeRequiredLabel.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridwidth = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHEAST;
accountTypesPane.add(accountTypeRequiredLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(15, 0, 0, 0);
mainPanel.add(accountTypesPane, gridBagConstraints);
scrollPane.setViewportView(mainPanel);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(scrollPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
/**
@ -933,11 +1001,15 @@ final public class FiltersPanel extends JPanel {
private final javax.swing.JLabel limitErrorMsgLabel = new javax.swing.JLabel();
private final javax.swing.JLabel limitHeaderLabel = new javax.swing.JLabel();
private final javax.swing.JPanel limitPane = new javax.swing.JPanel();
private final javax.swing.JPanel limitTitlePanel = new javax.swing.JPanel();
private final javax.swing.JPanel mainPanel = new javax.swing.JPanel();
private final javax.swing.JLabel mostRecentLabel = new javax.swing.JLabel();
private final javax.swing.JLabel needsRefreshLabel = new javax.swing.JLabel();
private final javax.swing.JButton refreshButton = new javax.swing.JButton();
private final javax.swing.JScrollPane scrollPane = new javax.swing.JScrollPane();
private final javax.swing.JCheckBox startCheckBox = new javax.swing.JCheckBox();
private final com.github.lgooddatepicker.components.DatePicker startDatePicker = new com.github.lgooddatepicker.components.DatePicker();
private final javax.swing.JPanel topPane = new javax.swing.JPanel();
private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton();
private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton();
// End of variables declaration//GEN-END:variables

View File

@ -44,3 +44,4 @@ SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Other O
SummaryViewer_Creation_Date_Title=Creation Date
SummaryViewer_FileRefNameColumn_Title=Path
SummaryViewer_TabTitle=Summary
SummeryViewer_FileRef_Message=<Select one Accout to see File References>

View File

@ -11,29 +11,38 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="contactPane" max="32767" attributes="0"/>
<Component id="outlineViewPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="outlineViewPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="contactPane" pref="332" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane" name="contactPane">
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
</Component>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="orientation" type="int" value="0"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="23" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane" name="contactPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -23,7 +23,6 @@ import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
@ -126,6 +125,9 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
updateOutlineViewPanel();
}
});
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
}
@Override
@ -183,29 +185,32 @@ public final class ContactsViewer extends JPanel implements RelationshipsViewer{
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
contactPane = new org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(outlineViewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(outlineViewPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(contactPane, javax.swing.GroupLayout.DEFAULT_SIZE, 332, Short.MAX_VALUE))
);
setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setRightComponent(contactPane);
splitPane.setLeftComponent(outlineViewPanel);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(splitPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.communications.relationships.ContactDetailsPane contactPane;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -11,58 +11,57 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="thumbnailViewer" alignment="1" max="32767" attributes="0"/>
<Component id="contentViewer" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="separator" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="thumbnailViewer" pref="350" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="separator" min="-2" pref="2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="contentViewer" min="-2" pref="450" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail" name="thumbnailViewer">
<Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 102]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[450, 400]"/>
</Property>
<Property name="orientation" type="int" value="0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM)"/>
</AuxValues>
</Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[450, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
</Component>
<Component class="javax.swing.JSeparator" name="separator">
</Component>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail" name="thumbnailViewer">
<Properties>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 102]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[450, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM)"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[450, 400]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="right"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -65,6 +65,11 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
* Creates new form ThumbnailViewer
*/
public MediaViewer() {
initComponents();
splitPane.setResizeWeight(0.5);
splitPane.setDividerLocation(0.5);
proxyLookup = new ModifiableProxyLookup(createLookup(tableEM, getActionMap()));
// See org.sleuthkit.autopsy.timeline.TimeLineTopComponent for a detailed
@ -87,8 +92,6 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
}
};
initComponents();
tableEM.addPropertyChangeListener((PropertyChangeEvent evt) -> {
if (evt.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) {
handleNodeSelectionChange();
@ -190,43 +193,37 @@ final class MediaViewer extends JPanel implements RelationshipsViewer, ExplorerM
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
thumbnailViewer = new org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail(tableEM);
contentViewer = new MessageDataContent();
separator = new javax.swing.JSeparator();
setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
thumbnailViewer.setMinimumSize(new java.awt.Dimension(350, 102));
thumbnailViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setLeftComponent(thumbnailViewer);
contentViewer.setPreferredSize(new java.awt.Dimension(450, 400));
splitPane.setRightComponent(contentViewer);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(thumbnailViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(contentViewer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(separator)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(thumbnailViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 350, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(separator, javax.swing.GroupLayout.PREFERRED_SIZE, 2, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(contentViewer, javax.swing.GroupLayout.PREFERRED_SIZE, 450, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(3, 3, 3))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(splitPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
private javax.swing.JSeparator separator;
private javax.swing.JSplitPane splitPane;
private org.sleuthkit.autopsy.corecomponents.DataResultViewerThumbnail thumbnailViewer;
// End of variables declaration//GEN-END:variables

View File

@ -18,13 +18,10 @@
*/
package org.sleuthkit.autopsy.communications.relationships;
import java.util.List;
import java.util.TimeZone;
import java.util.logging.Level;
import org.apache.commons.lang3.StringUtils;
import org.openide.nodes.Sheet;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.BlackboardArtifactNode;
@ -40,7 +37,6 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHO
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO;
import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_SUBJECT;
import static org.sleuthkit.datamodel.BlackboardAttribute.TSK_BLACKBOARD_ATTRIBUTE_VALUE_TYPE.DATETIME;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TimeUtilities;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.autopsy.communications.Utils;
@ -72,7 +68,6 @@ final class MessageNode extends BlackboardArtifactNode {
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
List<Tag> tags = getAllTagsFromDatabase();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@ -81,17 +76,6 @@ final class MessageNode extends BlackboardArtifactNode {
sheetSet.put(new NodeProperty<>("Type", Bundle.MessageNode_Node_Property_Type(), "", getDisplayName())); //NON-NLS
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
final BlackboardArtifact artifact = getArtifact();
BlackboardArtifact.ARTIFACT_TYPE fromID = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID());

View File

@ -11,32 +11,41 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,13,0,0,2,103"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="contentViewer" max="32767" attributes="0"/>
<Component id="outlineViewPanel" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="outlineViewPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="contentViewer" pref="390" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
</Component>
<Container class="javax.swing.JSplitPane" name="splitPane">
<Properties>
<Property name="orientation" type="int" value="0"/>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.MessageContentViewer" name="contentViewer">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new MessageDataContent()"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="bottom"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="outlineViewPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -23,7 +23,6 @@ import java.awt.KeyboardFocusManager;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import static javax.swing.SwingUtilities.isDescendingFrom;
import org.netbeans.swing.outline.DefaultOutlineModel;
import org.netbeans.swing.outline.Outline;
@ -68,6 +67,8 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
public MessagesViewer() {
initComponents();
splitPane.setResizeWeight(0.5);
outlineViewPanel.hideOutlineView(Bundle.MessageViewer_no_messages());
proxyLookup = new ModifiableProxyLookup(createLookup(outlineViewPanel.getExplorerManager(), getActionMap()));
@ -138,6 +139,8 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
updateOutlineViewPanel();
}
});
outlineViewPanel.setTableColumnsWidth(5,10,10,15,50,10);
}
@Override
@ -192,29 +195,32 @@ public final class MessagesViewer extends JPanel implements RelationshipsViewer
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
splitPane = new javax.swing.JSplitPane();
contentViewer = new MessageDataContent();
outlineViewPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(contentViewer, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(outlineViewPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(outlineViewPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(contentViewer, javax.swing.GroupLayout.DEFAULT_SIZE, 390, Short.MAX_VALUE))
);
setLayout(new java.awt.GridBagLayout());
splitPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitPane.setBottomComponent(contentViewer);
splitPane.setLeftComponent(outlineViewPanel);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(splitPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.MessageContentViewer contentViewer;
private org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel outlineViewPanel;
private javax.swing.JSplitPane splitPane;
// End of variables declaration//GEN-END:variables
}

View File

@ -1,6 +1,9 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
@ -21,6 +24,7 @@
<SubComponents>
<Component class="org.openide.explorer.view.OutlineView" name="outlineView">
<Properties>
<Property name="horizontalScrollBarPolicy" type="int" value="32"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 400]"/>
</Property>

View File

@ -19,7 +19,9 @@
package org.sleuthkit.autopsy.communications.relationships;
import java.awt.CardLayout;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableColumn;
import org.openide.explorer.ExplorerManager;
import static org.openide.explorer.ExplorerUtils.createLookup;
import org.openide.explorer.view.OutlineView;
@ -98,7 +100,20 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
super.setEnabled(enabled);
outlineView.setEnabled(enabled);
}
public void setTableColumnsWidth(double... percentages) {
JTable table = outlineView.getOutline();
double total = 0;
for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
total += percentages[i];
}
for (int i = 0; i < table.getColumnModel().getColumnCount(); i++) {
TableColumn column = table.getColumnModel().getColumn(i);
column.setPreferredWidth((int) (table.getPreferredSize().width * (percentages[i] / total)));
}
}
/**
* 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
@ -112,8 +127,10 @@ public class OutlineViewPanel extends javax.swing.JPanel implements ExplorerMana
messagePanel = new javax.swing.JPanel();
messageLabel = new javax.swing.JLabel();
setEnabled(false);
setLayout(new java.awt.CardLayout(5, 5));
outlineView.setHorizontalScrollBarPolicy(javax.swing.ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
outlineView.setPreferredSize(new java.awt.Dimension(300, 400));
add(outlineView, "outlineCard");

View File

@ -11,42 +11,22 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="1" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Component id="scrollPane" alignment="0" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JScrollPane" name="scrollPane">
<Properties>
<Property name="verticalScrollBarPolicy" type="int" value="21"/>
</Properties>
<Container class="javax.swing.JTabbedPane" name="tabPane">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="tabPaneStateChanged"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="1" insetsLeft="1" insetsBottom="1" insetsRight="1" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Container class="javax.swing.JTabbedPane" name="tabPane">
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="tabPaneStateChanged"/>
</Events>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
</Container>
</SubComponents>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -54,9 +54,7 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
tabPane.add(summaryViewer.getDisplayName(), summaryViewer);
tabPane.add(messagesViewer.getDisplayName(), messagesViewer);
tabPane.add(contactsViewer.getDisplayName(), contactsViewer);
tabPane.add(mediaViewer.getDisplayName(), mediaViewer);
tabPane.add(mediaViewer.getDisplayName(), mediaViewer);
}
/**
@ -78,33 +76,26 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
scrollPane = new javax.swing.JScrollPane();
tabPane = new javax.swing.JTabbedPane();
scrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_NEVER);
setLayout(new java.awt.GridBagLayout());
tabPane.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
tabPaneStateChanged(evt);
}
});
scrollPane.setViewportView(tabPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane, javax.swing.GroupLayout.Alignment.TRAILING))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollPane))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(1, 1, 1, 1);
add(tabPane, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents
private void tabPaneStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_tabPaneStateChanged
@ -121,7 +112,6 @@ public final class RelationshipBrowser extends JPanel implements Lookup.Provider
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JScrollPane scrollPane;
private javax.swing.JTabbedPane tabPane;
// End of variables declaration//GEN-END:variables

View File

@ -11,36 +11,10 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,3,70,0,0,4,-35"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="countsPanel" max="32767" attributes="0"/>
<Component id="fileReferencesPanel" alignment="0" pref="485" max="32767" attributes="0"/>
<Component id="caseReferencesPanel" alignment="1" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="countsPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="fileReferencesPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="caseReferencesPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="countsPanel">
<Properties>
@ -52,6 +26,11 @@
</Border>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
@ -183,7 +162,7 @@
</Component>
</SubComponents>
</Container>
<Component class="org.sleuthkit.autopsy.communications.OutlineViewPanel" name="fileReferencesPanel">
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="fileReferencesPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
@ -192,12 +171,14 @@
</TitledBorder>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[472, 300]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.sleuthkit.autopsy.communications.OutlineViewPanel" name="caseReferencesPanel">
<Component class="org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel" name="caseReferencesPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.TitledBorderInfo">
@ -206,10 +187,12 @@
</TitledBorder>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[472, 300]"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="18" weightX="1.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -46,7 +46,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
"SummaryViewer_FileRefNameColumn_Title=Path",
"SummaryViewer_CaseRefNameColumn_Title=Case Name",
"SummaryViewer_CentralRepository_Message=<Enable Central Resposity to see Other Occurrences>",
"SummaryViewer_Creation_Date_Title=Creation Date"
"SummaryViewer_Creation_Date_Title=Creation Date",
"SummeryViewer_FileRef_Message=<Select one Accout to see File References>",
})
/**
@ -70,6 +71,9 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.SummaryViewer_CaseRefNameColumn_Title());
clearControls();
caseReferencesPanel.hideOutlineView(Bundle.SummaryViewer_CentralRepository_Message());
fileReferencesPanel.hideOutlineView(Bundle.SummeryViewer_FileRef_Message());
}
@Override
@ -96,7 +100,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
if (info.getAccounts().size() != 1) {
setEnabled(false);
clearControls();
fileReferencesPanel.hideOutlineView(Bundle.SummeryViewer_FileRef_Message());
} else {
SelectionSummary summaryDetails = info.getSummary();
@ -105,6 +110,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
contactsDataLabel.setText(Integer.toString(summaryDetails.getContactsCnt()));
emailDataLabel.setText(Integer.toString(summaryDetails.getEmailCnt()));
messagesDataLabel.setText(Integer.toString(summaryDetails.getMessagesCnt()));
fileReferencesPanel.showOutlineView();
fileReferencesPanel.setNode(new AbstractNode(Children.create(new AccountSourceContentChildNodeFactory(info.getAccounts()), true)));
caseReferencesPanel.setNode(new AbstractNode(Children.create(new CorrelationCaseChildNodeFactory(info.getAccounts()), true)));
@ -178,6 +185,7 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
countsPanel = new javax.swing.JPanel();
emailLabel = new javax.swing.JLabel();
@ -193,6 +201,8 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
fileReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
caseReferencesPanel = new org.sleuthkit.autopsy.communications.relationships.OutlineViewPanel();
setLayout(new java.awt.GridBagLayout());
countsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.countsPanel.border.title"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(emailLabel, org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.emailLabel.text")); // NOI18N
@ -262,35 +272,33 @@ public class SummaryViewer extends javax.swing.JPanel implements RelationshipsVi
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
add(countsPanel, gridBagConstraints);
fileReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.fileReferencesPanel.border.title"))); // NOI18N
fileReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300));
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(fileReferencesPanel, gridBagConstraints);
caseReferencesPanel.setBorder(javax.swing.BorderFactory.createTitledBorder(org.openide.util.NbBundle.getMessage(SummaryViewer.class, "SummaryViewer.caseReferencesPanel.border.title"))); // NOI18N
caseReferencesPanel.setPreferredSize(new java.awt.Dimension(472, 300));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(countsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(fileReferencesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 485, Short.MAX_VALUE)
.addComponent(caseReferencesPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(countsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fileReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(caseReferencesPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 2;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.weighty = 1.0;
add(caseReferencesPanel, gridBagConstraints);
}// </editor-fold>//GEN-END:initComponents

View File

@ -89,3 +89,4 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
MediaPlayerPanel.playButton.text=\u25ba
MediaPlayerPanel.infoLabel.text=No Errors
MediaViewImagePanel.tagsMenu.text_1=Tags Menu

View File

@ -44,7 +44,11 @@ MediaPlayerPanel.timeFormat=%02d:%02d:%02d
MediaPlayerPanel.unknownTime=Unknown
MediaViewImagePanel.errorLabel.OOMText=Could not load file into Media View: insufficent memory.
MediaViewImagePanel.errorLabel.text=Could not load file into Media View.
MediaViewImagePanel.exportSaveText=Save
MediaViewImagePanel.externalViewerButton.text=Open in External Viewer Ctrl+E
MediaViewImagePanel.fileChooserTitle=Choose a save location
MediaViewImagePanel.successfulExport=Tagged image was successfully saved.
MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk.
MediaViewVideoPanel.pauseButton.text=\u25ba
MediaViewVideoPanel.progressLabel.text=00:00
MediaViewVideoPanel.infoLabel.text=info
@ -151,6 +155,7 @@ MediaPlayerPanel.VolumeIcon.text=\ \ \ \ \ Volume
MediaPlayerPanel.progressLabel.text=00:00:00/00:00:00
MediaPlayerPanel.playButton.text=\u25ba
MediaPlayerPanel.infoLabel.text=No Errors
MediaViewImagePanel.tagsMenu.text_1=Tags Menu
# {0} - tableName
SQLiteViewer.readTable.errorText=Error getting rows for table: {0}
# {0} - tableName

View File

@ -11,30 +11,8 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="htmlPanel" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="htmlPanel" max="32767" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.HtmlPanel" name="htmlPanel">
</Component>
</SubComponents>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Form>

View File

@ -37,17 +37,18 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer {
private static final long serialVersionUID = 1L;
private static final Logger logger = Logger.getLogger(HtmlViewer.class.getName());
private static final String[] SUPPORTED_MIMETYPES = new String[]{
"text/html",
"application/xhtml+xml"
};
private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
/**
* Creates new form HtmlViewerPanel
*/
HtmlViewer() {
initComponents();
this.add(htmlPanel);
}
/**
@ -81,31 +82,12 @@ final class HtmlViewer extends javax.swing.JPanel implements FileTypeViewer {
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addContainerGap())
);
setLayout(new java.awt.BorderLayout());
}// </editor-fold>//GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel;
// End of variables declaration//GEN-END:variables
@Override
public List<String> getSupportedMIMETypes() {
return Arrays.asList(SUPPORTED_MIMETYPES);

View File

@ -19,7 +19,7 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-112"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,100,0,0,3,117"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
@ -200,6 +200,44 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="zoomResetButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.Box$Filler" name="filler1">
</Component>
<Component class="javax.swing.Box$Filler" name="filler2">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[32767, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.HorizontalGlue"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="jPanel1">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JButton" name="tagsMenu">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/contentviewers/Bundle.properties" key="MediaViewImagePanel.tagsMenu.text_1" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[75, 21]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[75, 21]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[75, 21]"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="mousePressed" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="tagsMenuMousePressed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>

View File

@ -20,18 +20,31 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import static java.util.Objects.nonNull;
import java.util.SortedSet;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import java.util.stream.Collectors;
import javafx.application.Platform;
import javafx.collections.ListChangeListener.Change;
import javafx.concurrent.Task;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
@ -45,19 +58,39 @@ import javafx.scene.transform.Rotate;
import javafx.scene.transform.Scale;
import javafx.scene.transform.Translate;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javafx.scene.Node;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.SwingUtilities;
import javax.swing.SwingWorker;
import org.apache.commons.io.FilenameUtils;
import org.controlsfx.control.MaskerPane;
import org.openide.util.NbBundle;
import org.python.google.common.collect.Lists;
import javafx.scene.Group;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog;
import org.sleuthkit.autopsy.actions.GetTagNameAndCommentDialog.TagNameAndComment;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.SerializationException;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagControls;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagCreator;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsGroup;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.FileNode;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Image viewer part of the Media View layered pane. Uses JavaFX to display the
@ -70,17 +103,30 @@ import org.sleuthkit.datamodel.AbstractFile;
class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPanel {
private static final Image EXTERNAL = new Image(MediaViewImagePanel.class.getResource("/org/sleuthkit/autopsy/images/external.png").toExternalForm());
private final static Logger LOGGER = Logger.getLogger(MediaViewImagePanel.class.getName());
private final boolean fxInited;
private JFXPanel fxPanel;
private Group imageGroup;
private ImageTaggingTool tagger;
private AbstractFile file;
private Group masterGroup;
private ImageTagsGroup tagsGroup;
private ImageTagCreator imageTagCreator;
private ImageView fxImageView;
private ScrollPane scrollPane;
private final ProgressBar progressBar = new ProgressBar();
private final MaskerPane maskerPane = new MaskerPane();
private final JPopupMenu popupMenu = new JPopupMenu();
private final JMenuItem createTagMenuItem;
private final JMenuItem deleteTagMenuItem;
private final JMenuItem hideTagsMenuItem;
private final JMenuItem exportTagsMenuItem;
private final JFileChooser exportChooser;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private double zoomRatio;
private double rotation; // Can be 0, 90, 180, and 270.
@ -116,33 +162,163 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
public MediaViewImagePanel() {
initComponents();
fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited();
exportChooser = new JFileChooser();
exportChooser.setDialogTitle(Bundle.MediaViewImagePanel_fileChooserTitle());
//Build popupMenu when Tags Menu button is pressed.
createTagMenuItem = new JMenuItem("Create");
createTagMenuItem.addActionListener((event) -> createTag());
popupMenu.add(createTagMenuItem);
popupMenu.add(new JSeparator());
deleteTagMenuItem = new JMenuItem("Delete");
deleteTagMenuItem.addActionListener((event) -> deleteTag());
popupMenu.add(deleteTagMenuItem);
popupMenu.add(new JSeparator());
hideTagsMenuItem = new JMenuItem("Hide");
hideTagsMenuItem.addActionListener((event) -> showOrHideTags());
popupMenu.add(hideTagsMenuItem);
popupMenu.add(new JSeparator());
exportTagsMenuItem = new JMenuItem("Export");
exportTagsMenuItem.addActionListener((event) -> exportTags());
popupMenu.add(exportTagsMenuItem);
popupMenu.setPopupSize(300, 150);
if (fxInited) {
Platform.runLater(() -> {
Platform.runLater(new Runnable() {
@Override
public void run() {
// build jfx ui (we could do this in FXML?)
fxImageView = new ImageView(); // will hold image
masterGroup = new Group(fxImageView);
tagsGroup = new ImageTagsGroup(fxImageView);
tagsGroup.getChildren().addListener((Change<? extends Node> c) -> {
if (c.getList().isEmpty()) {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.EMPTY));
}
});
// build jfx ui (we could do this in FXML?)
fxImageView = new ImageView(); // will hold image
imageGroup = new Group();
imageGroup.getChildren().add(fxImageView);
scrollPane = new ScrollPane(imageGroup); // scrolls and sizes imageview
scrollPane.getStyleClass().add("bg"); //NOI18N
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
subscribeTagMenuItemsToStateChanges();
fxPanel = new JFXPanel(); // bridge jfx-swing
Scene scene = new Scene(scrollPane); //root of jfx tree
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
fxPanel.setScene(scene);
masterGroup.getChildren().add(tagsGroup);
fxImageView.setSmooth(true);
fxImageView.setCache(true);
//Update buttons when users select (or unselect) image tags.
tagsGroup.addFocusChangeListener((event) -> {
if (event.getPropertyName().equals(ImageTagControls.NOT_FOCUSED.getName())) {
if (masterGroup.getChildren().contains(imageTagCreator)) {
return;
}
EventQueue.invokeLater(() -> {
add(fxPanel);//add jfx ui to JPanel
});
if (tagsGroup.getChildren().isEmpty()) {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.EMPTY));
} else {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.CREATE));
}
} else if (event.getPropertyName().equals(ImageTagControls.FOCUSED.getName())) {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.SELECTED));
}
});
scrollPane = new ScrollPane(masterGroup); // scrolls and sizes imageview
scrollPane.getStyleClass().add("bg"); //NOI18N
scrollPane.setVbarPolicy(ScrollBarPolicy.AS_NEEDED);
scrollPane.setHbarPolicy(ScrollBarPolicy.AS_NEEDED);
fxPanel = new JFXPanel(); // bridge jfx-swing
Scene scene = new Scene(scrollPane); //root of jfx tree
scene.getStylesheets().add(MediaViewImagePanel.class.getResource("MediaViewImagePanel.css").toExternalForm()); //NOI18N
fxPanel.setScene(scene);
fxImageView.setSmooth(true);
fxImageView.setCache(true);
EventQueue.invokeLater(() -> {
add(fxPanel);//add jfx ui to JPanel
});
}
});
}
}
/**
* Handle tags menu item enabling and disabling given the state of the
* content viewer. For example, when the tags group is empty (no tags on image),
* disable delete menu item, hide menu item, and export menu item.
*/
private void subscribeTagMenuItemsToStateChanges() {
pcs.addPropertyChangeListener((event) -> {
State currentState = (State) event.getNewValue();
switch (currentState) {
case CREATE:
createTagMenuItem.setEnabled(true);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(true);
exportTagsMenuItem.setEnabled(true);
break;
case SELECTED:
if (masterGroup.getChildren().contains(imageTagCreator)) {
imageTagCreator.disconnect();
masterGroup.getChildren().remove(imageTagCreator);
}
createTagMenuItem.setEnabled(false);
deleteTagMenuItem.setEnabled(true);
hideTagsMenuItem.setEnabled(true);
exportTagsMenuItem.setEnabled(true);
break;
case HIDDEN:
createTagMenuItem.setEnabled(false);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(true);
hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName());
exportTagsMenuItem.setEnabled(false);
break;
case VISIBLE:
createTagMenuItem.setEnabled(true);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(true);
hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName());
exportTagsMenuItem.setEnabled(true);
break;
case DEFAULT:
case EMPTY:
if (masterGroup.getChildren().contains(imageTagCreator)) {
imageTagCreator.disconnect();
}
createTagMenuItem.setEnabled(true);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(false);
hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName());
exportTagsMenuItem.setEnabled(false);
break;
case NONEMPTY:
createTagMenuItem.setEnabled(true);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(true);
exportTagsMenuItem.setEnabled(true);
break;
case DISABLE:
createTagMenuItem.setEnabled(false);
deleteTagMenuItem.setEnabled(false);
hideTagsMenuItem.setEnabled(false);
exportTagsMenuItem.setEnabled(false);
break;
default:
break;
}
});
}
public boolean isInited() {
return fxInited;
}
@ -154,10 +330,11 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
Platform.runLater(() -> {
fxImageView.setViewport(new Rectangle2D(0, 0, 0, 0));
fxImageView.setImage(null);
tagger.defaultSettings();
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.DEFAULT));
masterGroup.getChildren().clear();
scrollPane.setContent(null);
scrollPane.setContent(imageGroup);
scrollPane.setContent(masterGroup);
});
}
@ -205,14 +382,31 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
try {
Image fxImage = readImageTask.get();
masterGroup.getChildren().clear();
tagsGroup.getChildren().clear();
this.file = file;
if (nonNull(fxImage)) {
// We have a non-null image, so let's show it.
fxImageView.setImage(fxImage);
imageGroup.getChildren().remove(tagger);
tagger = new ImageTaggingTool(fxImageView, Color.RED);
imageGroup.getChildren().add(tagger);
resetView();
scrollPane.setContent(imageGroup);
masterGroup.getChildren().add(fxImageView);
masterGroup.getChildren().add(tagsGroup);
try {
List<ContentTag> tags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file);
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags);
//Add all image tags
tagsGroup = buildImageTagsGroup(contentViewerTags);
if (!tagsGroup.getChildren().isEmpty()) {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.NONEMPTY));
}
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not retrieve image tags for file in case db", ex); //NON-NLS
}
scrollPane.setContent(masterGroup);
} else {
showErrorNode(Bundle.MediaViewImagePanel_errorLabel_text(), file);
}
@ -252,6 +446,52 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
});
}
/**
* Finds all ContentViewerTags that are of type 'ImageTagRegion' for the
* current file.
*
* @param contentTags
* @return
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private List<ContentViewerTag<ImageTagRegion>> getContentViewerTags(List<ContentTag> contentTags)
throws TskCoreException, NoCurrentCaseException {
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = new ArrayList<>();
for (ContentTag contentTag : contentTags) {
ContentViewerTag<ImageTagRegion> contentViewerTag = ContentViewerTagManager
.getTag(contentTag, ImageTagRegion.class);
if (contentViewerTag == null) {
continue;
}
contentViewerTags.add(contentViewerTag);
}
return contentViewerTags;
}
/**
* Builds ImageTag instances from stored ContentViewerTags of the
* appropriate type.
*
* @param contentTags
* @return
* @throws TskCoreException
* @throws NoCurrentCaseException
*/
private ImageTagsGroup buildImageTagsGroup(List<ContentViewerTag<ImageTagRegion>> contentViewerTags) {
contentViewerTags.forEach(contentViewerTag -> {
/**
* Build the image tag, add an edit event call back to persist all
* edits made on this image tag instance.
*/
tagsGroup.getChildren().add(buildImageTag(contentViewerTag));
});
return tagsGroup;
}
/**
* @return supported mime types
*/
@ -303,6 +543,10 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
zoomInButton = new javax.swing.JButton();
jSeparator2 = new javax.swing.JToolBar.Separator();
zoomResetButton = new javax.swing.JButton();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0));
filler2 = new javax.swing.Box.Filler(new java.awt.Dimension(0, 0), new java.awt.Dimension(0, 0), new java.awt.Dimension(32767, 0));
jPanel1 = new javax.swing.JPanel();
tagsMenu = new javax.swing.JButton();
setBackground(new java.awt.Color(0, 0, 0));
addComponentListener(new java.awt.event.ComponentAdapter() {
@ -406,6 +650,23 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
}
});
toolbar.add(zoomResetButton);
toolbar.add(filler1);
toolbar.add(filler2);
toolbar.add(jPanel1);
org.openide.awt.Mnemonics.setLocalizedText(tagsMenu, org.openide.util.NbBundle.getMessage(MediaViewImagePanel.class, "MediaViewImagePanel.tagsMenu.text_1")); // NOI18N
tagsMenu.setFocusable(false);
tagsMenu.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
tagsMenu.setMaximumSize(new java.awt.Dimension(75, 21));
tagsMenu.setMinimumSize(new java.awt.Dimension(75, 21));
tagsMenu.setPreferredSize(new java.awt.Dimension(75, 21));
tagsMenu.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
tagsMenu.addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
tagsMenuMousePressed(evt);
}
});
toolbar.add(tagsMenu);
add(toolbar);
}// </editor-fold>//GEN-END:initComponents
@ -450,12 +711,234 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
updateView();
}//GEN-LAST:event_formComponentResized
/**
* Deletes the selected tag when the Delete button is pressed in the Tag
* Menu.
*/
private void deleteTag() {
Platform.runLater(() -> {
ImageTag tagInFocus = tagsGroup.getFocus();
if (tagInFocus == null) {
return;
}
try {
ContentViewerTag<ImageTagRegion> contentViewerTag = tagInFocus.getContentViewerTag();
scrollPane.setCursor(Cursor.WAIT);
ContentViewerTagManager.deleteTag(contentViewerTag);
Case.getCurrentCase().getServices().getTagsManager().deleteContentTag(contentViewerTag.getContentTag());
tagsGroup.getChildren().remove(tagInFocus);
} catch (TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not delete image tag in case db", ex); //NON-NLS
}
scrollPane.setCursor(Cursor.DEFAULT);
});
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.CREATE));
}
/**
* Enables create tag logic when the Create button is pressed in the Tags
* Menu.
*/
private void createTag() {
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.DISABLE));
imageTagCreator = new ImageTagCreator(fxImageView);
PropertyChangeListener newTagListener = (event) -> {
SwingUtilities.invokeLater(() -> {
ImageTagRegion tag = (ImageTagRegion) event.getNewValue();
//Ask the user for tag name and comment
TagNameAndComment result = GetTagNameAndCommentDialog.doDialog();
if (result != null) {
//Persist and build image tag
Platform.runLater(() -> {
try {
scrollPane.setCursor(Cursor.WAIT);
ContentViewerTag<ImageTagRegion> contentViewerTag = storeImageTag(tag, result);
ImageTag imageTag = buildImageTag(contentViewerTag);
tagsGroup.getChildren().add(imageTag);
} catch (TskCoreException | SerializationException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not save new image tag in case db", ex); //NON-NLS
}
scrollPane.setCursor(Cursor.DEFAULT);
});
}
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.CREATE));
});
//Remove image tag creator from panel
Platform.runLater(() -> {
imageTagCreator.disconnect();
masterGroup.getChildren().remove(imageTagCreator);
});
};
imageTagCreator.addNewTagListener(newTagListener);
Platform.runLater(() -> masterGroup.getChildren().add(imageTagCreator));
}
/**
* Creates an ImageTag instance from the ContentViewerTag.
*
* @param contentViewerTag
* @return
*/
private ImageTag buildImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag) {
ImageTag imageTag = new ImageTag(contentViewerTag, fxImageView);
//Automatically persist edits made by user
imageTag.subscribeToEditEvents((edit) -> {
try {
scrollPane.setCursor(Cursor.WAIT);
ImageTagRegion newRegion = (ImageTagRegion) edit.getNewValue();
ContentViewerTagManager.updateTag(contentViewerTag, newRegion);
} catch (SerializationException | TskCoreException | NoCurrentCaseException ex) {
LOGGER.log(Level.WARNING, "Could not save edit for image tag in case db", ex); //NON-NLS
}
scrollPane.setCursor(Cursor.DEFAULT);
});
return imageTag;
}
/**
* Stores the image tag by creating a ContentTag instance and associating
* the ImageTagRegion data with it in the case database.
*
* @param data
* @param result
*/
private ContentViewerTag<ImageTagRegion> storeImageTag(ImageTagRegion data, TagNameAndComment result)
throws TskCoreException, SerializationException, NoCurrentCaseException {
scrollPane.setCursor(Cursor.WAIT);
try {
ContentTag contentTag = Case.getCurrentCaseThrows().getServices().getTagsManager()
.addContentTag(file, result.getTagName(), result.getComment());
return ContentViewerTagManager.saveTag(contentTag, data);
} finally {
scrollPane.setCursor(Cursor.DEFAULT);
}
}
/**
* Hides or show tags when the Hide or Show button is pressed in the Tags
* Menu.
*/
private void showOrHideTags() {
Platform.runLater(() -> {
if (DisplayOptions.HIDE_TAGS.getName().equals(hideTagsMenuItem.getText())) {
//Temporarily remove the tags group and update buttons
masterGroup.getChildren().remove(tagsGroup);
hideTagsMenuItem.setText(DisplayOptions.SHOW_TAGS.getName());
tagsGroup.clearFocus();
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.HIDDEN));
} else {
//Add tags group back in and update buttons
masterGroup.getChildren().add(tagsGroup);
hideTagsMenuItem.setText(DisplayOptions.HIDE_TAGS.getName());
pcs.firePropertyChange(new PropertyChangeEvent(this,
"state", null, State.VISIBLE));
}
});
}
@NbBundle.Messages({
"MediaViewImagePanel.exportSaveText=Save",
"MediaViewImagePanel.successfulExport=Tagged image was successfully saved.",
"MediaViewImagePanel.unsuccessfulExport=Unable to export tagged image to disk.",
"MediaViewImagePanel.fileChooserTitle=Choose a save location"
})
private void exportTags() {
tagsGroup.clearFocus();
exportChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
//Always base chooser location to export folder
exportChooser.setCurrentDirectory(new File(Case.getCurrentCase().getExportDirectory()));
int returnVal = exportChooser.showDialog(this, Bundle.MediaViewImagePanel_exportSaveText());
if (returnVal == JFileChooser.APPROVE_OPTION) {
new SwingWorker<Void, Void>() {
@Override
protected Void doInBackground() {
try {
//Retrieve content viewer tags
List<ContentTag> tags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file);
List<ContentViewerTag<ImageTagRegion>> contentViewerTags = getContentViewerTags(tags);
//Pull out image tag regions
Collection<ImageTagRegion> regions = contentViewerTags.stream()
.map(cvTag -> cvTag.getDetails()).collect(Collectors.toList());
//Apply tags to image and write to file
BufferedImage pngImage = ImageTagsUtility.writeTags(file, regions, "png");
Path output = Paths.get(exportChooser.getSelectedFile().getPath(),
FilenameUtils.getBaseName(file.getName()) + "-with_tags.png"); //NON-NLS
ImageIO.write(pngImage, "png", output.toFile());
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_successfulExport());
} catch (TskCoreException | NoCurrentCaseException | IOException ex) {
LOGGER.log(Level.WARNING, "Unable to export tagged image to disk", ex); //NON-NLS
JOptionPane.showMessageDialog(null, Bundle.MediaViewImagePanel_unsuccessfulExport());
}
return null;
}
}.execute();
}
}
private void tagsMenuMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tagsMenuMousePressed
popupMenu.show(tagsMenu, -300 + tagsMenu.getWidth(), tagsMenu.getHeight() + 3);
}//GEN-LAST:event_tagsMenuMousePressed
/**
* Display states for the show/hide tags button.
*/
enum DisplayOptions {
HIDE_TAGS("Hide"),
SHOW_TAGS("Show");
private final String name;
DisplayOptions(String name) {
this.name = name;
}
String getName() {
return name;
}
}
/**
* Different states that the content viewer can be in. These states drive
* which buttons are enabled for tagging.
*/
enum State {
HIDDEN,
VISIBLE,
SELECTED,
CREATE,
EMPTY,
NONEMPTY,
DEFAULT,
DISABLE;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.Box.Filler filler1;
private javax.swing.Box.Filler filler2;
private javax.swing.JPanel jPanel1;
private javax.swing.JToolBar.Separator jSeparator1;
private javax.swing.JToolBar.Separator jSeparator2;
private javax.swing.JButton rotateLeftButton;
private javax.swing.JButton rotateRightButton;
private javax.swing.JTextField rotationTextField;
private javax.swing.JButton tagsMenu;
private javax.swing.JToolBar toolbar;
private javax.swing.JButton zoomInButton;
private javax.swing.JButton zoomOutButton;
@ -601,8 +1084,8 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
// Add the transforms in reverse order of intended execution.
// Note: They MUST be added in this order to ensure translate is
// executed last.
imageGroup.getTransforms().clear();
imageGroup.getTransforms().addAll(translate, rotate, scale);
masterGroup.getTransforms().clear();
masterGroup.getTransforms().addAll(translate, rotate, scale);
// Adjust scroll bar positions for view changes.
if (viewportWidth > fxPanel.getWidth()) {
@ -618,107 +1101,4 @@ class MediaViewImagePanel extends JPanel implements MediaFileViewer.MediaViewPan
rotationTextField.setText((int) rotation + "°");
zoomTextField.setText((Math.round(zoomRatio * 100.0)) + "%");
}
/**
* Enables users to 'tag' a region of an image by clicking and dragging a
* rectangle overtop.
*/
class ImageTaggingTool extends Rectangle {
private final double imageWidth;
private final double imageHeight;
private final double imageOriginX;
private final double imageOriginY;
//Origin of the drag event.
private double rectangleOriginX;
private double rectangleOriginY;
//Rectangle lines should be 1.5% of the image. This level of thickness has
//a good balance between visual acuity and loss of selection at the borders
//of the image.
private double lineThicknessAsPercent = 1.5;
/**
* Adds tagging support to an image, where the 'tag' rectangle will be
* the specified color.
*
* @param image Image to tag
* @param color Color of the 'tag' rectangle
*/
private ImageTaggingTool(ImageView image, Color color) {
defaultSettings();
imageWidth = image.getImage().getWidth();
imageHeight = image.getImage().getHeight();
imageOriginX = image.getX();
imageOriginY = image.getY();
setStroke(color);
setFill(color.deriveColor(0, 0, 0, 0));
//Calculate how many pixels the stroke width should be to guarentee
//a consistent % of image consumed by the rectangle border.
double min = Math.min(imageWidth, imageHeight);
double lineThicknessPixels = min * lineThicknessAsPercent / 100.0;
setStrokeWidth(lineThicknessPixels);
setVisible(false);
//Create a rectangle by left clicking on the image
image.setOnMousePressed((MouseEvent event) -> {
if (event.isSecondaryButtonDown()) {
return;
}
//Reset box on new click.
defaultSettings();
rectangleOriginX = event.getX();
rectangleOriginY = event.getY();
setX(rectangleOriginX);
setY(rectangleOriginY);
});
//Adjust the rectangle by dragging the left mouse button
image.setOnMouseDragged((MouseEvent event) -> {
if (event.isSecondaryButtonDown()) {
return;
}
/**
* Ensure the rectangle is contained within image boundaries and
* that the line thickness is kept within bounds.
*/
double newX = Math.min(Math.max(event.getX(), imageOriginX)
+ lineThicknessPixels / 2, imageWidth - lineThicknessPixels / 2);
double newY = Math.min(Math.max(event.getY(), imageOriginY)
+ lineThicknessPixels / 2, imageHeight - lineThicknessPixels / 2);
setVisible(true);
double offsetX = newX - rectangleOriginX;
if (offsetX < 0) {
setX(newX);
}
setWidth(Math.abs(offsetX));
double offsetY = newY - rectangleOriginY;
if (offsetY < 0) {
setY(newY);
}
setHeight(Math.abs(offsetY));
});
}
/**
* Reset the rectangle to default dimensions.
*/
public final void defaultSettings() {
setX(0);
setY(0);
setWidth(0);
setHeight(0);
setVisible(false);
}
}
}

View File

@ -271,22 +271,7 @@
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="htmlPanel" alignment="0" pref="647" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="htmlPanel" alignment="0" pref="362" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="org.sleuthkit.autopsy.contentviewers.HtmlPanel" name="htmlPanel">
</Component>
</SubComponents>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
</Container>
<Container class="javax.swing.JScrollPane" name="rtfbodyScrollPane">
<Properties>

View File

@ -87,7 +87,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
private static final int ATTM_TAB_INDEX = 4;
private final List<JTextComponent> textAreas;
private final org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
/**
* Artifact currently being displayed
*/
@ -101,6 +101,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
@NbBundle.Messages("MessageContentViewer.AtrachmentsPanel.title=Attachments")
public MessageContentViewer() {
initComponents();
htmlPane.add(htmlPanel);
envelopePanel.setBackground(new Color(0, 0, 0, 38));
drp = DataResultPanel.createInstanceUninitialized(Bundle.MessageContentViewer_AtrachmentsPanel_title(), "", new TableFilterNode(Node.EMPTY, false), 0, null);
attachmentsScrollPane.setViewportView(drp);
@ -153,7 +154,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
textbodyScrollPane = new javax.swing.JScrollPane();
textbodyTextArea = new javax.swing.JTextArea();
htmlPane = new javax.swing.JPanel();
htmlPanel = new org.sleuthkit.autopsy.contentviewers.HtmlPanel();
rtfbodyScrollPane = new javax.swing.JScrollPane();
rtfbodyTextPane = new javax.swing.JTextPane();
attachmentsPanel = new javax.swing.JPanel();
@ -266,17 +266,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.textbodyScrollPane.TabConstraints.tabTitle"), textbodyScrollPane); // NOI18N
javax.swing.GroupLayout htmlPaneLayout = new javax.swing.GroupLayout(htmlPane);
htmlPane.setLayout(htmlPaneLayout);
htmlPaneLayout.setHorizontalGroup(
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 647, Short.MAX_VALUE)
);
htmlPaneLayout.setVerticalGroup(
htmlPaneLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(htmlPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 362, Short.MAX_VALUE)
);
htmlPane.setLayout(new java.awt.BorderLayout());
msgbodyTabbedPane.addTab(org.openide.util.NbBundle.getMessage(MessageContentViewer.class, "MessageContentViewer.htmlPane.TabConstraints.tabTitle"), htmlPane); // NOI18N
rtfbodyScrollPane.setVerticalScrollBarPolicy(javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
@ -357,7 +347,6 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
private javax.swing.JScrollPane headersScrollPane;
private javax.swing.JTextArea headersTextArea;
private javax.swing.JPanel htmlPane;
private org.sleuthkit.autopsy.contentviewers.HtmlPanel htmlPanel;
private javax.swing.JTabbedPane msgbodyTabbedPane;
private javax.swing.JScrollPane rtfbodyScrollPane;
private javax.swing.JTextPane rtfbodyTextPane;
@ -534,7 +523,7 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
textComponent.setCaretPosition(0); //make sure we start at the top
}
}
final boolean hasText = attributeText.length() > 0;
msgbodyTabbedPane.setEnabledAt(index, hasText);
@ -680,18 +669,18 @@ public class MessageContentViewer extends javax.swing.JPanel implements DataCont
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
Set<String> keepProps = new HashSet<>(Arrays.asList(
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl")));
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.nameColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.score.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.comment.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.createSheet.count.name"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.sizeColLbl"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.mimeType"),
NbBundle.getMessage(AbstractAbstractFileNode.class, "AbstractAbstractFileNode.knownColLbl")));
//Remove all other props except for the ones above
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
for(Property<?> p : sheetSet.getProperties()) {
if(!keepProps.contains(p.getName())){
for (Property<?> p : sheetSet.getProperties()) {
if (!keepProps.contains(p.getName())) {
sheetSet.remove(p.getName());
}
}

View File

@ -0,0 +1,343 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import com.sun.javafx.event.EventDispatchChainImpl;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.collections.ListChangeListener;
import javafx.scene.Cursor;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.control.Tooltip;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
/**
* A tagged region displayed over an image. This class contains a "physical tag"
* and 8 edit "handles". The physical tag is a plain old rectangle that defines
* the tag boundaries. The edit handles serve two purposes. One is to represent
* selection. All 8 edit handles will become visible overtop the physical tag
* when the user clicks on the rectangle. The other purpose is to allow the user to edit
* and manipulate the physical tag boundaries (hence the name, edit handle).
* This class should be treated as a logical image tag.
*/
public final class ImageTag extends Group {
// Used to tell the 8 edit handles to hide if this tag is no longer selected
private final EventDispatchChainImpl ALL_CHILDREN;
//Notifies listeners that the user has editted the tag boundaries
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
//The underlying presistent tag details that this image tag originates from
private final ContentViewerTag<ImageTagRegion> appTag;
public ImageTag(ContentViewerTag<ImageTagRegion> contentViewerTag, ImageView image) {
ALL_CHILDREN = new EventDispatchChainImpl();
this.appTag = contentViewerTag;
this.getChildren().addListener((ListChangeListener<Node>) change -> {
change.next();
change.getAddedSubList().forEach((node) -> ALL_CHILDREN.append(node.getEventDispatcher()));
});
ImageTagRegion details = contentViewerTag.getDetails();
PhysicalTag physicalTag = new PhysicalTag(details);
//Defines the max allowable boundary that a user may drag any given handle.
Boundary dragBoundary = (x, y) -> {
double boundingX = image.getX();
double boundingY = image.getY();
double width = image.getImage().getWidth();
double height = image.getImage().getHeight();
return x > boundingX + details.getStrokeThickness() / 2
&& x < boundingX + width - details.getStrokeThickness() / 2
&& y > boundingY + details.getStrokeThickness() / 2
&& y < boundingY + height - details.getStrokeThickness() / 2;
};
EditHandle bottomLeft = new EditHandle(physicalTag)
.setPosition(Position.bottom(), Position.left())
.setDrag(dragBoundary, Draggable.bottom(), Draggable.left());
EditHandle bottomRight = new EditHandle(physicalTag)
.setPosition(Position.bottom(), Position.right())
.setDrag(dragBoundary, Draggable.bottom(), Draggable.right());
EditHandle topLeft = new EditHandle(physicalTag)
.setPosition(Position.top(), Position.left())
.setDrag(dragBoundary, Draggable.top(), Draggable.left());
EditHandle topRight = new EditHandle(physicalTag)
.setPosition(Position.top(), Position.right())
.setDrag(dragBoundary, Draggable.top(), Draggable.right());
EditHandle bottomMiddle = new EditHandle(physicalTag)
.setPosition(Position.bottom(), Position.xMiddle())
.setDrag(dragBoundary, Draggable.bottom());
EditHandle topMiddle = new EditHandle(physicalTag)
.setPosition(Position.top(), Position.xMiddle())
.setDrag(dragBoundary, Draggable.top());
EditHandle rightMiddle = new EditHandle(physicalTag)
.setPosition(Position.right(), Position.yMiddle())
.setDrag(dragBoundary, Draggable.right());
EditHandle leftMiddle = new EditHandle(physicalTag)
.setPosition(Position.left(), Position.yMiddle())
.setDrag(dragBoundary, Draggable.left());
//The "logical" tag is the Group
this.getChildren().addAll(physicalTag, bottomLeft, bottomRight, topLeft,
topRight, bottomMiddle, topMiddle, rightMiddle, leftMiddle);
Tooltip.install(this, new Tooltip(contentViewerTag.getContentTag()
.getName().getDisplayName()));
this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event));
this.addEventHandler(ImageTagControls.FOCUSED, event -> ALL_CHILDREN.dispatchEvent(event));
}
/**
* Add a new listener for edit events. These events are generated when a
* user drags on one of the edit "knobs" of the tag.
*
* @param listener
*/
public void subscribeToEditEvents(PropertyChangeListener listener) {
pcs.addPropertyChangeListener(listener);
}
/**
* Get the content viewer tag that this class represents.
*
* @return
*/
public ContentViewerTag<ImageTagRegion> getContentViewerTag() {
return appTag;
}
/**
* Plain old rectangle that represents an unselected Image Tag
*/
class PhysicalTag extends Rectangle {
public PhysicalTag(ImageTagRegion details) {
this.setStroke(Color.RED);
this.setFill(Color.RED.deriveColor(0, 0, 0, 0));
this.setStrokeWidth(details.getStrokeThickness());
setX(details.getX());
setY(details.getY());
setWidth(details.getWidth());
setHeight(details.getHeight());
this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setOpacity(1));
this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setOpacity(0.5));
}
/**
* Builds a portable description of the tag region.
*
* @return
*/
public ImageTagRegion getState() {
return new ImageTagRegion()
.setX(this.getX())
.setY(this.getY())
.setWidth(this.getWidth())
.setHeight(this.getHeight())
.setStrokeThickness(this.getStrokeWidth());
}
}
/**
* Draggable "knob" used to manipulate the physical tag boundaries.
*/
class EditHandle extends Circle {
private final PhysicalTag parent;
public EditHandle(PhysicalTag parent) {
this.setVisible(false);
//Hide when the tag is not selected.
this.addEventHandler(ImageTagControls.NOT_FOCUSED, event -> this.setVisible(false));
this.addEventHandler(ImageTagControls.FOCUSED, event -> this.setVisible(true));
this.setRadius(parent.getStrokeWidth());
this.setFill(parent.getStroke());
this.setOnDragDetected(event -> {
this.getParent().setCursor(Cursor.CLOSED_HAND);
});
this.setOnMouseReleased(event -> {
this.getParent().setCursor(Cursor.DEFAULT);
pcs.firePropertyChange(new PropertyChangeEvent(this, "Tag Edit", null, parent.getState()));
});
this.parent = parent;
}
/**
* Sets the positioning of this edit handle on the physical tag.
*
* @param vals
* @return
*/
public EditHandle setPosition(Position... vals) {
for (Position pos : vals) {
parent.widthProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this));
parent.heightProperty().addListener((obs, oldVal, newVal) -> pos.set(parent, this));
pos.set(parent, this);
}
return this;
}
/**
* Sets the drag capabilities for manipulating the physical tag.
*
* @param bounds
* @param vals
* @return
*/
public EditHandle setDrag(Boundary bounds, Draggable... vals) {
this.setOnMouseDragged((event) -> {
for (Draggable drag : vals) {
drag.perform(parent, event, bounds);
}
});
return this;
}
}
/**
* Position strategies for "sticking" to a location on the physical tag when
* it is resized.
*/
static interface Position {
void set(PhysicalTag parent, Circle knob);
static Position left() {
return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty());
}
static Position right() {
return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth()));
}
static Position top() {
return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty());
}
static Position bottom() {
return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight()));
}
static Position xMiddle() {
return (parent, knob) -> knob.centerXProperty().bind(parent.xProperty().add(parent.getWidth() / 2));
}
static Position yMiddle() {
return (parent, knob) -> knob.centerYProperty().bind(parent.yProperty().add(parent.getHeight() / 2));
}
}
/**
* Defines the bounding box for which dragging is allowable.
*/
@FunctionalInterface
static interface Boundary {
boolean isPointInBounds(double x, double y);
}
/**
* Drag strategies for manipulating the physical tag from a given side of
* the rectangle.
*/
static interface Draggable {
void perform(PhysicalTag parent, MouseEvent event, Boundary b);
static Draggable bottom() {
return (parent, event, bounds) -> {
if (!bounds.isPointInBounds(event.getX(), event.getY())) {
return;
}
double deltaY = event.getY() - parent.getY();
if (deltaY > 0) {
parent.setHeight(deltaY);
}
};
}
static Draggable top() {
return (parent, event, bounds) -> {
if (!bounds.isPointInBounds(event.getX(), event.getY())) {
return;
}
double deltaY = parent.getY() + parent.getHeight() - event.getY();
if (deltaY < parent.getY() + parent.getHeight() && deltaY > 0) {
parent.setHeight(deltaY);
parent.setY(event.getY());
}
};
}
static Draggable left() {
return (parent, event, bounds) -> {
if (!bounds.isPointInBounds(event.getX(), event.getY())) {
return;
}
double deltaX = parent.getX() + parent.getWidth() - event.getX();
if (deltaX < parent.getX() + parent.getWidth() && deltaX > 0) {
parent.setWidth(deltaX);
parent.setX(event.getX());
}
};
}
static Draggable right() {
return (parent, event, bounds) -> {
if (!bounds.isPointInBounds(event.getX(), event.getY())) {
return;
}
double deltaX = event.getX() - parent.getX();
if (deltaX > 0) {
parent.setWidth(deltaX);
}
};
}
}
}

View File

@ -0,0 +1,31 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import javafx.event.Event;
import javafx.event.EventType;
/**
* Focus events for ImageTags to consume. These events trigger selection behavior
* on ImageTags and are originated from the ImageTagsGroup class.
*/
public class ImageTagControls {
public static final EventType<Event> NOT_FOCUSED = new EventType<>("NOT_FOCUSED");
public static final EventType<Event> FOCUSED = new EventType<>("FOCUSED");
}

View File

@ -0,0 +1,180 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.event.EventHandler;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
/**
* Creates image tags. This class attaches itself to a source image, waiting
* for mouse press, mouse drag, and mouse release events. Upon a mouse release
* event, any listeners are updated with the portable description of the new tag
* boundaries (ImageTagRegion).
*/
public final class ImageTagCreator extends Rectangle {
//Origin of the drag event.
private double rectangleOriginX, rectangleOriginY;
//Rectangle lines should be 1.5% of the image. This level of thickness has
//a good balance between visual acuity and loss of selection at the borders
//of the image.
private final static double LINE_THICKNESS_PERCENT = 1.5;
private final double minArea;
//Used to update listeners of the new tag boundaries
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private final EventHandler<MouseEvent> mousePressed;
private final EventHandler<MouseEvent> mouseDragged;
private final EventHandler<MouseEvent> mouseReleased;
//Handles the unregistering this ImageTagCreator from mouse press, mouse drag,
//and mouse release events of the source image.
private final Runnable disconnectRunnable;
/**
* Adds tagging support to an image, where the 'tag' rectangle will be the
* specified color.
*
* @param image Image to tag
*/
public ImageTagCreator(ImageView image) {
setStroke(Color.RED);
setFill(Color.RED.deriveColor(0, 0, 0, 0));
//Calculate how many pixels the stroke width should be to guarentee
//a consistent % of image consumed by the rectangle border.
double min = Math.min(image.getImage().getWidth(), image.getImage().getHeight());
double lineThicknessPixels = min * LINE_THICKNESS_PERCENT / 100.0;
setStrokeWidth(lineThicknessPixels);
minArea = lineThicknessPixels * lineThicknessPixels;
setVisible(false);
this.mousePressed = (MouseEvent event) -> {
if (event.isSecondaryButtonDown()) {
return;
}
//Reset box on new click.
defaultSettings();
rectangleOriginX = event.getX();
rectangleOriginY = event.getY();
setX(rectangleOriginX);
setY(rectangleOriginY);
};
image.addEventHandler(MouseEvent.MOUSE_PRESSED, this.mousePressed);
this.mouseDragged = (MouseEvent event) -> {
if (event.isSecondaryButtonDown()) {
return;
}
double currentX = event.getX(), currentY = event.getY();
/**
* Ensure the rectangle is contained within image boundaries and
* that the line thickness is kept within bounds.
*/
double newX = Math.min(Math.max(currentX, image.getX())
+ lineThicknessPixels / 2, image.getImage().getWidth() - lineThicknessPixels / 2);
double newY = Math.min(Math.max(currentY, image.getY())
+ lineThicknessPixels / 2, image.getImage().getHeight() - lineThicknessPixels / 2);
setVisible(true);
double offsetX = newX - rectangleOriginX;
if (offsetX < 0) {
setX(newX);
}
setWidth(Math.abs(offsetX));
double offsetY = newY - rectangleOriginY;
if (offsetY < 0) {
setY(newY);
}
setHeight(Math.abs(offsetY));
};
image.addEventHandler(MouseEvent.MOUSE_DRAGGED, this.mouseDragged);
this.mouseReleased = event -> {
//Reject any drags that are too small to count as a meaningful tag.
//Meaningful is described as having an area that is visible that is
//not consumed by the thickness of the stroke.
if ((this.getWidth() - this.getStrokeWidth())
* (this.getHeight() - this.getStrokeWidth()) <= minArea) {
defaultSettings();
return;
}
this.pcs.firePropertyChange(new PropertyChangeEvent(this, "New Tag",
null, new ImageTagRegion()
.setX(this.getX())
.setY(this.getY())
.setWidth(this.getWidth())
.setHeight(this.getHeight())
.setStrokeThickness(lineThicknessPixels)));
};
image.addEventHandler(MouseEvent.MOUSE_RELEASED, this.mouseReleased);
//Used to remove itself from mouse events on the source image
disconnectRunnable = () -> {
defaultSettings();
image.removeEventHandler(MouseEvent.MOUSE_RELEASED, mouseReleased);
image.removeEventHandler(MouseEvent.MOUSE_DRAGGED, mouseDragged);
image.removeEventHandler(MouseEvent.MOUSE_PRESSED, mousePressed);
};
}
/**
* Registers a PCL for new tag events. Listeners are updated with a portable
* description (ImageTagRegion) of the new tag, which represent the
* rectangle boundaries.
*
* @param listener
*/
public void addNewTagListener(PropertyChangeListener listener) {
this.pcs.addPropertyChangeListener(listener);
}
/**
* Removes itself from mouse events on the source image.
*/
public void disconnect() {
this.disconnectRunnable.run();
}
/**
* Reset the rectangle to default dimensions.
*/
private void defaultSettings() {
setWidth(0);
setHeight(0);
setVisible(false);
}
}

View File

@ -0,0 +1,82 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
/**
* Bean representation of an image tag. This class is used for storage and
* retrieval of ImageTags from the case database.
*/
public class ImageTagRegion {
/**
* These fields will be serialized and stored in the case database by the
* ContentViewerTagManager.
*/
private double x;
private double y;
private double width;
private double height;
private double strokeThickness;
public ImageTagRegion setStrokeThickness(double thickness) {
this.strokeThickness = thickness;
return this;
}
public ImageTagRegion setX(double x) {
this.x = x;
return this;
}
public ImageTagRegion setWidth(double width) {
this.width = width;
return this;
}
public ImageTagRegion setY(double y) {
this.y = y;
return this;
}
public ImageTagRegion setHeight(double height) {
this.height = height;
return this;
}
public double getX() {
return x;
}
public double getWidth() {
return width;
}
public double getY() {
return y;
}
public double getHeight() {
return height;
}
public double getStrokeThickness() {
return strokeThickness;
}
}

View File

@ -0,0 +1,134 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import com.sun.javafx.event.EventDispatchChainImpl;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import javafx.event.Event;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.input.MouseEvent;
/**
* Manages the focus and z-ordering of ImageTags. Only one image tag may be
* selected at a time. Image tags show their 8 edit "handles" upon selection
* (see ImageTag class for more details) and get the highest z-ordering to make
* editing easier. This class is responsible for setting and dropping focus as
* the user navigates from tag to tag. The ImageTag is treated as a logical
* unit, however it's underlying representation consists of the physical
* rectangle and the 8 edit handles. JavaFX will report selection on the Node
* level (so either the Rectangle, or a singe edit handle), which makes keeping
* the entire image tag in focus a non-trivial problem.
*/
public final class ImageTagsGroup extends Group {
private final EventDispatchChainImpl NO_OP_CHAIN = new EventDispatchChainImpl();
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private volatile ImageTag currentFocus;
public ImageTagsGroup(Node backDrop) {
//Reset focus of current selection if the back drop has focus.
backDrop.setOnMousePressed((mouseEvent) -> {
if (currentFocus != null) {
currentFocus.getEventDispatcher().dispatchEvent(
new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN);
}
this.pcs.firePropertyChange(new PropertyChangeEvent(this,
ImageTagControls.NOT_FOCUSED.getName(), currentFocus, null));
currentFocus = null;
});
//Set the focus of selected tag
this.addEventFilter(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
if (!e.isPrimaryButtonDown()) {
return;
}
//Pull out the logical image tag that this node is associated with
Node topLevelChild = e.getPickResult().getIntersectedNode();
while (!this.getChildren().contains(topLevelChild)) {
topLevelChild = topLevelChild.getParent();
}
requestFocus((ImageTag) topLevelChild);
});
}
/**
* Subscribe to focus change events on Image tags.
*
* @param fcl PCL to be notified which Image tag has been selected.
*/
public void addFocusChangeListener(PropertyChangeListener fcl) {
this.pcs.addPropertyChangeListener(fcl);
}
/**
* Get the image tag that current has focus.
*
* @return ImageTag instance or null if no tag is in focus.
*/
public ImageTag getFocus() {
return currentFocus;
}
/**
* Clears the current focus
*/
public void clearFocus() {
if(currentFocus != null) {
resetFocus(currentFocus);
currentFocus = null;
}
}
/**
* Notifies the logical image tag that it is no longer in focus.
*
* @param n
*/
private void resetFocus(ImageTag n) {
n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.NOT_FOCUSED), NO_OP_CHAIN);
this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.NOT_FOCUSED.getName(), n, null));
}
/**
* Notifies the logical image that it is in focus.
*
* @param n
*/
private void requestFocus(ImageTag n) {
if (n.equals(currentFocus)) {
return;
} else if (currentFocus != null && !currentFocus.equals(n)) {
resetFocus(currentFocus);
}
n.getEventDispatcher().dispatchEvent(new Event(ImageTagControls.FOCUSED), NO_OP_CHAIN);
this.pcs.firePropertyChange(new PropertyChangeEvent(this, ImageTagControls.FOCUSED.getName(), currentFocus, n));
currentFocus = n;
n.toFront();
}
}

View File

@ -0,0 +1,141 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.contentviewers.imagetagging;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import javax.imageio.ImageIO;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfByte;
import org.opencv.core.MatOfInt;
import org.opencv.core.Point;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.highgui.Highgui;
import org.opencv.imgproc.Imgproc;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Utility class for handling content viewer tags on images.
*/
public final class ImageTagsUtility {
/**
* Sizes for thumbnails
*/
public enum IconSize {
SMALL(50),
MEDIUM(100),
LARGE(200);
private final int SIZE;
IconSize(int size) {
this.SIZE = size;
}
public int getSize() {
return SIZE;
}
}
/**
* Embeds the tag regions into an image.
*
* @param file Base Image
* @param tagRegions Tag regions to be saved into the image
* @param outputEncoding Format of image (jpg, png, etc). See OpenCV for
* supported formats. Do not include a "."
* @return Output image as a BufferedImage
*
* @throws TskCoreException Cannot read from abstract file
* @throws IOException Could not create buffered image from OpenCV result
*/
public static BufferedImage writeTags(AbstractFile file, Collection<ImageTagRegion> tagRegions,
String outputEncoding) throws TskCoreException, IOException {
byte[] imageInMemory = new byte[(int) file.getSize()];
file.read(imageInMemory, 0, file.getSize());
Mat originalImage = Highgui.imdecode(new MatOfByte(imageInMemory), Highgui.IMREAD_UNCHANGED);
tagRegions.forEach((region) -> {
Core.rectangle(
originalImage, //Matrix obj of the image
new Point(region.getX(), region.getY()), //p1
new Point(region.getX() + region.getWidth(), region.getY() + region.getHeight()), //p2
new Scalar(0, 0, 255), //Scalar object for color
(int) Math.rint(region.getStrokeThickness())
);
});
MatOfByte matOfByte = new MatOfByte();
MatOfInt params = new MatOfInt(Highgui.IMWRITE_JPEG_QUALITY, 100);
Highgui.imencode("." + outputEncoding, originalImage, matOfByte, params);
try (ByteArrayInputStream imageStream = new ByteArrayInputStream(matOfByte.toArray())) {
BufferedImage result = ImageIO.read(imageStream);
originalImage.release();
matOfByte.release();
return result;
}
}
/**
* Creates a thumbnail version of the image with tags applied.
*
* @param file Input file to apply tags & produce thumbnail from
* @param tagRegions Tags to apply
* @param iconSize Size of the output thumbnail
* @param outputEncoding Format of thumbnail (jpg, png, etc). See OpenCV for
* supported formats. Do not include a "."
* @return BufferedImage representing the thumbnail
*
* @throws TskCoreException Could not read from file
* @throws IOException Could not create buffered image from OpenCV result
*/
public static BufferedImage makeThumbnail(AbstractFile file, Collection<ImageTagRegion> tagRegions,
IconSize iconSize, String outputEncoding) throws TskCoreException, IOException {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
BufferedImage result = writeTags(file, tagRegions, outputEncoding);
ImageIO.write(result, outputEncoding, baos);
Mat markedUpImage = Highgui.imdecode(new MatOfByte(baos.toByteArray()), Highgui.IMREAD_UNCHANGED);
Mat thumbnail = new Mat();
Size resize = new Size(iconSize.getSize(), iconSize.getSize());
Imgproc.resize(markedUpImage, thumbnail, resize);
MatOfByte matOfByte = new MatOfByte();
Highgui.imencode("." + outputEncoding, thumbnail, matOfByte);
try (ByteArrayInputStream thumbnailStream = new ByteArrayInputStream(matOfByte.toArray())) {
BufferedImage thumbnailImage = ImageIO.read(thumbnailStream);
thumbnail.release();
matOfByte.release();
markedUpImage.release();
return thumbnailImage;
}
}
}
private ImageTagsUtility() {
}
}

View File

@ -23,6 +23,7 @@ import org.openide.nodes.Node;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.datamodel.AbstractFile;
/**
* A DataContentViewer that displays text with the TextViewers available.
@ -90,6 +91,17 @@ public class TextContentViewer implements DataContentViewer {
if (node == null) {
return false;
}
// get the node's File, if it has one
AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file == null) {
return false;
}
// disable the text content viewer for directories and empty files
if (file.isDir() || file.getSize() == 0) {
return false;
}
return panel.isSupported(node);
}

View File

@ -79,7 +79,7 @@ public class TextContentViewerPanel extends javax.swing.JPanel implements DataCo
}
/**
* Deterime wether the content viewer which displays this panel isSupported.
* Determine whether the content viewer which displays this panel isSupported.
* This panel is supported if any of the TextViewer's displayed in it are
* supported.
*

View File

@ -217,3 +217,4 @@ DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save table as CSV

View File

@ -32,6 +32,7 @@ DataResultViewerTable.commentRenderer.noComment.toolTip=No comments found
DataResultViewerTable.commentRenderer.tagComment.toolTip=Comment exists on associated tag(s)
DataResultViewerTable.countRender.name=O
DataResultViewerTable.countRender.toolTip=O(ccurrences) indicates the number of data sources containing the item in the Central Repository
DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export
DataResultViewerTable.firstColLbl=Name
DataResultViewerTable.goToPageTextField.err=Invalid page number
# {0} - totalPages
@ -270,3 +271,4 @@ DataResultViewerTable.pageNumLabel.text=
DataResultViewerTable.pageLabel.text=Page:
ViewPreferencesPanel.maxResultsLabel.text=Maximum number of Results to show in table:
ViewPreferencesPanel.maxResultsLabel.toolTipText=<html>\nSetting this value to 0 will display all results in the results table.\n<br>Note that setting this value to 0 may result in poor UI responsiveness when there are large numbers of results.\n</html>
DataResultViewerTable.exportCSVButton.text=Save table as CSV

View File

@ -16,13 +16,13 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="outlineView" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace pref="608" max="32767" attributes="0"/>
<Component id="outlineView" pref="904" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="pageNumLabel" min="-2" pref="53" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="14" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="pagePrevButton" linkSize="1" min="-2" pref="16" max="-2" attributes="0"/>
@ -32,14 +32,15 @@
<Component id="gotoPageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="gotoPageTextField" min="-2" pref="33" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="exportCSVButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Group type="103" groupAlignment="2" attributes="0">
<Component id="pageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="pageNumLabel" alignment="2" min="-2" max="-2" attributes="0"/>
@ -48,9 +49,10 @@
<Component id="pageNextButton" linkSize="2" alignment="2" min="-2" pref="15" max="-2" attributes="0"/>
<Component id="gotoPageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="gotoPageTextField" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="exportCSVButton" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="outlineView" pref="324" max="32767" attributes="0"/>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="outlineView" pref="321" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -164,5 +166,15 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="gotoPageTextFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="exportCSVButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerTable.exportCSVButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exportCSVButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -77,6 +77,7 @@ import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataResultViewer;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.datamodel.NodeProperty;
import org.sleuthkit.autopsy.datamodel.NodeSelectionInfo;
@ -176,6 +177,13 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
initComponents();
initializePagingSupport();
/*
* Disable the CSV export button for the common properties results
*/
if (this instanceof org.sleuthkit.autopsy.commonpropertiessearch.CommonAttributesSearchResultsViewerTable) {
exportCSVButton.setEnabled(false);
}
/*
* Configure the child OutlineView (explorer view) component.
@ -293,20 +301,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
/*
* If the given node is not null and has children, set it as the
* root context of the child OutlineView, otherwise make an
* "empty"node the root context.
*
* IMPORTANT NOTE: This is the first of many times where a
* getChildren call on the current root node causes all of the
* children of the root node to be created and defeats lazy child
* node creation, if it is enabled. It also likely leads to many
* case database round trips.
*/
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
this.rootNode = rootNode;
if (rootNode != null) {
/**
* Check to see if we have previously created a paging support
* class for this node.
@ -355,6 +350,21 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
// No-op
}
});
}
/*
* If the given node is not null and has children, set it as the
* root context of the child OutlineView, otherwise make an
* "empty"node the root context.
*
* IMPORTANT NOTE: This is the first of many times where a
* getChildren call on the current root node causes all of the
* children of the root node to be created and defeats lazy child
* node creation, if it is enabled. It also likely leads to many
* case database round trips.
*/
if (rootNode != null && rootNode.getChildren().getNodesCount() > 0) {
this.rootNode = rootNode;
this.getExplorerManager().setRootContext(this.rootNode);
setupTable();
@ -1291,6 +1301,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
outlineView = new OutlineView(DataResultViewerTable.FIRST_COLUMN_LABEL);
gotoPageLabel = new javax.swing.JLabel();
gotoPageTextField = new javax.swing.JTextField();
exportCSVButton = new javax.swing.JButton();
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.pageLabel.text")); // NOI18N
@ -1338,17 +1349,24 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
}
});
exportCSVButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerTable.class, "DataResultViewerTable.exportCSVButton.text")); // NOI18N
exportCSVButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
exportCSVButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap(608, Short.MAX_VALUE)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 904, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(pageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 53, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGap(14, 14, 14)
.addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 16, javax.swing.GroupLayout.PREFERRED_SIZE)
@ -1358,7 +1376,8 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
.addComponent(gotoPageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, 33, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(exportCSVButton))
);
layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {pageNextButton, pagePrevButton});
@ -1366,7 +1385,7 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGap(3, 3, 3)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(pageLabel)
.addComponent(pageNumLabel)
@ -1374,9 +1393,10 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 14, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 15, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(gotoPageLabel)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 324, Short.MAX_VALUE)
.addComponent(gotoPageTextField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(exportCSVButton))
.addGap(3, 3, 3)
.addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 321, Short.MAX_VALUE)
.addContainerGap())
);
@ -1397,7 +1417,19 @@ public class DataResultViewerTable extends AbstractDataResultViewer {
pagingSupport.gotoPage();
}//GEN-LAST:event_gotoPageTextFieldActionPerformed
@NbBundle.Messages({"DataResultViewerTable.exportCSVButtonActionPerformed.empty=No data to export"
})
private void exportCSVButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exportCSVButtonActionPerformed
Node currentRoot = this.getExplorerManager().getRootContext();
if (currentRoot != null && currentRoot.getChildren().getNodesCount() > 0) {
org.sleuthkit.autopsy.directorytree.ExportCSVAction.saveNodesToCSV(java.util.Arrays.asList(currentRoot.getChildren().getNodes()), this);
} else {
MessageNotifyUtil.Message.info(Bundle.DataResultViewerTable_exportCSVButtonActionPerformed_empty());
}
}//GEN-LAST:event_exportCSVButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton exportCSVButton;
private javax.swing.JLabel gotoPageLabel;
private javax.swing.JTextField gotoPageTextField;
private org.openide.explorer.view.OutlineView outlineView;

View File

@ -11,217 +11,278 @@
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,-119,0,0,4,73"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="iconView" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="pageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="pageNumLabel" min="-2" pref="95" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="pagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="pagePrevButton" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="0" max="-2" attributes="0"/>
<Component id="pageNextButton" min="-2" pref="23" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="goToPageLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="goToPageField" min="-2" pref="54" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="12" max="-2" attributes="0"/>
<Component id="imagesLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="imagesRangeLabel" min="-2" pref="91" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="thumbnailSizeComboBox" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="30" max="-2" attributes="0"/>
<Component id="sortButton" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="sortLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="filePathLabel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="2" attributes="0">
<Component id="pageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="pageNumLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="pagesLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="pagePrevButton" alignment="2" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="pageNextButton" alignment="2" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="goToPageLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="goToPageField" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="imagesLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="imagesRangeLabel" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="thumbnailSizeComboBox" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="sortButton" alignment="2" min="-2" max="-2" attributes="0"/>
<Component id="sortLabel" alignment="2" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
<Component id="iconView" pref="322" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="filePathLabel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="pageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="pagePrevButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagePrevButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 0, 2, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[55, 23]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pagePrevButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="pageNextButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 0, 2, 0]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pageNextButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="imagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.imagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="imagesRangeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.imagesRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="pageNumLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageNumLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="buttonBarPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="North"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout">
<Property name="alignment" type="int" value="0"/>
</Layout>
<SubComponents>
<Container class="javax.swing.JPanel" name="pagesPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Container class="javax.swing.JPanel" name="pageNumberPane">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="23" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="pageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="23" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="pageNumLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageNumLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="15" anchor="23" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pageButtonPanel">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="pagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="17" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="pagePrevButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pagePrevButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 0, 0, 0]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pagePrevButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="2" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="23" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="pageNextButton">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"/>
</Property>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.pageNextButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="null"/>
</Property>
<Property name="disabledIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"/>
</Property>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[0, 0, 0, 0]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[27, 23]"/>
</Property>
<Property name="rolloverIcon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pageNextButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="2" gridY="0" gridWidth="1" gridHeight="2" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="15" anchor="23" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="pageGotoPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="goToPageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.goToPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="17" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JTextField" name="goToPageField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.goToPageField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="goToPageFieldActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="2" fill="0" ipadX="75" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="15" anchor="23" weightX="1.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="imagePane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="imagesLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.imagesLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="9" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JLabel" name="imagesRangeLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.imagesRangeLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="15" anchor="10" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JComboBox" name="thumbnailSizeComboBox">
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="thumbnailSizeComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JComboBox&lt;&gt;()"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Container class="javax.swing.JPanel" name="sortPane">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
<SubComponents>
<Component class="javax.swing.JLabel" name="sortLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.sortLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="1" gridY="0" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="23" weightX="0.0" weightY="1.0"/>
</Constraint>
</Constraints>
</Component>
<Component class="javax.swing.JButton" name="sortButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.sortButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sortButtonActionPerformed"/>
</Events>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
<GridBagConstraints gridX="0" gridY="0" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="15" insetsBottom="0" insetsRight="9" anchor="23" weightX="0.0" weightY="0.0"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="filePathLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.filePathLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="goToPageLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.goToPageLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="goToPageField">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.goToPageField.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="goToPageFieldActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="thumbnailSizeComboBox">
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="thumbnailSizeComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new javax.swing.JComboBox&lt;&gt;()"/>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="South"/>
</Constraint>
</Constraints>
</Component>
<Component class="org.openide.explorer.view.IconView" name="iconView">
</Component>
<Component class="javax.swing.JButton" name="sortButton">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.sortButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sortButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="sortLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor">
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="DataResultViewerThumbnail.sortLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties>
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout" value="org.netbeans.modules.form.compat2.layouts.DesignBorderLayout$BorderConstraintsDescription">
<BorderConstraints direction="Center"/>
</Constraint>
</Constraints>
</Component>
</SubComponents>
</Form>

View File

@ -122,6 +122,11 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
currentPage = -1;
totalPages = 0;
currentPageImages = 0;
// The GUI builder is using FlowLayout therefore this change so have no
// impact on the initally designed layout. This change will just effect
// how the components are laid out as size of the window changes.
buttonBarPanel.setLayout(new WrapLayout(java.awt.FlowLayout.LEFT));
}
/**
@ -132,33 +137,84 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
java.awt.GridBagConstraints gridBagConstraints;
buttonBarPanel = new javax.swing.JPanel();
pagesPanel = new javax.swing.JPanel();
pageNumberPane = new javax.swing.JPanel();
pageLabel = new javax.swing.JLabel();
pageNumLabel = new javax.swing.JLabel();
pageButtonPanel = new javax.swing.JPanel();
pagesLabel = new javax.swing.JLabel();
pagePrevButton = new javax.swing.JButton();
pageNextButton = new javax.swing.JButton();
imagesLabel = new javax.swing.JLabel();
imagesRangeLabel = new javax.swing.JLabel();
pageNumLabel = new javax.swing.JLabel();
filePathLabel = new javax.swing.JLabel();
pageGotoPane = new javax.swing.JPanel();
goToPageLabel = new javax.swing.JLabel();
goToPageField = new javax.swing.JTextField();
imagePane = new javax.swing.JPanel();
imagesLabel = new javax.swing.JLabel();
imagesRangeLabel = new javax.swing.JLabel();
thumbnailSizeComboBox = new javax.swing.JComboBox<>();
iconView = new org.openide.explorer.view.IconView();
sortButton = new javax.swing.JButton();
sortPane = new javax.swing.JPanel();
sortLabel = new javax.swing.JLabel();
sortButton = new javax.swing.JButton();
filePathLabel = new javax.swing.JLabel();
iconView = new org.openide.explorer.view.IconView();
setLayout(new java.awt.BorderLayout());
buttonBarPanel.setLayout(new java.awt.FlowLayout(java.awt.FlowLayout.LEFT));
pagesPanel.setLayout(new java.awt.GridBagLayout());
pageNumberPane.setLayout(new java.awt.GridBagLayout());
pageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9);
pageNumberPane.add(pageLabel, gridBagConstraints);
pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNumLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15);
pageNumberPane.add(pageNumLabel, gridBagConstraints);
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
pagesPanel.add(pageNumberPane, gridBagConstraints);
buttonBarPanel.add(pagesPanel);
pageButtonPanel.setLayout(new java.awt.GridBagLayout());
pagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9);
pageButtonPanel.add(pagesLabel, gridBagConstraints);
pagePrevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back.png"))); // NOI18N
pagePrevButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pagePrevButton.text")); // NOI18N
pagePrevButton.setBorder(null);
pagePrevButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_disabled.png"))); // NOI18N
pagePrevButton.setFocusable(false);
pagePrevButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
pagePrevButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
pagePrevButton.setPreferredSize(new java.awt.Dimension(55, 23));
pagePrevButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
pagePrevButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_back_hover.png"))); // NOI18N
pagePrevButton.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
pagePrevButton.addActionListener(new java.awt.event.ActionListener() {
@ -166,13 +222,20 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
pagePrevButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
pageButtonPanel.add(pagePrevButton, gridBagConstraints);
pageNextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward.png"))); // NOI18N
pageNextButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNextButton.text")); // NOI18N
pageNextButton.setBorder(null);
pageNextButton.setDisabledIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_disabled.png"))); // NOI18N
pageNextButton.setFocusable(false);
pageNextButton.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
pageNextButton.setMargin(new java.awt.Insets(2, 0, 2, 0));
pageNextButton.setMargin(new java.awt.Insets(0, 0, 0, 0));
pageNextButton.setMaximumSize(new java.awt.Dimension(27, 23));
pageNextButton.setMinimumSize(new java.awt.Dimension(27, 23));
pageNextButton.setRolloverIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/btn_step_forward_hover.png"))); // NOI18N
@ -182,16 +245,27 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
pageNextButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 2;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 2;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15);
pageButtonPanel.add(pageNextButton, gridBagConstraints);
imagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesLabel.text")); // NOI18N
buttonBarPanel.add(pageButtonPanel);
imagesRangeLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesRangeLabel.text")); // NOI18N
pageNumLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.pageNumLabel.text")); // NOI18N
filePathLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.filePathLabel.text")); // NOI18N
pageGotoPane.setLayout(new java.awt.GridBagLayout());
goToPageLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.goToPageLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
gridBagConstraints.weighty = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9);
pageGotoPane.add(goToPageLabel, gridBagConstraints);
goToPageField.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.goToPageField.text")); // NOI18N
goToPageField.addActionListener(new java.awt.event.ActionListener() {
@ -199,12 +273,49 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
goToPageFieldActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.gridheight = 2;
gridBagConstraints.ipadx = 75;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weightx = 1.0;
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15);
pageGotoPane.add(goToPageField, gridBagConstraints);
buttonBarPanel.add(pageGotoPane);
imagePane.setLayout(new java.awt.GridBagLayout());
imagesLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 9);
imagePane.add(imagesLabel, gridBagConstraints);
imagesRangeLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.imagesRangeLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 15);
imagePane.add(imagesRangeLabel, gridBagConstraints);
buttonBarPanel.add(imagePane);
thumbnailSizeComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
thumbnailSizeComboBoxActionPerformed(evt);
}
});
buttonBarPanel.add(thumbnailSizeComboBox);
sortPane.setLayout(new java.awt.GridBagLayout());
sortLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortLabel.text")); // NOI18N
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 1;
gridBagConstraints.gridy = 0;
gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.weighty = 1.0;
sortPane.add(sortLabel, gridBagConstraints);
sortButton.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortButton.text")); // NOI18N
sortButton.addActionListener(new java.awt.event.ActionListener() {
@ -212,65 +323,20 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
sortButtonActionPerformed(evt);
}
});
gridBagConstraints = new java.awt.GridBagConstraints();
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = java.awt.GridBagConstraints.FIRST_LINE_START;
gridBagConstraints.insets = new java.awt.Insets(0, 15, 0, 9);
sortPane.add(sortButton, gridBagConstraints);
sortLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.sortLabel.text")); // NOI18N
buttonBarPanel.add(sortPane);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, layout.createSequentialGroup()
.addComponent(pageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(pageNumLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 95, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(0, 0, 0)
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(goToPageLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, 54, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(12, 12, 12)
.addComponent(imagesLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(imagesRangeLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 91, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(30, 30, 30)
.addComponent(sortButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(sortLabel))
.addComponent(filePathLabel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
.addComponent(pageLabel)
.addComponent(pageNumLabel)
.addComponent(pagesLabel)
.addComponent(pagePrevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(pageNextButton, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(goToPageLabel)
.addComponent(goToPageField, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(imagesLabel)
.addComponent(imagesRangeLabel)
.addComponent(thumbnailSizeComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(sortButton)
.addComponent(sortLabel))
.addGap(13, 13, 13)
.addComponent(iconView, javax.swing.GroupLayout.DEFAULT_SIZE, 322, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(filePathLabel))
);
add(buttonBarPanel, java.awt.BorderLayout.NORTH);
filePathLabel.setText(org.openide.util.NbBundle.getMessage(DataResultViewerThumbnail.class, "DataResultViewerThumbnail.filePathLabel.text")); // NOI18N
add(filePathLabel, java.awt.BorderLayout.SOUTH);
add(iconView, java.awt.BorderLayout.CENTER);
}// </editor-fold>//GEN-END:initComponents
private void pagePrevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pagePrevButtonActionPerformed
@ -355,19 +421,26 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel buttonBarPanel;
private javax.swing.JLabel filePathLabel;
private javax.swing.JTextField goToPageField;
private javax.swing.JLabel goToPageLabel;
private org.openide.explorer.view.IconView iconView;
private javax.swing.JPanel imagePane;
private javax.swing.JLabel imagesLabel;
private javax.swing.JLabel imagesRangeLabel;
private javax.swing.JPanel pageButtonPanel;
private javax.swing.JPanel pageGotoPane;
private javax.swing.JLabel pageLabel;
private javax.swing.JButton pageNextButton;
private javax.swing.JLabel pageNumLabel;
private javax.swing.JPanel pageNumberPane;
private javax.swing.JButton pagePrevButton;
private javax.swing.JLabel pagesLabel;
private javax.swing.JPanel pagesPanel;
private javax.swing.JButton sortButton;
private javax.swing.JLabel sortLabel;
private javax.swing.JPanel sortPane;
private javax.swing.JComboBox<String> thumbnailSizeComboBox;
// End of variables declaration//GEN-END:variables
@ -679,5 +752,5 @@ public final class DataResultViewerThumbnail extends AbstractDataResultViewer {
}
}
}
}
}
}

View File

@ -23,6 +23,7 @@ import java.util.EnumSet;
import java.util.Objects;
import java.util.TimeZone;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
@ -55,6 +56,9 @@ public class ViewPreferencesPanel extends JPanel implements OptionsPanel {
groupByDataSourceCheckbox.setEnabled(evt.getNewValue() != null);
});
this.timeZoneList.setListData(TimeZoneUtils.createTimeZoneList().stream().toArray(String[]::new));
// Disable manual editing of max results spinner
((JSpinner.DefaultEditor)maxResultsSpinner.getEditor()).getTextField().setEditable(false);
}
@Override

View File

@ -0,0 +1,203 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.corecomponents;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Insets;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
/**
* FlowLayout subclass that fully supports wrapping of components.
*
* Originally written by Rob Camick
* https://tips4java.wordpress.com/2008/11/06/wrap-layout/
*/
class WrapLayout extends FlowLayout {
/**
* Constructs a new <code>WrapLayout</code> with a left alignment and a
* default 5-unit horizontal and vertical gap.
*/
public WrapLayout() {
super();
}
/**
* Constructs a new <code>FlowLayout</code> with the specified alignment
* and a default 5-unit horizontal and vertical gap. The value of the
* alignment argument must be one of <code>WrapLayout</code>,
* <code>WrapLayout</code>, or <code>WrapLayout</code>.
*
* @param align the alignment value
*/
public WrapLayout(int align) {
super(align);
}
/**
* Creates a new flow layout manager with the indicated alignment and
* the indicated horizontal and vertical gaps.
* <p>
* The value of the alignment argument must be one of
* <code>WrapLayout</code>, <code>WrapLayout</code>, or
* <code>WrapLayout</code>.
*
* @param align the alignment value
* @param hgap the horizontal gap between components
* @param vgap the vertical gap between components
*/
public WrapLayout(int align, int hgap, int vgap) {
super(align, hgap, vgap);
}
/**
* Returns the preferred dimensions for this layout given the
* <i>visible</i> components in the specified target container.
*
* @param target the component which needs to be laid out
*
* @return the preferred dimensions to lay out the subcomponents of the
* specified container
*/
@Override
public Dimension preferredLayoutSize(Container target) {
return layoutSize(target, true);
}
/**
* Returns the minimum dimensions needed to layout the <i>visible</i>
* components contained in the specified target container.
*
* @param target the component which needs to be laid out
*
* @return the minimum dimensions to lay out the subcomponents of the
* specified container
*/
@Override
public Dimension minimumLayoutSize(Container target) {
Dimension minimum = layoutSize(target, false);
minimum.width -= (getHgap() + 1);
return minimum;
}
/**
* Returns the minimum or preferred dimension needed to layout the
* target container.
*
* @param target target to get layout size for
* @param preferred should preferred size be calculated
*
* @return the dimension to layout the target container
*/
private Dimension layoutSize(Container target, boolean preferred) {
synchronized (target.getTreeLock()) {
// Each row must fit with the width allocated to the containter.
// When the container width = 0, the preferred width of the container
// has not yet been calculated so lets ask for the maximum.
int targetWidth = target.getSize().width;
Container container = target;
while (container.getSize().width == 0 && container.getParent() != null) {
container = container.getParent();
}
targetWidth = container.getSize().width;
if (targetWidth == 0) {
targetWidth = Integer.MAX_VALUE;
}
int hgap = getHgap();
int vgap = getVgap();
Insets insets = target.getInsets();
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
int maxWidth = targetWidth - horizontalInsetsAndGap;
// Fit components into the allowed width
Dimension dim = new Dimension(0, 0);
int rowWidth = 0;
int rowHeight = 0;
int nmembers = target.getComponentCount();
for (int i = 0; i < nmembers; i++) {
Component m = target.getComponent(i);
if (m.isVisible()) {
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
// Can't add the component to current row. Start a new row.
if (rowWidth + d.width > maxWidth) {
addRow(dim, rowWidth, rowHeight);
rowWidth = 0;
rowHeight = 0;
}
// Add a horizontal gap for all components after the first
if (rowWidth != 0) {
rowWidth += hgap;
}
rowWidth += d.width;
rowHeight = Math.max(rowHeight, d.height);
}
}
addRow(dim, rowWidth, rowHeight);
dim.width += horizontalInsetsAndGap;
dim.height += insets.top + insets.bottom + vgap * 2;
// When using a scroll pane or the DecoratedLookAndFeel we need to
// make sure the preferred size is less than the size of the
// target containter so shrinking the container size works
// correctly. Removing the horizontal gap is an easy way to do this.
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
if (scrollPane != null && target.isValid()) {
dim.width -= (hgap + 1);
}
return dim;
}
}
/*
* A new row has been completed. Use the dimensions of this row to
* update the preferred size for the container.
*
* @param dim update the width and height when appropriate @param
* rowWidth the width of the row to add @param rowHeight the height of
* the row to add
*/
private void addRow(Dimension dim, int rowWidth, int rowHeight) {
dim.width = Math.max(dim.width, rowWidth);
if (dim.height > 0) {
dim.height += getVgap();
}
dim.height += rowHeight;
}
}

View File

@ -221,7 +221,8 @@ public class StringExtract {
StringExtractResult resWin = null;
if (enableUTF8 && resUTF16 != null) {
resWin = runUTF16 && resUTF16.numChars > resUTF8.numChars ? resUTF16 : resUTF8;
} else if (enableUTF16) {
} else if (runUTF16) {
//Only let resUTF16 "win" if it was actually run.
resWin = resUTF16;
} else if (enableUTF8) {
resWin = resUTF8;

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -18,7 +18,6 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
@ -27,8 +26,6 @@ import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import java.util.stream.Collectors;
import org.apache.commons.io.FilenameUtils;
@ -65,6 +62,7 @@ import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.ContentTag;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
@ -76,15 +74,10 @@ import org.sleuthkit.datamodel.TskData;
public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends AbstractContentNode<T> {
private static final Logger logger = Logger.getLogger(AbstractAbstractFileNode.class.getName());
@NbBundle.Messages("AbstractAbstractFileNode.addFileProperty.desc=no description")
private static final String NO_DESCR = AbstractAbstractFileNode_addFileProperty_desc();
private static final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.CURRENT_CASE,
Case.Events.CONTENT_TAG_ADDED, Case.Events.CONTENT_TAG_DELETED, Case.Events.CR_COMMENT_CHANGED);
private static final ExecutorService translationPool;
private static final Integer MAX_POOL_SIZE = 10;
/**
* @param abstractFile file to wrap
*/
@ -101,7 +94,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
}
if (UserPreferences.displayTranslatedFileNames()) {
AbstractAbstractFileNode.translationPool.submit(new TranslationTask(
backgroundTasksPool.submit(new TranslationTask(
new WeakReference<>(this), weakPcl));
}
@ -110,13 +103,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
Case.addEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
translationPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("translation-task-thread-%d").build());
}
/**
* The finalizer removes event listeners as the BlackboardArtifactNode is
* being garbage collected. Yes, we know that finalizers are considered to
@ -137,16 +123,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
Case.removeEventTypeSubscriber(CASE_EVENTS_OF_INTEREST, weakPcl);
}
/**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
* 1) Retreiving the translation of the file name
*/
enum NodeSpecificEvents {
TRANSLATION_AVAILABLE,
}
private final PropertyChangeListener pcl = (PropertyChangeEvent evt) -> {
String eventType = evt.getPropertyName();
@ -190,7 +166,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_ADDED.toString())) {
ContentTagAddedEvent event = (ContentTagAddedEvent) evt;
if (event.getAddedTag().getContent().equals(content)) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = this.getAllTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@ -202,7 +178,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CONTENT_TAG_DELETED.toString())) {
ContentTagDeletedEvent event = (ContentTagDeletedEvent) evt;
if (event.getDeletedTagInfo().getContentID() == content.getId()) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = getAllTagsFromDatabase();
Pair<Score, String> scorePropAndDescr = getScorePropertyAndDescription(tags);
Score value = scorePropAndDescr.getLeft();
String descr = scorePropAndDescr.getRight();
@ -214,7 +190,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} else if (eventType.equals(Case.Events.CR_COMMENT_CHANGED.toString())) {
CommentChangedEvent event = (CommentChangedEvent) evt;
if (event.getContentID() == content.getId()) {
List<ContentTag> tags = getContentTagsFromDatabase();
List<Tag> tags = getAllTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, getCommentProperty(tags, attribute)));
}
@ -223,6 +199,18 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
//Set the tooltip
this.setShortDescription(content.getName());
updateSheet(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, content.getName()));
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
SCOData scoData = (SCOData) evt.getNewValue();
if (scoData.getScoreAndDescription() != null) {
updateSheet(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, scoData.getComment()));
}
if (scoData.getCountAndDescription() != null
&& !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
updateSheet(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
};
/**
@ -235,38 +223,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
*/
private final PropertyChangeListener weakPcl = WeakListeners.propertyChange(pcl, null);
/**
* Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
* current sheet will it be applied. That way, we allow for subclasses to
* add their own (or omit some!) properties and we will not accidentally
* disrupt their UI.
*
* Race condition if not synchronized. Only one update should be applied at
* a time.
*
* @param newProps New file property instances to be updated in the current
* sheet.
*/
private synchronized void updateSheet(NodeProperty<?>... newProps) {
//Refresh ONLY those properties in the sheet currently. Subclasses may have
//only added a subset of our properties or their own props.s
Sheet visibleSheet = this.getSheet();
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
Property<?>[] visibleProps = visibleSheetSet.getProperties();
for (NodeProperty<?> newProp : newProps) {
for (int i = 0; i < visibleProps.length; i++) {
if (visibleProps[i].getName().equals(newProp.getName())) {
visibleProps[i] = newProp;
}
}
}
visibleSheetSet.put(visibleProps);
visibleSheet.put(visibleSheetSet);
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
/*
* This is called when the node is first initialized. Any new updates or
* changes happen by directly manipulating the sheet. That means we can fire
@ -368,18 +324,17 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
properties.add(new NodeProperty<>(ORIGINAL_NAME.toString(), ORIGINAL_NAME.toString(), NO_DESCR, ""));
}
//SCO column prereq info..
List<ContentTag> tags = getContentTagsFromDatabase();
CorrelationAttributeInstance attribute = getCorrelationAttributeInstance();
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
DataResultViewerTable.HasCommentStatus comment = getCommentProperty(tags, attribute);
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), NO_DESCR, comment));
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute);
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), countAndDescription.getRight(), countAndDescription.getLeft()));
// Create place holders for S C O
properties.add(new NodeProperty<>(SCORE.toString(), SCORE.toString(), VALUE_LOADING, ""));
properties.add(new NodeProperty<>(COMMENT.toString(), COMMENT.toString(), VALUE_LOADING, ""));
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
properties.add(new NodeProperty<>(OCCURRENCES.toString(), OCCURRENCES.toString(), VALUE_LOADING, ""));
}
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
new WeakReference<>(this), weakPcl));
properties.add(new NodeProperty<>(LOCATION.toString(), LOCATION.toString(), NO_DESCR, getContentPath(content)));
properties.add(new NodeProperty<>(MOD_TIME.toString(), MOD_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getMtime(), content)));
properties.add(new NodeProperty<>(CHANGED_TIME.toString(), CHANGED_TIME.toString(), NO_DESCR, ContentUtils.getStringTime(content.getCtime(), content)));
@ -437,19 +392,19 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.count.displayName=O",
"AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated",
"# {0} - occuranceCount",
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance attribute) {
"# {0} - occurenceCount",
"AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurences of the MD5 correlation value"})
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
String description = Bundle.AbstractAbstractFileNode_createSheet_count_noCentralRepo_description();
String description = defaultDescription;
try {
//don't perform the query if there is no correlation value
if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
if (attributeType != null && StringUtils.isNotBlank(attributeValue)) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue);
description = Bundle.AbstractAbstractFileNode_createSheet_count_description(count);
} else if (attribute != null) {
} else if (attributeType != null) {
description = Bundle.AbstractAbstractFileNode_createSheet_count_hashLookupNotRun_description();
}
} catch (EamDbException ex) {
@ -457,7 +412,6 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
return Pair.of(count, description);
}
@ -468,7 +422,8 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
"AbstractAbstractFileNode.createSheet.taggedFile.description=File has been tagged.",
"AbstractAbstractFileNode.createSheet.notableTaggedFile.description=File tagged with notable tag.",
"AbstractAbstractFileNode.createSheet.noScore.description=No score"})
Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<ContentTag> tags) {
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
DataResultViewerTable.Score score = DataResultViewerTable.Score.NO_SCORE;
String description = Bundle.AbstractAbstractFileNode_createSheet_noScore_description();
if (content.getKnown() == TskData.FileKnown.BAD) {
@ -486,7 +441,7 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
if (!tags.isEmpty() && (score == DataResultViewerTable.Score.NO_SCORE || score == DataResultViewerTable.Score.INTERESTING_SCORE)) {
score = DataResultViewerTable.Score.INTERESTING_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_taggedFile_description();
for (ContentTag tag : tags) {
for (Tag tag : tags) {
if (tag.getName().getKnownStatus() == TskData.FileKnown.BAD) {
score = DataResultViewerTable.Score.NOTABLE_SCORE;
description = Bundle.AbstractAbstractFileNode_createSheet_notableTaggedFile_description();
@ -499,11 +454,12 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
@NbBundle.Messages({
"AbstractAbstractFileNode.createSheet.comment.displayName=C"})
HasCommentStatus getCommentProperty(List<ContentTag> tags, CorrelationAttributeInstance attribute) {
@Override
protected HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
DataResultViewerTable.HasCommentStatus status = !tags.isEmpty() ? DataResultViewerTable.HasCommentStatus.TAG_NO_COMMENT : DataResultViewerTable.HasCommentStatus.NO_COMMENT;
for (ContentTag tag : tags) {
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
//if the tag is null or empty or contains just white space it will indicate there is not a comment
status = DataResultViewerTable.HasCommentStatus.TAG_COMMENT;
@ -571,7 +527,13 @@ public abstract class AbstractAbstractFileNode<T extends AbstractFile> extends A
return tags;
}
CorrelationAttributeInstance getCorrelationAttributeInstance() {
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>(getContentTagsFromDatabase());
}
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance attribute = null;
if (EamDb.isEnabled() && !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
attribute = EamArtifactUtil.getInstanceFromContent(content);

View File

@ -18,20 +18,30 @@
*/
package org.sleuthkit.autopsy.datamodel;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.logging.Level;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Children;
import org.openide.nodes.Sheet;
import org.openide.util.lookup.Lookups;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.Content;
import org.sleuthkit.datamodel.SleuthkitCase;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.TskData;
import org.sleuthkit.datamodel.TskException;
@ -51,27 +61,60 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
private static final Logger logger = Logger.getLogger(AbstractContentNode.class.getName());
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
* A pool of background tasks to run any long computation needed to populate
* this node.
*/
AbstractContentNode(T content) {
this(content, Lookups.singleton(content) );
static final ExecutorService backgroundTasksPool;
private static final Integer MAX_POOL_SIZE = 10;
/**
* Default no description string
*/
@NbBundle.Messages({"AbstractContentNode.nodescription=no description",
"AbstractContentNode.valueLoading=value loading"})
protected static final String NO_DESCR = Bundle.AbstractContentNode_nodescription();
protected static final String VALUE_LOADING = Bundle.AbstractContentNode_valueLoading();
/**
* Event signals to indicate the background tasks have completed processing.
* Currently, we have one property task in the background:
*
* 1) Retrieving the translation of the file name
*/
enum NodeSpecificEvents {
TRANSLATION_AVAILABLE,
SCO_AVAILABLE
}
static {
//Initialize this pool only once! This will be used by every instance of AAFN
//to do their heavy duty SCO column and translation updates.
backgroundTasksPool = Executors.newFixedThreadPool(MAX_POOL_SIZE,
new ThreadFactoryBuilder().setNameFormat("content-node-background-task-%d").build());
}
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
* @param lookup The Lookup object for the node.
*/
AbstractContentNode(T content) {
this(content, Lookups.singleton(content));
}
/**
* Handles aspects that depend on the Content object
*
* @param content Underlying Content instances
* @param lookup The Lookup object for the node.
*/
AbstractContentNode(T content, Lookup lookup) {
super(Children.create(new ContentChildren(content), true), lookup);
super(Children.create(new ContentChildren(content), false), lookup);
this.content = content;
//super.setName(ContentUtils.getSystemName(content));
super.setName("content_" + Long.toString(content.getId())); //NON-NLS
}
/**
* Return the content data associated with this node
*
@ -100,40 +143,40 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
public boolean hasVisibleContentChildren() {
return contentHasVisibleContentChildren(content);
}
/**
* Return true if the given content object has children. Useful for lazy
* loading.
*
*
* @param c The content object to look for children on
*
* @return true if has children
*/
public static boolean contentHasVisibleContentChildren(Content c){
public static boolean contentHasVisibleContentChildren(Content c) {
if (c != null) {
try {
if( ! c.hasChildren()) {
if (!c.hasChildren()) {
return false;
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error checking if the node has children, for content: " + c, ex); //NON-NLS
return false;
}
String query = "SELECT COUNT(obj_id) AS count FROM "
+ " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
+ TskData.ObjectType.ARTIFACT.getObjectType()
+ " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId()
+ " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
+ " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") "
+ " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId()
+ " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS;
+ " ( SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId() + " AND type = "
+ TskData.ObjectType.ARTIFACT.getObjectType()
+ " INTERSECT SELECT artifact_obj_id FROM blackboard_artifacts WHERE obj_id = " + c.getId()
+ " AND (artifact_type_id = " + ARTIFACT_TYPE.TSK_EMAIL_MSG.getTypeID()
+ " OR artifact_type_id = " + ARTIFACT_TYPE.TSK_MESSAGE.getTypeID() + ") "
+ " UNION SELECT obj_id FROM tsk_objects WHERE par_obj_id = " + c.getId()
+ " AND type = " + TskData.ObjectType.ABSTRACTFILE.getObjectType() + ") AS OBJECT_IDS"; //NON-NLS;
try (SleuthkitCase.CaseDbQuery dbQuery = Case.getCurrentCaseThrows().getSleuthkitCase().executeQuery(query)) {
ResultSet resultSet = dbQuery.getResultSet();
if(resultSet.next()){
if (resultSet.next()) {
return (0 < resultSet.getInt("count"));
}
} catch (TskCoreException | SQLException | NoCurrentCaseException ex) {
@ -142,7 +185,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
}
return false;
}
/**
* Return true if the underlying content object has children Useful for lazy
* loading.
@ -162,7 +205,7 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
return hasChildren;
}
/**
* Return ids of children of the underlying content. The ids can be treated
* as keys - useful for lazy loading.
@ -240,4 +283,83 @@ public abstract class AbstractContentNode<T extends Content> extends ContentNode
public int read(byte[] buf, long offset, long len) throws TskException {
return content.read(buf, offset, len);
}
/**
* Updates the values of the properties in the current property sheet with
* the new properties being passed in. Only if that property exists in the
* current sheet will it be applied. That way, we allow for subclasses to
* add their own (or omit some!) properties and we will not accidentally
* disrupt their UI.
*
* Race condition if not synchronized. Only one update should be applied at
* a time.
*
* @param newProps New file property instances to be updated in the current
* sheet.
*/
protected synchronized void updateSheet(NodeProperty<?>... newProps) {
//Refresh ONLY those properties in the sheet currently. Subclasses may have
//only added a subset of our properties or their own props.s
Sheet visibleSheet = this.getSheet();
Sheet.Set visibleSheetSet = visibleSheet.get(Sheet.PROPERTIES);
Property<?>[] visibleProps = visibleSheetSet.getProperties();
for (NodeProperty<?> newProp : newProps) {
for (int i = 0; i < visibleProps.length; i++) {
if (visibleProps[i].getName().equals(newProp.getName())) {
visibleProps[i] = newProp;
}
}
}
visibleSheetSet.put(visibleProps);
visibleSheet.put(visibleSheetSet);
//setSheet() will notify Netbeans to update this node in the UI.
this.setSheet(visibleSheet);
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* @return list of tags associated with the node.
*/
abstract protected List<Tag> getAllTagsFromDatabase();
/**
* Returns correlation attribute instance for the underlying content of the
* node.
*
* @return correlation attribute instance for the underlying content of the
* node.
*/
abstract protected CorrelationAttributeInstance getCorrelationAttributeInstance();
/**
* Returns Score property for the node.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
abstract protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags);
/**
* Returns comment property for the node.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
abstract protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute);
/**
* Returns occurrences/count property for the node.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to count
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count property for the underlying content of the node.
*/
abstract protected Pair<Long, String> getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription);
}

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2018 Basis Technology Corp.
* Copyright 2011-2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -22,6 +22,7 @@ import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
@ -37,6 +38,7 @@ import java.util.logging.Level;
import java.util.stream.Collectors;
import javax.swing.Action;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.Lookup;
import org.openide.util.NbBundle;
@ -50,16 +52,19 @@ import org.sleuthkit.autopsy.casemodule.events.CommentChangedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagAddedEvent;
import org.sleuthkit.autopsy.casemodule.events.ContentTagDeletedEvent;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeNormalizationException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.Score;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import static org.sleuthkit.autopsy.datamodel.DisplayableItemNode.findLinked;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable.HasCommentStatus;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.backgroundTasksPool;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager;
import org.sleuthkit.autopsy.timeline.actions.ViewArtifactInTimelineAction;
import org.sleuthkit.autopsy.timeline.actions.ViewFileInTimelineAction;
@ -96,9 +101,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
private List<NodeProperty<? extends Object>> customProperties;
private final static String NO_DESCR = NbBundle.getMessage(BlackboardArtifactNode.class, "BlackboardArtifactNode.noDesc.text");
/*
* Artifact types which should have the full unique path of the associated
* content as a property.
@ -150,6 +152,18 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
removeListeners();
contentCache.invalidateAll();
}
} else if (eventType.equals(NodeSpecificEvents.SCO_AVAILABLE.toString())) {
SCOData scoData = (SCOData) evt.getNewValue();
if (scoData.getScoreAndDescription() != null) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoData.getScoreAndDescription().getRight(), scoData.getScoreAndDescription().getLeft()));
}
if (scoData.getComment() != null) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR, scoData.getComment()));
}
if (scoData.getCountAndDescription() != null
&& !UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
updateSheet(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), scoData.getCountAndDescription().getRight(), scoData.getCountAndDescription().getLeft()));
}
}
}
};
@ -319,7 +333,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
return srcName;
}
@NbBundle.Messages({
"BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type",
"BlackboardArtifactNode.createSheet.artifactType.name=Result Type",
@ -335,8 +349,6 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
@Override
protected Sheet createSheet() {
Sheet sheet = super.createSheet();
List<Tag> tags = getAllTagsFromDatabase();
Sheet.Set sheetSet = sheet.get(Sheet.PROPERTIES);
if (sheetSet == null) {
sheetSet = Sheet.createPropertiesSet();
@ -351,17 +363,17 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
NO_DESCR,
this.getSourceName()));
addScoreProperty(sheetSet, tags);
CorrelationAttributeInstance correlationAttribute = null;
// Create place holders for S C O
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), VALUE_LOADING, ""));
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), VALUE_LOADING, ""));
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
correlationAttribute = getCorrelationAttributeInstance();
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), VALUE_LOADING, ""));
}
addCommentProperty(sheetSet, tags, correlationAttribute);
if (UserPreferences.hideCentralRepoCommentsAndOccurrences() == false) {
addCountProperty(sheetSet, correlationAttribute);
}
// Get the SCO columns data in a background task
backgroundTasksPool.submit(new GetSCOTask(
new WeakReference<>(this), weakPcl));
if (artifact.getArtifactTypeID() == ARTIFACT_TYPE.TSK_INTERESTING_ARTIFACT_HIT.getTypeID()) {
try {
BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(ATTRIBUTE_TYPE.TSK_ASSOCIATED_ARTIFACT));
@ -520,6 +532,7 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* @return a list of tags which on the artifact or the file it is associated
* with
*/
@Override
protected final List<Tag> getAllTagsFromDatabase() {
List<Tag> tags = new ArrayList<>();
try {
@ -569,6 +582,13 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
NO_DESCR, tags.stream().map(t -> t.getName().getDisplayName()).collect(Collectors.joining(", "))));
}
/**
* Gets the correlation attribute for the associated file
*
* @return the correlation attribute for the file associated with this
* BlackboardArtifactNode
*/
@Override
protected final CorrelationAttributeInstance getCorrelationAttributeInstance() {
CorrelationAttributeInstance correlationAttribute = null;
if (EamDb.isEnabled()) {
@ -581,16 +601,37 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
* Used by (subclasses of) BlackboardArtifactNode to add the comment
* property to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param tags the list of tags associated with the file
* @param attribute the correlation attribute associated with this
* artifact's associated file, null if central repo is not
* enabled
*
* @deprecated Use the GetSCOTask to get this data on a background
* thread..., and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.comment.name=C",
"BlackboardArtifactNode.createSheet.comment.displayName=C"})
@Deprecated
protected final void addCommentProperty(Sheet.Set sheetSet, List<Tag> tags, CorrelationAttributeInstance attribute) {
HasCommentStatus status = getCommentProperty(tags, attribute);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
status));
}
/**
* Gets the comment property for the node
*
* @param tags the list of tags associated with the file
* @param attribute the correlation attribute associated with this
* artifact's associated file, null if central repo is not
* enabled
*
* @return comment property
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
HasCommentStatus status = tags.size() > 0 ? HasCommentStatus.TAG_NO_COMMENT : HasCommentStatus.NO_COMMENT;
for (Tag tag : tags) {
if (!StringUtils.isBlank(tag.getComment())) {
@ -609,17 +650,18 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
status = HasCommentStatus.CR_COMMENT;
}
}
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_comment_name(), Bundle.BlackboardArtifactNode_createSheet_comment_displayName(), NO_DESCR,
status));
return status;
}
/**
* Used by (subclasses of) BlackboardArtifactNode to add the Score property
* to their sheets.
*
* @param sheetSet the modifiable Sheet.Set returned by
* Sheet.get(Sheet.PROPERTIES)
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param tags the list of tags associated with the file
*
* @deprecated Use the GetSCOTask to get this data on a background
* thread..., and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.score.name=S",
"BlackboardArtifactNode.createSheet.score.displayName=S",
@ -628,7 +670,21 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
"BlackboardArtifactNode.createSheet.taggedItem.description=Result or associated file has been tagged.",
"BlackboardArtifactNode.createSheet.notableTaggedItem.description=Result or associated file tagged with notable tag.",
"BlackboardArtifactNode.createSheet.noScore.description=No score"})
protected final void addScoreProperty(Sheet.Set sheetSet, List<Tag> tags) {
@Deprecated
protected final void addScorePropertyAndDescription(Sheet.Set sheetSet, List<Tag> tags) {
Pair<DataResultViewerTable.Score, String> scoreAndDescription = getScorePropertyAndDescription(tags);
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), scoreAndDescription.getRight(), scoreAndDescription.getLeft()));
}
/**
* Get the score property for the node.
*
* @param tags the list of tags associated with the file
*
* @return score property and description
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
Score score = Score.NO_SCORE;
String description = Bundle.BlackboardArtifactNode_createSheet_noScore_description();
if (associated instanceof AbstractFile) {
@ -673,34 +729,63 @@ public class BlackboardArtifactNode extends AbstractContentNode<BlackboardArtifa
}
}
}
sheetSet.put(new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_score_name(), Bundle.BlackboardArtifactNode_createSheet_score_displayName(), description, score));
return Pair.of(score, description);
}
/**
* Used by (subclasses of) BlackboardArtifactNode to add the Occurrences
* property to their sheets.
*
* @param sheetSet the modifiable Sheet.Set to add the property to
* @param attribute correlation attribute instance
*
* @deprecated Use the GetSCOTask to get this data on a background
* thread..., and then update the property sheet asynchronously
*/
@NbBundle.Messages({"BlackboardArtifactNode.createSheet.count.name=O",
"BlackboardArtifactNode.createSheet.count.displayName=O",
"BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated",
"BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated",
"# {0} - occuranceCount",
"BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value"})
"BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found",
"BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property",
"# {0} - occurenceCount",
"# {1} - attributeType",
"BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}"})
@Deprecated
protected final void addCountProperty(Sheet.Set sheetSet, CorrelationAttributeInstance attribute) {
Long count = -1L; //The column renderer will not display negative values, negative value used when count unavailble to preserve sorting
String description = Bundle.BlackboardArtifactNode_createSheet_count_noCentralRepo_description();
Pair<Long, String> countAndDescription = getCountPropertyAndDescription(attribute.getCorrelationType(), attribute.getCorrelationValue(), Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationAttributes_description());
sheetSet.put(
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), countAndDescription.getRight(), countAndDescription.getLeft()));
}
/**
* Gets the Occurrences property for the node.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to count
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count and description
*
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(Type attributeType, String attributeValue, String defaultDescription) {
Long count = -1L;
String description = defaultDescription;
try {
//don't perform the query if there is no correlation value
if (attribute != null && StringUtils.isNotBlank(attribute.getCorrelationValue())) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attribute.getCorrelationType(), attribute.getCorrelationValue());
description = Bundle.BlackboardArtifactNode_createSheet_count_description(count);
} else if (attribute != null) {
description = Bundle.BlackboardArtifactNode_createSheet_count_hashLookupNotRun_description();
if (attributeType != null && StringUtils.isNotBlank(attributeValue)) {
count = EamDb.getInstance().getCountUniqueCaseDataSourceTuplesHavingTypeValue(attributeType, attributeValue);
description = Bundle.BlackboardArtifactNode_createSheet_count_description(count, attributeType.getDisplayName());
} else if (attributeType != null) {
description = Bundle.BlackboardArtifactNode_createSheet_count_noCorrelationValues_description();
}
} catch (EamDbException ex) {
logger.log(Level.WARNING, "Error getting count of datasources with correlation attribute", ex);
} catch (CorrelationAttributeNormalizationException ex) {
logger.log(Level.WARNING, "Unable to normalize data to get count of datasources with correlation attribute", ex);
}
sheetSet.put(
new NodeProperty<>(Bundle.BlackboardArtifactNode_createSheet_count_name(), Bundle.BlackboardArtifactNode_createSheet_count_displayName(), description, count));
return Pair.of(count, description);
}
private void updateSheet() {

View File

@ -1,16 +1,14 @@
AbstractAbstractFileNode.accessTimeColLbl=Access Time
AbstractAbstractFileNode.addFileProperty.desc=no description
AbstractAbstractFileNode.attrAddrColLbl=Attr. Addr.
AbstractAbstractFileNode.changeTimeColLbl=Change Time
AbstractAbstractFileNode.createdTimeColLbl=Created Time
AbstractAbstractFileNode.createSheet.comment.displayName=C
AbstractAbstractFileNode.createSheet.comment.name=C
# {0} - occuranceCount
AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value
# {0} - occurenceCount
AbstractAbstractFileNode.createSheet.count.description=There were {0} datasource(s) found with occurences of the MD5 correlation value
AbstractAbstractFileNode.createSheet.count.displayName=O
AbstractAbstractFileNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this file when the column was populated
AbstractAbstractFileNode.createSheet.count.name=O
AbstractAbstractFileNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated
AbstractAbstractFileNode.createSheet.interestingResult.description=File has interesting result associated with it.
AbstractAbstractFileNode.createSheet.noScore.description=No score
AbstractAbstractFileNode.createSheet.notableFile.description=File recognized as notable.
@ -37,6 +35,8 @@ AbstractAbstractFileNode.tagsProperty.displayName=Tags
AbstractAbstractFileNode.typeDirColLbl=Type(Dir)
AbstractAbstractFileNode.typeMetaColLbl=Type(Meta)
AbstractAbstractFileNode.useridColLbl=UserID
AbstractContentNode.nodescription=no description
AbstractContentNode.valueLoading=value loading
AbstractFsContentNode.noDesc.text=no description
ArtifactStringContent.attrsTableHeader.sources=Source(s)
ArtifactStringContent.attrsTableHeader.type=Type
@ -53,12 +53,13 @@ BlackboardArtifactNode.createSheet.artifactType.displayName=Result Type
BlackboardArtifactNode.createSheet.artifactType.name=Result Type
BlackboardArtifactNode.createSheet.comment.displayName=C
BlackboardArtifactNode.createSheet.comment.name=C
# {0} - occuranceCount
BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurances of the correlation value
# {0} - occurenceCount
# {1} - attributeType
BlackboardArtifactNode.createSheet.count.description=There were {0} datasource(s) found with occurrences of the correlation value of type {1}
BlackboardArtifactNode.createSheet.count.displayName=O
BlackboardArtifactNode.createSheet.count.hashLookupNotRun.description=Hash lookup had not been run on this artifact's associated file when the column was populated
BlackboardArtifactNode.createSheet.count.name=O
BlackboardArtifactNode.createSheet.count.noCentralRepo.description=Central repository was not enabled when this column was populated
BlackboardArtifactNode.createSheet.count.noCorrelationAttributes.description=No correlation properties found
BlackboardArtifactNode.createSheet.count.noCorrelationValues.description=Unable to find other occurrences because no value exists for the available correlation property
BlackboardArtifactNode.createSheet.fileSize.displayName=Size
BlackboardArtifactNode.createSheet.fileSize.name=Size
BlackboardArtifactNode.createSheet.interestingResult.description=Result has an interesting result associated with it.
@ -114,6 +115,8 @@ FileTypesByMimeTypeNode.createSheet.mediaSubtype.name=Subtype
FileTypesByMimeTypeNode.createSheet.mediaType.desc=no description
FileTypesByMimeTypeNode.createSheet.mediaType.displayName=Type
FileTypesByMimeTypeNode.createSheet.mediaType.name=Type
GetSCOTask.occurrences.defaultDescription=No correlation properties found
GetSCOTask.occurrences.multipleProperties=Multiple different correlation properties exist for this result
ImageNode.action.runIngestMods.text=Run Ingest Modules
ImageNode.createSheet.deviceId.desc=Device ID of the image
ImageNode.createSheet.deviceId.displayName=Device ID

View File

@ -36,6 +36,7 @@ import org.sleuthkit.autopsy.actions.ReplaceBlackboardArtifactTagAction;
import org.sleuthkit.autopsy.actions.ReplaceContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.datamodel.Reports.ReportNode;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -92,6 +93,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -119,6 +121,7 @@ public class DataModelActionsFactory {
actionsList.add(new NewWindowViewAction(VIEW_IN_NEW_WINDOW, slackFileNode));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -155,6 +158,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());//
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -189,6 +193,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -223,6 +228,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -257,6 +263,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -291,6 +298,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -325,6 +333,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -379,6 +388,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {
@ -415,6 +425,7 @@ public class DataModelActionsFactory {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
if (isArtifactSource) {

View File

@ -57,7 +57,7 @@ public class DataSourcesNode extends DisplayableItemNode {
}
public DataSourcesNode(long dsObjId) {
super(Children.create(new DataSourcesNodeChildren(dsObjId), true), Lookups.singleton(NAME));
super(Children.create(new DataSourcesNodeChildren(dsObjId), false), Lookups.singleton(NAME));
displayName = (dsObjId > 0) ? NbBundle.getMessage(DataSourcesNode.class, "DataSourcesNode.group_by_datasource.name") : NAME;
init();
}

View File

@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -37,7 +36,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -59,7 +57,7 @@ import org.sleuthkit.datamodel.VirtualDirectory;
public class DeletedContent implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
@NbBundle.Messages({"DeletedContent.fsDelFilter.text=File System",
"DeletedContent.allDelFilter.text=All"})
@ -105,11 +103,11 @@ public class DeletedContent implements AutopsyVisitableItem {
public DeletedContent(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
this.datasourceObjId = dsObjId;
this.filteringDSObjId = dsObjId;
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
return this.filteringDSObjId;
}
@Override
@ -439,7 +437,7 @@ public class DeletedContent implements AutopsyVisitableItem {
}
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
if (filteringDSObjId > 0) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}
return query;

View File

@ -28,6 +28,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -89,6 +90,7 @@ public class DirectoryNode extends AbstractFsContentNode<AbstractFile> {
actionsList.add(ViewFileInTimelineAction.createViewFileAction(content));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(new RunIngestModulesAction(content));
actionsList.add(null); // creates a menu separator

View File

@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -40,7 +39,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -88,7 +86,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
}
private SleuthkitCase skCase;
private final EmailResults emailResults;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
@ -110,7 +108,7 @@ public class EmailExtracted implements AutopsyVisitableItem {
*/
public EmailExtracted(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
emailResults = new EmailResults();
}
@ -162,8 +160,8 @@ public class EmailExtracted implements AutopsyVisitableItem {
+ "attribute_type_id=" + pathAttrId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {

View File

@ -26,7 +26,6 @@ import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
import java.util.logging.Level;
import org.openide.nodes.ChildFactory;
import org.openide.nodes.Children;
@ -35,7 +34,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -64,7 +62,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
private SleuthkitCase skCase; // set to null after case has been closed
private Blackboard blackboard;
public static final String NAME = NbBundle.getMessage(RootNode.class, "ExtractedContentNode.name.text");
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Constructs extracted content object
@ -83,7 +81,7 @@ public class ExtractedContent implements AutopsyVisitableItem {
*/
public ExtractedContent(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
this.blackboard = skCase.getBlackboard();
}
@ -307,8 +305,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifact.Type> list) {
if (skCase != null) {
try {
List<BlackboardArtifact.Type> types = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
blackboard.getArtifactTypesInUse(datasourceObjId) :
List<BlackboardArtifact.Type> types = (filteringDSObjId > 0) ?
blackboard.getArtifactTypesInUse(filteringDSObjId) :
skCase.getArtifactTypesInUse() ;
types.removeAll(doNotShow);
@ -372,8 +370,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
// a performance increase might be had by adding a
// "getBlackboardArtifactCount()" method to skCase
try {
this.childCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ?
blackboard.getArtifactsCount(type.getTypeID(), datasourceObjId) :
this.childCount = (filteringDSObjId > 0) ?
blackboard.getArtifactsCount(type.getTypeID(), filteringDSObjId) :
skCase.getBlackboardArtifactsTypeCount(type.getTypeID());
} catch (TskException ex) {
Logger.getLogger(TypeNode.class.getName())
@ -501,8 +499,8 @@ public class ExtractedContent implements AutopsyVisitableItem {
protected List<BlackboardArtifact> makeKeys() {
if (skCase != null) {
try {
return Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? blackboard.getArtifacts(type.getTypeID(), datasourceObjId)
return (filteringDSObjId > 0)
? blackboard.getArtifacts(type.getTypeID(), filteringDSObjId)
: skCase.getBlackboardArtifacts(type.getTypeID());
} catch (TskException ex) {
Logger.getLogger(ArtifactFactory.class.getName()).log(Level.SEVERE, "Couldn't get blackboard artifacts from database", ex); //NON-NLS

View File

@ -32,6 +32,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -173,6 +174,7 @@ public class FileNode extends AbstractFsContentNode<AbstractFile> {
actionsList.add(null); // Creates an item separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // Creates an item separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -24,7 +24,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -37,9 +36,7 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.AbstractFile;
@ -63,7 +60,7 @@ import org.sleuthkit.datamodel.VirtualDirectory;
public class FileSize implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
public enum FileSizeFilter implements AutopsyVisitableItem {
@ -105,7 +102,7 @@ public class FileSize implements AutopsyVisitableItem {
public FileSize(SleuthkitCase skCase, long dsObjId) {
this.skCase = skCase;
this.datasourceObjId = dsObjId;
this.filteringDSObjId = dsObjId;
}
@Override
@ -118,7 +115,7 @@ public class FileSize implements AutopsyVisitableItem {
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
return this.filteringDSObjId;
}
/*
* Root node. Children are nodes for specific sizes.
@ -437,7 +434,7 @@ public class FileSize implements AutopsyVisitableItem {
query = query + " AND (type != " + TskData.TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS.getFileType() + ")"; //NON-NLS
// filter by datasource if indicated in case preferences
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
if (filteringDSObjId > 0) {
query += " AND data_source_obj_id = " + filteringDSObjId;
}

View File

@ -24,7 +24,6 @@ import java.util.Arrays;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -39,7 +38,6 @@ import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -366,7 +364,7 @@ public final class FileTypesByExtension implements AutopsyVisitableItem {
+ (UserPreferences.hideKnownFilesInViewsTree()
? " AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")"
: " ")
+ (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
+ (filteringDataSourceObjId() > 0
? " AND data_source_obj_id = " + filteringDataSourceObjId()
: " ")
+ " AND (extension IN (" + filter.getFilter().stream()

View File

@ -28,7 +28,6 @@ import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import static org.sleuthkit.autopsy.core.UserPreferences.hideKnownFilesInViewsTree;
import static org.sleuthkit.autopsy.core.UserPreferences.hideSlackFilesInViewsTree;
@ -103,7 +101,7 @@ public final class FileTypesByMimeType extends Observable implements AutopsyVisi
+ TskData.TSK_DB_FILES_TYPE_ENUM.LOCAL.ordinal()
+ (hideSlackFilesInViewsTree() ? "" : ("," + TskData.TSK_DB_FILES_TYPE_ENUM.SLACK.ordinal()))
+ "))"
+ ( Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
+ ( (filteringDataSourceObjId() > 0) ? " AND data_source_obj_id = " + this.filteringDataSourceObjId() : " ")
+ (hideKnownFilesInViewsTree() ? (" AND (known IS NULL OR known != " + TskData.FileKnown.KNOWN.getFileKnownValue() + ")") : "");
}

View File

@ -0,0 +1,129 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.logging.Level;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance.Type;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamArtifactUtil;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.events.AutopsyEvent;
import org.sleuthkit.datamodel.AbstractFile;
import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.Tag;
import org.sleuthkit.datamodel.TskCoreException;
/**
* Background task to get Score, Comment and Occurrences values for an Abstract
* content node.
*
*/
class GetSCOTask implements Runnable {
private final WeakReference<AbstractContentNode<?>> weakNodeRef;
private final PropertyChangeListener listener;
private static final Logger logger = Logger.getLogger(GetSCOTask.class.getName());
GetSCOTask(WeakReference<AbstractContentNode<?>> weakContentRef, PropertyChangeListener listener) {
this.weakNodeRef = weakContentRef;
this.listener = listener;
}
@Messages({"GetSCOTask.occurrences.defaultDescription=No correlation properties found",
"GetSCOTask.occurrences.multipleProperties=Multiple different correlation properties exist for this result"})
@Override
public void run() {
AbstractContentNode<?> contentNode = weakNodeRef.get();
//Check for stale reference
if (contentNode == null) {
return;
}
// get the SCO column values
List<Tag> tags = contentNode.getAllTagsFromDatabase();
CorrelationAttributeInstance fileAttribute = contentNode.getCorrelationAttributeInstance();
SCOData scoData = new SCOData();
scoData.setScoreAndDescription(contentNode.getScorePropertyAndDescription(tags));
scoData.setComment(contentNode.getCommentProperty(tags, fileAttribute));
if (!UserPreferences.hideCentralRepoCommentsAndOccurrences()) {
Type type = null;
String value = null;
String description = Bundle.GetSCOTask_occurrences_defaultDescription();
if (contentNode instanceof BlackboardArtifactNode) {
BlackboardArtifact bbArtifact = ((BlackboardArtifactNode) contentNode).getArtifact();
//for specific artifact types we still want to display information for the file instance correlation attribute
if (bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_DETECTED.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_ENCRYPTION_SUSPECTED.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_INTERESTING_FILE_HIT.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_OBJECT_DETECTED.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_EXT_MISMATCH_DETECTED.getTypeID()
|| bbArtifact.getArtifactTypeID() == BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) {
try {
if (bbArtifact.getParent() instanceof AbstractFile) {
type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID);
value = ((AbstractFile) bbArtifact.getParent()).getMd5Hash();
}
} catch (TskCoreException | EamDbException ex) {
logger.log(Level.WARNING, "Unable to get correlation type or value to determine value for O column for artifact", ex);
}
} else {
List<CorrelationAttributeInstance> listOfPossibleAttributes = EamArtifactUtil.makeInstancesFromBlackboardArtifact(bbArtifact, false);
if (listOfPossibleAttributes.size() > 1) {
//Don't display anything if there is more than 1 correlation property for an artifact but let the user know
description = Bundle.GetSCOTask_occurrences_multipleProperties();
} else if (!listOfPossibleAttributes.isEmpty()) {
//there should only be one item in the list
type = listOfPossibleAttributes.get(0).getCorrelationType();
value = listOfPossibleAttributes.get(0).getCorrelationValue();
}
}
} else if (contentNode.getContent() instanceof AbstractFile) {
//use the file instance correlation attribute if the node is not a BlackboardArtifactNode
try {
type = CorrelationAttributeInstance.getDefaultCorrelationTypes().get(CorrelationAttributeInstance.FILES_TYPE_ID);
value = ((AbstractFile) contentNode.getContent()).getMd5Hash();
} catch (EamDbException ex) {
logger.log(Level.WARNING, "Unable to get correlation type to determine value for O column for file", ex);
}
}
scoData.setCountAndDescription(contentNode.getCountPropertyAndDescription(type, value, description));
}
// signal SCO data is available.
if (listener
!= null) {
listener.propertyChange(new PropertyChangeEvent(
AutopsyEvent.SourceType.LOCAL.toString(),
AbstractAbstractFileNode.NodeSpecificEvents.SCO_AVAILABLE.toString(),
null, scoData));
}
}
}

View File

@ -30,7 +30,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -65,7 +63,7 @@ public class HashsetHits implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(HashsetHits.class.getName());
private SleuthkitCase skCase;
private final HashsetResults hashsetResults;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
@ -87,7 +85,7 @@ public class HashsetHits implements AutopsyVisitableItem {
*/
public HashsetHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
hashsetResults = new HashsetResults();
}
@ -142,8 +140,8 @@ public class HashsetHits implements AutopsyVisitableItem {
+ "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {

View File

@ -28,12 +28,15 @@ import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
@ -47,6 +50,7 @@ import org.sleuthkit.datamodel.SleuthkitCase.CaseDbQuery;
import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for the image. The children of
@ -126,7 +130,7 @@ public class ImageNode extends AbstractContentNode<Image> {
"ImageNode.createSheet.type.text=Image",
"ImageNode.createSheet.sectorSize.name=Sector Size (Bytes)",
"ImageNode.createSheet.sectorSize.displayName=Sector Size (Bytes)",
"ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.",
"ImageNode.createSheet.sectorSize.desc=Sector size of the image in bytes.",
"ImageNode.createSheet.timezone.name=Timezone",
"ImageNode.createSheet.timezone.displayName=Timezone",
"ImageNode.createSheet.timezone.desc=Timezone of the image",
@ -250,4 +254,75 @@ public class ImageNode extends AbstractContentNode<Image> {
}
};
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the
* node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the
* node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to coun
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -30,7 +30,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -42,7 +41,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
@ -61,7 +59,7 @@ public class InterestingHits implements AutopsyVisitableItem {
private static final Logger logger = Logger.getLogger(InterestingHits.class.getName());
private SleuthkitCase skCase;
private final InterestingResults interestingResults = new InterestingResults();
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* Constructor
@ -82,7 +80,7 @@ public class InterestingHits implements AutopsyVisitableItem {
*/
public InterestingHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
interestingResults.update();
}
@ -133,8 +131,8 @@ public class InterestingHits implements AutopsyVisitableItem {
+ "attribute_type_id=" + setNameId //NON-NLS
+ " AND blackboard_attributes.artifact_id=blackboard_artifacts.artifact_id" //NON-NLS
+ " AND blackboard_artifacts.artifact_type_id=" + artId; //NON-NLS
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
query += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
if (filteringDSObjId > 0) {
query += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(query)) {

View File

@ -31,7 +31,6 @@ import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -45,7 +44,6 @@ import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.Bundle.*;
@ -76,7 +74,7 @@ public class KeywordHits implements AutopsyVisitableItem {
private SleuthkitCase skCase;
private final KeywordResults keywordResults;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
/**
* String used in the instance MAP so that exact matches and substring can
@ -123,7 +121,7 @@ public class KeywordHits implements AutopsyVisitableItem {
*/
public KeywordHits(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
keywordResults = new KeywordResults();
}
@ -344,8 +342,8 @@ public class KeywordHits implements AutopsyVisitableItem {
}
String queryStr = KEYWORD_HIT_ATTRIBUTES_QUERY;
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
queryStr += " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId;
if (filteringDSObjId > 0) {
queryStr += " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId;
}
try (CaseDbQuery dbQuery = skCase.executeQuery(queryStr)) {

View File

@ -29,6 +29,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -105,6 +106,7 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -31,6 +31,7 @@ import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
import org.sleuthkit.autopsy.directorytree.ExternalViewerShortcutAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
@ -82,6 +83,7 @@ public class LocalFileNode extends AbstractAbstractFileNode<AbstractFile> {
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -25,6 +25,7 @@ import org.openide.nodes.Children;
import org.openide.nodes.Node;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.datamodel.accounts.Accounts;
import org.sleuthkit.datamodel.SleuthkitVisitableItem;
/**
* Children implementation for the root node of a ContentNode tree. Accepts a
@ -34,6 +35,7 @@ public class RootContentChildren extends Children.Keys<Object> {
private final Collection<? extends Object> contentKeys;
private final CreateAutopsyNodeVisitor createAutopsyNodeVisitor = new CreateAutopsyNodeVisitor();
private final CreateSleuthkitNodeVisitor createSleuthkitNodeVisitor = new CreateSleuthkitNodeVisitor();
/**
* @param contentKeys root Content objects for the Node tree
@ -68,7 +70,7 @@ public class RootContentChildren extends Children.Keys<Object> {
if (key instanceof AutopsyVisitableItem) {
return new Node[] {((AutopsyVisitableItem)key).accept(createAutopsyNodeVisitor)};
} else {
return null;
return new Node[] {((SleuthkitVisitableItem)key).accept(createSleuthkitNodeVisitor)};
}
}

View File

@ -0,0 +1,56 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.datamodel;
import org.apache.commons.lang3.tuple.Pair;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
/**
* Container to bag the S C & O data for an abstract file node.
*
*/
class SCOData {
private Pair<DataResultViewerTable.Score, String> scoreAndDescription = null;
private DataResultViewerTable.HasCommentStatus comment = null;
private Pair<Long, String> countAndDescription = null;
Pair<DataResultViewerTable.Score, String> getScoreAndDescription() {
return scoreAndDescription;
}
DataResultViewerTable.HasCommentStatus getComment() {
return comment;
}
Pair<Long, String> getCountAndDescription() {
return countAndDescription;
}
void setScoreAndDescription(Pair<DataResultViewerTable.Score, String> scoreAndDescription) {
this.scoreAndDescription = scoreAndDescription;
}
void setComment(DataResultViewerTable.HasCommentStatus comment) {
this.comment = comment;
}
void setCountAndDescription(Pair<Long, String> countAndDescription) {
this.countAndDescription = countAndDescription;
}
}

View File

@ -28,6 +28,7 @@ import org.openide.util.Utilities;
import org.sleuthkit.autopsy.actions.AddContentTagAction;
import org.sleuthkit.autopsy.actions.DeleteFileContentTagAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
import org.sleuthkit.autopsy.directorytree.ViewContextAction;
@ -85,6 +86,7 @@ public class SlackFileNode extends AbstractFsContentNode<AbstractFile> {
NbBundle.getMessage(this.getClass(), "SlackFileNode.getActions.viewInNewWin.text"), this));
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());

View File

@ -25,6 +25,7 @@ import javax.swing.Action;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.datasourcesummary.ViewSummaryInformationAction;
import org.sleuthkit.autopsy.coreutils.ContextMenuExtensionPoint;
import org.sleuthkit.autopsy.directorytree.ExportCSVAction;
import org.sleuthkit.autopsy.directorytree.ExtractAction;
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
@ -61,6 +62,7 @@ public abstract class SpecialDirectoryNode extends AbstractAbstractFileNode<Spec
Bundle.SpecialDirectoryNode_getActions_viewInNewWin_text(), this));
actions.add(null); // creates a menu separator
actions.add(ExtractAction.getInstance());
actions.add(ExportCSVAction.getInstance());
actions.add(null); // creates a menu separator
actions.add(new FileSearchAction(Bundle.ImageNode_getActions_openFileSearchByAttr_text()));
if (content.isDataSource()) {

View File

@ -23,7 +23,6 @@ import java.beans.PropertyChangeListener;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
import java.util.Observable;
import java.util.Observer;
import java.util.Set;
@ -35,7 +34,6 @@ import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.core.UserPreferences;
@ -61,14 +59,14 @@ public class Tags implements AutopsyVisitableItem {
private static final String USER_NAME_PROPERTY = "user.name"; //NON-NLS
private final String ICON_PATH = "org/sleuthkit/autopsy/images/tag-folder-blue-icon-16.png"; //NON-NLS
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
Tags() {
this(0);
}
Tags(long dsObjId) {
this.datasourceObjId = dsObjId;
this.filteringDSObjId = dsObjId;
}
/**
@ -81,7 +79,7 @@ public class Tags implements AutopsyVisitableItem {
}
long filteringDataSourceObjId() {
return this.datasourceObjId;
return this.filteringDSObjId;
}
@Override
@ -156,7 +154,7 @@ public class Tags implements AutopsyVisitableItem {
private class TagNameNodeFactory extends ChildFactory.Detachable<TagName> implements Observer {
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private final Set<Case.Events> CASE_EVENTS_OF_INTEREST = EnumSet.of(Case.Events.BLACKBOARD_ARTIFACT_TAG_ADDED,
Case.Events.BLACKBOARD_ARTIFACT_TAG_DELETED,
@ -219,7 +217,7 @@ public class Tags implements AutopsyVisitableItem {
* @param objId data source object id
*/
TagNameNodeFactory(long objId) {
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
}
@ -246,12 +244,12 @@ public class Tags implements AutopsyVisitableItem {
List<TagName> tagNamesInUse;
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(datasourceObjId, userName)
tagNamesInUse = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUseForUser(userName);
} else {
tagNamesInUse = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(datasourceObjId)
tagNamesInUse = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse(filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getTagNamesInUse();
}
Collections.sort(tagNamesInUse);
@ -303,17 +301,17 @@ public class Tags implements AutopsyVisitableItem {
TagsManager tm = Case.getCurrentCaseThrows().getServices().getTagsManager();
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName);
if (filteringDSObjId > 0) {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName);
} else {
tagsCount = tm.getContentTagsCountByTagNameForUser(tagName, userName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
}
} else {
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
tagsCount = tm.getContentTagsCountByTagName(tagName, datasourceObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId);
if (filteringDSObjId > 0) {
tagsCount = tm.getContentTagsCountByTagName(tagName, filteringDSObjId);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId);
} else {
tagsCount = tm.getContentTagsCountByTagName(tagName);
tagsCount += tm.getBlackboardArtifactTagsCountByTagName(tagName);
@ -424,12 +422,12 @@ public class Tags implements AutopsyVisitableItem {
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
tagsCount = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, datasourceObjId)
tagsCount = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
@ -486,8 +484,8 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<ContentTag> keys) {
// Use the content tags bearing the specified tag name as the keys.
try {
List<ContentTag> contentTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, datasourceObjId)
List<ContentTag> contentTags = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getContentTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
@ -544,12 +542,12 @@ public class Tags implements AutopsyVisitableItem {
try {
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);
tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, datasourceObjId, userName)
tagsCount = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, filteringDSObjId, userName)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagNameForUser(tagName, userName);
} else {
tagsCount = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, datasourceObjId)
tagsCount = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsCountByTagName(tagName);
}
} catch (TskCoreException | NoCurrentCaseException ex) {
@ -606,8 +604,8 @@ public class Tags implements AutopsyVisitableItem {
protected boolean createKeys(List<BlackboardArtifactTag> keys) {
try {
// Use the blackboard artifact tags bearing the specified tag name as the keys.
List<BlackboardArtifactTag> artifactTags = Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, datasourceObjId)
List<BlackboardArtifactTag> artifactTags = (filteringDSObjId > 0)
? Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName, filteringDSObjId)
: Case.getCurrentCaseThrows().getServices().getTagsManager().getBlackboardArtifactTagsByTagName(tagName);
if (UserPreferences.showOnlyCurrentUserTags()) {
String userName = System.getProperty(USER_NAME_PROPERTY);

View File

@ -25,10 +25,14 @@ import java.util.EnumSet;
import java.util.List;
import java.util.logging.Level;
import javax.swing.Action;
import org.apache.commons.lang3.tuple.Pair;
import org.openide.nodes.Sheet;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationAttributeInstance;
import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable;
import org.sleuthkit.autopsy.coreutils.Logger;
import static org.sleuthkit.autopsy.datamodel.AbstractContentNode.NO_DESCR;
import org.sleuthkit.autopsy.datamodel.BaseChildFactory.NoSuchEventBusException;
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
@ -39,12 +43,14 @@ import org.sleuthkit.datamodel.TskCoreException;
import org.sleuthkit.datamodel.VirtualDirectory;
import org.sleuthkit.datamodel.Volume;
import org.sleuthkit.autopsy.directorytree.FileSystemDetailsAction;
import org.sleuthkit.datamodel.Tag;
/**
* This class is used to represent the "Node" for the volume. Its child is the
* root directory of a file system
*/
public class VolumeNode extends AbstractContentNode<Volume> {
private static final Logger logger = Logger.getLogger(VolumeNode.class.getName());
/**
@ -212,4 +218,76 @@ public class VolumeNode extends AbstractContentNode<Volume> {
public String getItemType() {
return DisplayableItemNode.FILE_PARENT_NODE_KEY;
}
/**
* Reads and returns a list of all tags associated with this content node.
*
* Null implementation of an abstract method.
*
* @return list of tags associated with the node.
*/
@Override
protected List<Tag> getAllTagsFromDatabase() {
return new ArrayList<>();
}
/**
* Returns correlation attribute instance for the underlying content of the
* node.
*
* Null implementation of an abstract method.
*
* @return correlation attribute instance for the underlying content of the
* node.
*/
@Override
protected CorrelationAttributeInstance getCorrelationAttributeInstance() {
return null;
}
/**
* Returns Score property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags.
*
* @return Score property for the underlying content of the node.
*/
@Override
protected Pair<DataResultViewerTable.Score, String> getScorePropertyAndDescription(List<Tag> tags) {
return Pair.of(DataResultViewerTable.Score.NO_SCORE, NO_DESCR);
}
/**
* Returns comment property for the node.
*
* Null implementation of an abstract method.
*
* @param tags list of tags
* @param attribute correlation attribute instance
*
* @return Comment property for the underlying content of the node.
*/
@Override
protected DataResultViewerTable.HasCommentStatus getCommentProperty(List<Tag> tags, CorrelationAttributeInstance attribute) {
return DataResultViewerTable.HasCommentStatus.NO_COMMENT;
}
/**
* Returns occurrences/count property for the node.
*
* Null implementation of an abstract method.
*
* @param attributeType the type of the attribute to count
* @param attributeValue the value of the attribute to coun
* @param defaultDescription a description to use when none is determined by
* the getCountPropertyAndDescription method
*
* @return count property for the underlying content of the node.
*/
@Override
protected Pair<Long, String> getCountPropertyAndDescription(CorrelationAttributeInstance.Type attributeType, String attributeValue, String defaultDescription) {
return Pair.of(-1L, NO_DESCR);
}
}

View File

@ -57,9 +57,7 @@ import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.CasePreferences;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.core.UserPreferences;
import org.sleuthkit.autopsy.corecomponents.DataResultTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.datamodel.AutopsyItemVisitor;
@ -96,7 +94,7 @@ final public class Accounts implements AutopsyVisitableItem {
final public static String NAME = Bundle.AccountsRootNode_name();
private SleuthkitCase skCase;
private final long datasourceObjId;
private final long filteringDSObjId; // 0 if not filtering/grouping by data source
private final EventBus reviewStatusBus = new EventBus("ReviewStatusBus");
@ -123,7 +121,7 @@ final public class Accounts implements AutopsyVisitableItem {
*/
public Accounts(SleuthkitCase skCase, long objId) {
this.skCase = skCase;
this.datasourceObjId = objId;
this.filteringDSObjId = objId;
this.rejectActionInstance = new RejectAccounts();
this.approveActionInstance = new ApproveAccounts();
@ -153,8 +151,8 @@ final public class Accounts implements AutopsyVisitableItem {
* based on the CasePreferences groupItemsInTreeByDataSource setting
*/
private String getFilterByDataSourceClause() {
if (Objects.equals(CasePreferences.getGroupItemsInTreeByDataSource(), true)) {
return " AND blackboard_artifacts.data_source_obj_id = " + datasourceObjId + " ";
if (filteringDSObjId > 0) {
return " AND blackboard_artifacts.data_source_obj_id = " + filteringDSObjId + " ";
}
return " ";

View File

@ -1,3 +1,8 @@
CSVWriter.done.notifyMsg.error=Error exporting to CSV file
# {0} - Output file
CSVWriter.done.notifyMsg.success=Wrote to {0}
CSVWriter.progress.cancelling=Cancelling
CSVWriter.progress.extracting=Exporting to CSV file
CTL_DirectoryTreeTopComponent=Directory Tree
DataResultFilterNode.viewSourceArtifact.text=View Source Result
# {0} - dataSourceCount
@ -5,6 +10,11 @@ DirectoryTreeTopComponent.componentOpened.groupDataSources.text=This case contai
DirectoryTreeTopComponent.componentOpened.groupDataSources.title=Group by data source?
DirectoryTreeTopComponent.emptyMimeNode.text=Data not available. Run file type identification module.
DirectoryTreeTopComponent.resultsView.title=Listing
ExportCSV.saveNodesToCSV.empty=No data to export
# {0} - Output file
ExportCSV.saveNodesToCSV.fileExists=File {0} already exists
ExportCSV.saveNodesToCSV.noCurrentCase=No open case available
ExportCSV.title.text=Export selected rows to CSV
ExternalViewerAction.actionPerformed.failure.exe.message=The file is an executable and will not be opened.
ExternalViewerAction.actionPerformed.failure.IO.message=There is no associated editor for files of this type or the associated application failed to launch.
ExternalViewerAction.actionPerformed.failure.missingFile.message=The file no longer exists.

View File

@ -372,6 +372,7 @@ public class DataResultFilterNode extends FilterNode {
}
actionsList.add(null); // creates a menu separator
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(null); // creates a menu separator
actionsList.add(AddContentTagAction.getInstance());
actionsList.add(AddBlackboardArtifactTagAction.getInstance());

View File

@ -125,6 +125,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
}
}
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList;
}
@ -142,6 +143,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
}
}
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.addAll(ContextMenuExtensionPoint.getActions());
return actionsList;
}
@ -150,6 +152,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final DerivedFile d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =
@ -166,6 +169,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final LocalFile d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =
@ -182,6 +186,7 @@ public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? ext
public List<? extends Action> visit(final org.sleuthkit.datamodel.File d) {
List<Action> actionsList = new ArrayList<>();
actionsList.add(ExtractAction.getInstance());
actionsList.add(ExportCSVAction.getInstance());
actionsList.add(AddContentTagAction.getInstance());
final Collection<AbstractFile> selectedFilesList =

View File

@ -0,0 +1,360 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this content except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.directorytree;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.io.File;
import java.io.BufferedWriter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.InvocationTargetException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.logging.Level;
import javax.swing.AbstractAction;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import javax.swing.filechooser.FileNameExtensionFilter;
import org.netbeans.api.progress.ProgressHandle;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.datamodel.AbstractAbstractFileNode.AbstractFilePropertyType;
import org.openide.nodes.Node;
import org.openide.nodes.Node.PropertySet;
import org.openide.nodes.Node.Property;
/**
* Exports CSV version of result nodes to a location selected by the user.
*/
public final class ExportCSVAction extends AbstractAction {
private static final Logger logger = Logger.getLogger(ExportCSVAction.class.getName());
private final static String DEFAULT_FILENAME = "Results";
private final static List<String> columnsToSkip = Arrays.asList(AbstractFilePropertyType.SCORE.toString(),
AbstractFilePropertyType.COMMENT.toString(), AbstractFilePropertyType.OCCURRENCES.toString());
private static String userDefinedExportPath;
// This class is a singleton to support multi-selection of nodes, since
// org.openide.nodes.NodeOp.findActions(Node[] nodes) will only pick up an Action if every
// node in the array returns a reference to the same action object from Node.getActions(boolean).
private static ExportCSVAction instance;
/**
* Get an instance of the Action. See above for why
* the class is a singleton.
*
* @return the instance
*/
public static synchronized ExportCSVAction getInstance() {
if (null == instance) {
instance = new ExportCSVAction();
}
return instance;
}
/**
* Private constructor for the action.
*/
@NbBundle.Messages({"ExportCSV.title.text=Export selected rows to CSV"})
private ExportCSVAction() {
super(Bundle.ExportCSV_title_text());
}
/**
* Asks user to choose destination, then extracts content to destination
* (recursing on directories).
*
* @param e The action event.
*/
@Override
public void actionPerformed(ActionEvent e) {
Collection<? extends Node> selectedNodes = Utilities.actionsGlobalContext().lookupAll(Node.class);
saveNodesToCSV(selectedNodes, (Component)e.getSource());
}
/**
* Save the selected nodes to a CSV file
*
* @param nodesToExport the nodes to save
* @param component
*/
@NbBundle.Messages({
"# {0} - Output file",
"ExportCSV.saveNodesToCSV.fileExists=File {0} already exists",
"ExportCSV.saveNodesToCSV.noCurrentCase=No open case available",
"ExportCSV.saveNodesToCSV.empty=No data to export"})
public static void saveNodesToCSV(Collection<? extends Node> nodesToExport, Component component) {
if (nodesToExport.isEmpty()) {
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_empty());
return;
}
try {
// Set up the file chooser with a default name and either the Export
// folder or the last used folder.
String fileName = getDefaultOutputFileName(nodesToExport.iterator().next().getParentNode());
JFileChooser fileChooser = new JFileChooser();
fileChooser.setCurrentDirectory(new File(getExportDirectory(Case.getCurrentCaseThrows())));
fileChooser.setSelectedFile(new File(fileName));
fileChooser.setFileFilter(new FileNameExtensionFilter("csv file", "csv"));
int returnVal = fileChooser.showSaveDialog(component);
if (returnVal == JFileChooser.APPROVE_OPTION) {
// Get the file name, appending .csv if necessary
File selectedFile = fileChooser.getSelectedFile();
if (!selectedFile.getName().endsWith(".csv")) { // NON-NLS
selectedFile = new File(selectedFile.toString() + ".csv"); // NON-NLS
}
// Save the directory used for next time
updateExportDirectory(selectedFile.getParent(), Case.getCurrentCaseThrows());
if (selectedFile.exists()) {
logger.log(Level.SEVERE, "File {0} already exists", selectedFile.getAbsolutePath()); //NON-NLS
MessageNotifyUtil.Message.info(Bundle.ExportCSV_saveNodesToCSV_fileExists(selectedFile));
return;
}
CSVWriter writer = new CSVWriter(nodesToExport, selectedFile);
writer.execute();
}
} catch (NoCurrentCaseException ex) {
JOptionPane.showMessageDialog(component, Bundle.ExportCSV_saveNodesToCSV_noCurrentCase());
logger.log(Level.INFO, "Exception while getting open case.", ex); //NON-NLS
}
}
/**
* Create a default name for the CSV output.
*
* @param parent The parent node for the selected nodes
*
* @return the default name
*/
private static String getDefaultOutputFileName(Node parent) {
String dateStr = String.format("%1$tY%1$tm%1$te%1$tI%1$tM%1$tS", Calendar.getInstance());
if (parent != null) {
// The first value in the property set is generally a reasonable name
for (PropertySet set : parent.getPropertySets()) {
for (Property<?> prop : set.getProperties()) {
try {
String parentName = prop.getValue().toString();
// Strip off the count (if present)
parentName = parentName.replaceAll("\\([0-9]+\\)$", "");
// Strip out any invalid characters
parentName = parentName.replaceAll("[\\\\/:*?\"<>|]", "_");
return parentName + " " + dateStr;
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.log(Level.WARNING, "Failed to get property set value as string", ex);
}
}
}
}
return DEFAULT_FILENAME + " " + dateStr;
}
/**
* Get the export directory path.
*
* @param openCase The current case.
*
* @return The export directory path.
*/
private static String getExportDirectory(Case openCase) {
String caseExportPath = openCase.getExportDirectory();
if (userDefinedExportPath == null) {
return caseExportPath;
}
File file = new File(userDefinedExportPath);
if (file.exists() == false || file.isDirectory() == false) {
return caseExportPath;
}
return userDefinedExportPath;
}
/**
* Update the default export directory. If the directory path matches the
* case export directory, then the directory used will always match the
* export directory of any given case. Otherwise, the path last used will be
* saved.
*
* @param exportPath The export path.
* @param openCase The current case.
*/
private static void updateExportDirectory(String exportPath, Case openCase) {
if (exportPath.equalsIgnoreCase(openCase.getExportDirectory())) {
userDefinedExportPath = null;
} else {
userDefinedExportPath = exportPath;
}
}
/**
* Thread that does the actual extraction work
*/
private static class CSVWriter extends SwingWorker<Object, Void> {
private static final Logger logger = Logger.getLogger(CSVWriter.class.getName());
private ProgressHandle progress;
private final Collection<? extends Node> nodesToExport;
private final File outputFile;
/**
* Create an instance of the CSVWriter.
*
* @param extractionTasks List of file extraction tasks.
*/
CSVWriter(Collection<? extends Node> nodesToExport, File outputFile) {
this.nodesToExport = nodesToExport;
this.outputFile = outputFile;
}
@NbBundle.Messages({"CSVWriter.progress.extracting=Exporting to CSV file",
"CSVWriter.progress.cancelling=Cancelling"})
@Override
protected Object doInBackground() throws Exception {
if (nodesToExport.isEmpty()) {
return null;
}
// Set up progress bar.
final String displayName = Bundle.CSVWriter_progress_extracting();
progress = ProgressHandle.createHandle(displayName, new Cancellable() {
@Override
public boolean cancel() {
if (progress != null) {
progress.setDisplayName(Bundle.CSVWriter_progress_cancelling());
}
return ExportCSVAction.CSVWriter.this.cancel(true);
}
});
progress.start();
progress.switchToIndeterminate();
try (BufferedWriter br = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile), StandardCharsets.UTF_8))) {
// Write BOM
br.write('\ufeff');
// Write the header
List<String> headers = new ArrayList<>();
PropertySet[] sets = nodesToExport.iterator().next().getPropertySets();
for(PropertySet set : sets) {
for (Property<?> prop : set.getProperties()) {
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
headers.add(prop.getDisplayName());
}
}
}
br.write(listToCSV(headers));
// Write each line
Iterator<?> nodeIterator = nodesToExport.iterator();
while (nodeIterator.hasNext()) {
if (this.isCancelled()) {
break;
}
Node node = (Node)nodeIterator.next();
List<String> values = new ArrayList<>();
sets = node.getPropertySets();
for(PropertySet set : sets) {
for (Property<?> prop : set.getProperties()) {
if ( ! columnsToSkip.contains(prop.getDisplayName())) {
values.add(escapeQuotes(prop.getValue().toString()));
}
}
}
br.write(listToCSV(values));
}
}
return null;
}
/**
* Escape any quotes in the string
*
* @param original
*
* @return the string with quotes escaped
*/
private String escapeQuotes(String original) {
return original.replaceAll("\"", "\\\\\"");
}
/**
* Convert list of values to a comma separated string.
*
* @param values Values to convert
*
* @return values as CSV
*/
private String listToCSV(List<String> values) {
return "\"" + String.join("\",\"", values) + "\"\n";
}
@NbBundle.Messages({"CSVWriter.done.notifyMsg.error=Error exporting to CSV file",
"# {0} - Output file",
"CSVWriter.done.notifyMsg.success=Wrote to {0}"})
@Override
protected void done() {
boolean msgDisplayed = false;
try {
super.get();
} catch (InterruptedException | ExecutionException ex) {
logger.log(Level.SEVERE, "Fatal error during file extraction", ex); //NON-NLS
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_error());
msgDisplayed = true;
} catch (java.util.concurrent.CancellationException ex) {
// catch and ignore if we were cancelled
} finally {
progress.finish();
if (!this.isCancelled() && !msgDisplayed) {
MessageNotifyUtil.Message.info(Bundle.CSVWriter_done_notifyMsg_success(outputFile));
}
}
}
}
}

View File

@ -6,7 +6,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files
OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
FilesSetPanel.interesting.title=Interesting Files Set
FilesSetPanel.interesting.title=Interesting Files Set Rule
FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named.
FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name.
FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
@ -31,13 +31,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *
FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0}
FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists.
FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator
FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names
FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest:
FilesSetRulePanel.jLabel1.text=Type:
FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find.
FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on.
FilesSetRulePanel.nameCheck.text=Name:
FilesSetRulePanel.pathCheck.text=Path Substring:
FilesSetRulePanel.pathCheck.text=Folder Name:
FilesSetRulePanel.filesRadioButton.text=Files
FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets:

View File

@ -37,6 +37,8 @@ FilesSetPanel.ingest.createNewFilter=Create/edit file ingest filters...
FilesSetPanel.ingest.messages.filtersMustBeNamed=File ingest filters must be named.
FilesSetPanel.rule.title=File Filter Rule
FilesSetRulePanel.bytes=Bytes
#{0} - regex
FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: "{0}")
FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty.
FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer.
FilesSetRulePanel.gigaBytes=Gigabytes
@ -60,7 +62,7 @@ OptionsCategory_Name_InterestingItemDefinitions=Interesting Files
OptionsCategory_Keywords_InterestingItemDefinitions=InterestingItemDefinitions
InterestingItemsIdentifierIngestModule.moduleName=Interesting Files Identifier
InterestingItemsIdentifierIngestModule.moduleDescription=Identifies interesting items as defined by interesting item rule sets.
FilesSetPanel.interesting.title=Interesting Files Set
FilesSetPanel.interesting.title=Interesting Files Set Rule
FilesSetPanel.interesting.messages.filesSetsMustBeNamed=Interesting files sets must be named.
FilesSetPanel.messages.filesSetsReservedName=You have chosen a name reserved by the software, please choose a different name.
FilesSetPanel.ignoreKnownFilesCheckbox.text=Ignore Known Files
@ -85,13 +87,13 @@ FilesSetRulePanel.messages.invalidCharInName=The name cannot contain \\, /, :, *
FilesSetRulePanel.messages.invalidCharInPath=The path cannot contain \\, :, *, ?, \", <, or > unless it is a regular expression.
FilesSetRulePanel.messages.invalidPathRegex=The path regular expression is not valid:\n\n{0}
FilesSetDefsPanel.doFileSetsDialog.duplicateRuleSet.text=Rule set with name {0} already exists.
FilesSetRulePanel.pathSeparatorInfoLabel.text=Use / as path separator
FilesSetRulePanel.pathSeparatorInfoLabel.text=Folder must be in parent path. Use '/' to give consecutive names
FilesIdentifierIngestJobSettingsPanel.border.title=Select interesting files sets to enable during ingest:
FilesSetRulePanel.jLabel1.text=Type:
FilesSetRulePanel.interesting.jLabel5.text=Enter information about files that you want to find.
FilesSetRulePanel.ingest.jLabel5.text=Enter information about files that you want to run ingest on.
FilesSetRulePanel.nameCheck.text=Name:
FilesSetRulePanel.pathCheck.text=Path Substring:
FilesSetRulePanel.pathCheck.text=Folder Name:
FilesSetRulePanel.filesRadioButton.text=Files
FilesSetRulePanel.dirsRadioButton.text=Directories
FilesSetDefsPanel.interesting.setsListLabel.text=Rule Sets:

View File

@ -832,6 +832,7 @@
<Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
<Color blue="f0" green="f0" red="f0" type="rgb"/>
</Property>
<Property name="editable" type="boolean" value="true"/>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.DefaultComboBoxModel&lt;String&gt;(new String[] {&quot;&quot;})" type="code"/>
</Property>

View File

@ -22,6 +22,8 @@ import java.awt.EventQueue;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -278,6 +280,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
//enable the new button
FilesSetDefsPanel.this.newSetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.importSetButton.setEnabled(canBeEnabled);
// Get the selected interesting files set and populate the set
// components.
FilesSet selectedSet = FilesSetDefsPanel.this.setsList.getSelectedValue();
@ -294,14 +297,26 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
FilesSetDefsPanel.this.copySetButton.setEnabled(canBeEnabled);
FilesSetDefsPanel.this.exportSetButton.setEnabled(true);
// Populate the rule definitions list, sorted by name.
TreeMap<String, FilesSet.Rule> rules = new TreeMap<>(selectedSet.getRules());
rules.values().forEach((rule) -> {
List<FilesSet.Rule> rules = new ArrayList<>(selectedSet.getRules().values());
Collections.sort(rules, new Comparator<FilesSet.Rule>() {
@Override
public int compare(FilesSet.Rule rule1, FilesSet.Rule rule2) {
return rule1.toString().compareTo(rule2.toString());
}
});
rules.forEach((rule) -> {
FilesSetDefsPanel.this.rulesListModel.addElement(rule);
});
// Select the first rule by default.
if (!FilesSetDefsPanel.this.rulesListModel.isEmpty()) {
FilesSetDefsPanel.this.rulesList.setSelectedIndex(0);
}
} else {
// Disable the edit, delete, copy, and export buttons
FilesSetDefsPanel.this.editSetButton.setEnabled(false);
FilesSetDefsPanel.this.deleteSetButton.setEnabled(false);
FilesSetDefsPanel.this.copySetButton.setEnabled(false);
FilesSetDefsPanel.this.exportSetButton.setEnabled(false);
}
}
@ -796,6 +811,7 @@ public final class FilesSetDefsPanel extends IngestModuleGlobalSettingsPanel imp
org.openide.awt.Mnemonics.setLocalizedText(jLabel7, org.openide.util.NbBundle.getMessage(FilesSetDefsPanel.class, "FilesSetDefsPanel.jLabel7.text")); // NOI18N
mimeTypeComboBox.setBackground(new java.awt.Color(240, 240, 240));
mimeTypeComboBox.setEditable(true);
mimeTypeComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] {""}));
mimeTypeComboBox.setEnabled(false);
mimeTypeComboBox.setMinimumSize(new java.awt.Dimension(0, 20));

View File

@ -57,7 +57,9 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
"FilesSetRulePanel.NoPathError=Path cannot be empty",
"FilesSetRulePanel.DaysIncludedEmptyError=Number of days included cannot be empty.",
"FilesSetRulePanel.DaysIncludedInvalidError=Number of days included must be a positive integer.",
"FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected)."
"FilesSetRulePanel.ZeroFileSizeError=File size condition value must not be 0 (Unless = is selected).",
"#{0} - regex",
"FilesSetRulePanel.CommaInRegexWarning=Warning: Comma(s) in the file extension field will be interpreted as part of a regex and will not split the entry into multiple extensions (Entered: \"{0}\")",
})
private static final long serialVersionUID = 1L;
@ -130,6 +132,7 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
this.setButtons(okButton, cancelButton);
updateNameTextFieldPrompt();
setComponentsForSearchType();
}
/**
@ -358,6 +361,16 @@ final class FilesSetRulePanel extends javax.swing.JPanel {
return false;
}
if (this.nameRegexCheckbox.isSelected()) {
// If extension is also selected and the regex contains a comma, display a warning
// since it is unclear whether the comma is part of a regex or is separating extensions.
if (this.extensionRadioButton.isSelected() && this.nameTextField.getText().contains(",")) {
NotifyDescriptor notifyDesc = new NotifyDescriptor.Message(
Bundle.FilesSetRulePanel_CommaInRegexWarning(this.nameTextField.getText()),
NotifyDescriptor.WARNING_MESSAGE);
DialogDisplayer.getDefault().notify(notifyDesc);
}
try {
Pattern.compile(this.nameTextField.getText());
} catch (PatternSyntaxException ex) {

View File

@ -48,18 +48,18 @@ import java.util.TreeMap;
import java.util.logging.Level;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringEscapeUtils;
import org.openide.filesystems.FileUtil;
import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.casemodule.NoCurrentCaseException;
import org.sleuthkit.autopsy.casemodule.services.Services;
import org.sleuthkit.autopsy.casemodule.services.TagsManager;
import org.sleuthkit.autopsy.centralrepository.datamodel.CorrelationCase;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDb;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamDbException;
import org.sleuthkit.autopsy.centralrepository.datamodel.EamOrganization;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager;
import org.sleuthkit.autopsy.casemodule.services.contentviewertags.ContentViewerTagManager.ContentViewerTag;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagRegion;
import org.sleuthkit.autopsy.contentviewers.imagetagging.ImageTagsUtility;
import org.sleuthkit.autopsy.coreutils.EscapeUtil;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
import org.sleuthkit.autopsy.coreutils.Logger;
@ -731,6 +731,29 @@ class ReportHTML implements TableReportModule {
logger.log(Level.SEVERE, "Output writer is null. Page was not initialized before writing.", ex); //NON-NLS
}
}
/**
* Finds all associated image tags.
*
* @param contentTags
* @return
*/
private List<ImageTagRegion> getTaggedRegions(List<ContentTag> contentTags) {
ArrayList<ImageTagRegion> tagRegions = new ArrayList<>();
contentTags.forEach((contentTag) -> {
try {
ContentViewerTag<ImageTagRegion> contentViewerTag = ContentViewerTagManager
.getTag(contentTag, ImageTagRegion.class);
if (contentViewerTag != null) {
tagRegions.add(contentViewerTag.getDetails());
}
} catch (TskCoreException | NoCurrentCaseException ex) {
logger.log(Level.WARNING, "Could not get content viewer tag "
+ "from case db for content_tag with id %d", contentTag.getId());
}
});
return tagRegions;
}
/**
* Add the body of the thumbnails table.
@ -770,13 +793,54 @@ class ReportHTML implements TableReportModule {
}
AbstractFile file = (AbstractFile) content;
List<ContentTag> contentTags = new ArrayList<>();
String thumbnailPath = null;
String imageWithTagsFullPath = null;
try {
//Get content tags and all image tags
contentTags = Case.getCurrentCase().getServices()
.getTagsManager().getContentTagsByContent(file);
List<ImageTagRegion> imageTags = getTaggedRegions(contentTags);
if(!imageTags.isEmpty()) {
//Write the tags to the fullsize and thumbnail images
BufferedImage fullImageWithTags = ImageTagsUtility.writeTags(file, imageTags, "png");
BufferedImage thumbnailImageWithTags = ImageTagsUtility.makeThumbnail(file,
imageTags, ImageTagsUtility.IconSize.MEDIUM, "png");
String fileName = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(file.getName());
//Create paths in report to write tagged images
File thumbnailImageWithTagsFile = Paths.get(thumbsPath, FilenameUtils.removeExtension(fileName) + ".png").toFile();
String fullImageWithTagsPath = makeCustomUniqueFilePath(file, "thumbs_fullsize");
fullImageWithTagsPath = FilenameUtils.removeExtension(fullImageWithTagsPath) + ".png";
File fullImageWithTagsFile = Paths.get(fullImageWithTagsPath).toFile();
//Save images
ImageIO.write(thumbnailImageWithTags, "png", thumbnailImageWithTagsFile);
ImageIO.write(fullImageWithTags, "png", fullImageWithTagsFile);
thumbnailPath = THUMBS_REL_PATH + thumbnailImageWithTagsFile.getName();
//Relative path
imageWithTagsFullPath = fullImageWithTagsPath.substring(subPath.length());
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Could not get tags for file.", ex); //NON-NLS
} catch (IOException ex) {
logger.log(Level.WARNING, "Could make marked up thumbnail.", ex); //NON-NLS
}
// save copies of the orginal image and thumbnail image
String thumbnailPath = prepareThumbnail(file);
if(thumbnailPath == null) {
thumbnailPath = prepareThumbnail(file);
}
if (thumbnailPath == null) {
continue;
}
String contentPath = saveContent(file, "thumbs_fullsize"); //NON-NLS
String contentPath = saveContent(file, "original"); //NON-NLS
String nameInImage;
try {
nameInImage = file.getUniquePath();
@ -785,32 +849,27 @@ class ReportHTML implements TableReportModule {
}
StringBuilder linkToThumbnail = new StringBuilder();
linkToThumbnail.append("<div id='thumbnail_link'>");
linkToThumbnail.append("<a href=\""); //NON-NLS
linkToThumbnail.append(contentPath);
linkToThumbnail.append("\" target=\"_top\">");
linkToThumbnail.append("<img src=\"").append(thumbnailPath).append("\" title=\"").append(nameInImage).append("\"/>"); //NON-NLS
linkToThumbnail.append("</a><br>"); //NON-NLS
linkToThumbnail.append(file.getName()).append("<br>"); //NON-NLS
Services services = currentCase.getServices();
TagsManager tagsManager = services.getTagsManager();
try {
List<ContentTag> tags = tagsManager.getContentTagsByContent(content);
if (tags.size() > 0) {
linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags"));
}
for (int i = 0; i < tags.size(); i++) {
ContentTag tag = tags.get(i);
String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
if (i != tags.size() - 1) {
linkToThumbnail.append(", ");
}
}
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Could not find get tags for file.", ex); //NON-NLS
linkToThumbnail.append("<div id='thumbnail_link'><a href=\"")
.append((imageWithTagsFullPath != null) ? imageWithTagsFullPath : contentPath)
.append("\" target=\"_top\"><img src=\"")
.append(thumbnailPath).append("\" title=\"").append(nameInImage).append("\"/></a><br>") //NON-NLS
.append(file.getName()).append("<br>"); //NON-NLS
if(imageWithTagsFullPath != null) {
linkToThumbnail.append("<a href=\"").append(contentPath).append("\" target=\"_top\">View Original</a><br>");
}
if (!contentTags.isEmpty()) {
linkToThumbnail.append(NbBundle.getMessage(this.getClass(), "ReportHTML.thumbLink.tags"));
}
for (int i = 0; i < contentTags.size(); i++) {
ContentTag tag = contentTags.get(i);
String notableString = tag.getName().getKnownStatus() == TskData.FileKnown.BAD ? TagsManager.getNotableTagLabel() : "";
linkToThumbnail.append(tag.getName().getDisplayName()).append(notableString);
if (i != contentTags.size() - 1) {
linkToThumbnail.append(", ");
}
}
linkToThumbnail.append("</div>");
currentRow.add(linkToThumbnail.toString());
@ -839,17 +898,8 @@ class ReportHTML implements TableReportModule {
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNALLOC_BLOCKS
|| file.getType() == TSK_DB_FILES_TYPE_ENUM.UNUSED_BLOCKS;
}
/**
* Save a local copy of the given file in the reports folder.
*
* @param file File to save
* @param dirName Custom top-level folder to use to store the files in (tag
* name, etc.)
*
* @return Path to where file was stored (relative to root of HTML folder)
*/
public String saveContent(AbstractFile file, String dirName) {
private String makeCustomUniqueFilePath(AbstractFile file, String dirName) {
// clean up the dir name passed in
String dirName2 = org.sleuthkit.autopsy.coreutils.FileUtil.escapeFileName(dirName);
@ -883,16 +933,32 @@ class ReportHTML implements TableReportModule {
}
localFilePath.append(File.separator);
localFilePath.append(fileName);
return localFilePath.toString();
}
/**
* Save a local copy of the given file in the reports folder.
*
* @param file File to save
* @param dirName Custom top-level folder to use to store the files in (tag
* name, etc.)
*
* @return Path to where file was stored (relative to root of HTML folder)
*/
public String saveContent(AbstractFile file, String dirName) {
String localFilePath = makeCustomUniqueFilePath(file, dirName);
// If the local file doesn't already exist, create it now.
// The existence check is necessary because it is possible to apply multiple tags with the same tagName to a file.
File localFile = new File(localFilePath.toString());
File localFile = new File(localFilePath);
if (!localFile.exists()) {
ExtractFscContentVisitor.extract(file, localFile, null, null);
}
// get the relative path
return localFilePath.toString().substring(subPath.length());
return localFilePath.substring(subPath.length());
}
/**

View File

@ -120,4 +120,13 @@ public final class TextTranslationService {
public boolean hasProvider() {
return selectedTranslator.isPresent();
}
/**
* Returns the hard limit for translation request sizes.
*
* @return
*/
public int getMaxPayloadSize() {
return selectedTranslator.get().getMaxPayloadSize();
}
}

View File

@ -56,4 +56,11 @@ public interface TextTranslator {
* Save the settings as they have been modified in the component.
*/
void saveSettings();
/**
* Returns the hard limit for translation request sizes.
*
* @return
*/
int getMaxPayloadSize();
}

View File

@ -0,0 +1,179 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2019 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sleuthkit.autopsy.texttranslation.translators;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.squareup.okhttp.MediaType;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.Request;
import com.squareup.okhttp.RequestBody;
import com.squareup.okhttp.Response;
import java.awt.Component;
import java.io.IOException;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.texttranslation.TextTranslator;
import org.sleuthkit.autopsy.texttranslation.TranslationException;
/**
* Translates text by making HTTP requests to Bing Translator. This requires a
* valid subscription key for a Microsoft Azure account.
*/
@ServiceProvider(service = TextTranslator.class)
public class BingTranslator implements TextTranslator {
//The target language follows the to= in the string below. You can include multiple target
//languages separated by commas. A full list of supported languages is here:
//https://docs.microsoft.com/en-us/azure/cognitive-services/translator/language-support
private static final String BASE_URL = "https://api.cognitive.microsofttranslator.com/translate?api-version=3.0&to=";
private static final int MAX_STRING_LENGTH = 5000;
private final BingTranslatorSettingsPanel settingsPanel;
private final BingTranslatorSettings settings = new BingTranslatorSettings();
// This sends messages to Microsoft.
private final OkHttpClient CLIENT = new OkHttpClient();
/**
* Create a Bing Translator
*/
public BingTranslator() {
settingsPanel = new BingTranslatorSettingsPanel(settings.getAuthenticationKey(), settings.getTargetLanguageCode());
}
/**
* Get the tranlationurl for the specified language code
*
*
*
* @param languageCode language code for language to translate to
*
* @return a string representation of the url to request translation from
*/
static String getTranlatorUrl(String languageCode) {
return BASE_URL + languageCode;
}
/**
* Converts an input text to the JSON format required by Bing Translator,
* posts it to Microsoft, and returns the JSON text response.
*
* @param string The input text to be translated.
*
* @return The translation response as a JSON string
*
* @throws IOException if the request could not be executed due to
* cancellation, a connectivity problem or timeout.
*/
public String postTranslationRequest(String string) throws IOException {
MediaType mediaType = MediaType.parse("application/json");
JsonArray jsonArray = new JsonArray();
JsonObject jsonObject = new JsonObject();
jsonObject.addProperty("Text", string);
jsonArray.add(jsonObject);
String bodyString = jsonArray.toString();
RequestBody body = RequestBody.create(mediaType,
bodyString);
Request request = new Request.Builder()
.url(getTranlatorUrl(settings.getTargetLanguageCode())).post(body)
.addHeader("Ocp-Apim-Subscription-Key", settings.getAuthenticationKey())
.addHeader("Content-type", "application/json").build();
Response response = CLIENT.newCall(request).execute();
return response.body().string();
}
@Override
public String translate(String string) throws TranslationException {
if (settings.getAuthenticationKey() == null || settings.getAuthenticationKey().isEmpty()) {
throw new TranslationException("Bing Translator has not been configured, authentication key needs to be specified");
}
String toTranslate = string.trim();
//Translates some text into English, without specifying the source langauge.
//Google Translate required us to replace (\r\n|\n) with <br />
//but Bing Translator doesn not have that requirement.
//The free account has a maximum file size. If you have a paid account,
//you probably still want to limit file size to prevent accidentally
//translating very large documents.
if (toTranslate.length() > MAX_STRING_LENGTH) {
toTranslate = toTranslate.substring(0, MAX_STRING_LENGTH);
}
try {
String response = postTranslationRequest(toTranslate);
return parseJSONResponse(response);
} catch (IOException | TranslationException ex) {
throw new TranslationException("Exception while attempting to translate using BingTranslator", ex);
}
}
@Messages({"BingTranslator.name.text=Bing Translator"})
@Override
public String getName() {
return Bundle.BingTranslator_name_text();
}
@Override
public Component getComponent() {
return settingsPanel;
}
@Override
public void saveSettings() {
settings.setAuthenticationKey(settingsPanel.getAuthenticationKey());
settings.setTargetLanguageCode(settingsPanel.getTargetLanguageCode());
settings.saveSettings();
}
/**
* Parse the response to get the translated text
*
* @param json_text the json which was received as a response to a
* translation request
*
* @return the translated text
*
* @throws TranslationException
*/
private String parseJSONResponse(String json_text) throws TranslationException {
/*
* Here is an example of the text we get from Bing when input is "gato",
* the Spanish word for cat: [ { "detectedLanguage": { "language": "es",
* "score": 1.0 }, "translations": [ { "text": "cat", "to": "en" } ] } ]
*/
JsonParser parser = new JsonParser();
try {
JsonArray responses = parser.parse(json_text).getAsJsonArray();
//As far as I know, there's always exactly one item in the array.
JsonObject response0 = responses.get(0).getAsJsonObject();
JsonArray translations = response0.getAsJsonArray("translations");
JsonObject translation0 = translations.get(0).getAsJsonObject();
return translation0.get("text").getAsString();
} catch (IllegalStateException | ClassCastException | NullPointerException | IndexOutOfBoundsException e) {
throw new TranslationException("JSON text does not match Bing Translator scheme: " + e);
}
}
@Override
public int getMaxPayloadSize() {
return MAX_STRING_LENGTH;
}
}

Some files were not shown because too many files have changed in this diff Show More