Merge remote-tracking branch 'upstream/develop' into aut-2114

This commit is contained in:
Oliver Spohngellert 2016-05-12 11:07:14 -04:00
commit a5a8ec758e
43 changed files with 1395 additions and 649 deletions

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.contentviewers;
import java.awt.Component; import java.awt.Component;
import org.openide.nodes.Node; import org.openide.nodes.Node;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages;
import org.openide.util.lookup.ServiceProvider; import org.openide.util.lookup.ServiceProvider;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer; import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.datamodel.ContentUtils; import org.sleuthkit.autopsy.datamodel.ContentUtils;
@ -112,6 +113,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
sb.append("</td></tr>"); //NON-NLS sb.append("</td></tr>"); //NON-NLS
} }
@Messages({"Metadata.tableRowTitle.mimeType=MIME Type"})
@Override @Override
public void setNode(Node node) { public void setNode(Node node) {
AbstractFile file = node.getLookup().lookup(AbstractFile.class); AbstractFile file = node.getLookup().lookup(AbstractFile.class);
@ -130,13 +132,15 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
} }
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.type"), file.getType().getName()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.type"), file.getType().getName());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), new Long(file.getSize()).toString()); addRow(sb, Bundle.Metadata_tableRowTitle_mimeType(), file.getMIMEType());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.size"), Long.toString(file.getSize()));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.fileNameAlloc"), file.getDirFlagAsString());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.metadataAlloc"), file.getMetaFlagsAsString());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), ContentUtils.getStringTime(file.getMtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.modified"), ContentUtils.getStringTime(file.getMtime(), file));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.accessed"), ContentUtils.getStringTime(file.getAtime(), file));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.created"), ContentUtils.getStringTime(file.getCrtime(), file));
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file)); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.changed"), ContentUtils.getStringTime(file.getCtime(), file));
String md5 = file.getMd5Hash(); String md5 = file.getMd5Hash();
if (md5 == null) { if (md5 == null) {
@ -145,7 +149,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.md5"), md5);
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.hashLookupResults"), file.getKnown().toString());
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), new Long(file.getId()).toString()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.internalid"), Long.toString(file.getId()));
if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) { if (file.getType().compareTo(TSK_DB_FILES_TYPE_ENUM.LOCAL) == 0) {
addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath()); addRow(sb, NbBundle.getMessage(this.getClass(), "Metadata.tableRowTitle.localPath"), file.getLocalAbsPath());
} }
@ -205,10 +209,7 @@ public class Metadata extends javax.swing.JPanel implements DataContentViewer {
@Override @Override
public boolean isSupported(Node node) { public boolean isSupported(Node node) {
AbstractFile file = node.getLookup().lookup(AbstractFile.class); AbstractFile file = node.getLookup().lookup(AbstractFile.class);
if (file == null) { return file != null;
return false;
}
return true;
} }
@Override @Override

View File

@ -130,6 +130,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useBestViewerRB.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useBestViewerRB.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useBestViewerRBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="keepCurrentViewerRB"> <Component class="javax.swing.JRadioButton" name="keepCurrentViewerRB">
<Properties> <Properties>
@ -143,6 +146,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.keepCurrentViewerRB.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.keepCurrentViewerRB.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="keepCurrentViewerRBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="jLabelSelectFile"> <Component class="javax.swing.JLabel" name="jLabelSelectFile">
<Properties> <Properties>
@ -168,6 +174,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useLocalTimeRB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useLocalTimeRB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useLocalTimeRBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="useGMTTimeRB"> <Component class="javax.swing.JRadioButton" name="useGMTTimeRB">
<Properties> <Properties>
@ -178,6 +187,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useGMTTimeRB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.useGMTTimeRB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="useGMTTimeRBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="jLabelHideKnownFiles"> <Component class="javax.swing.JLabel" name="jLabelHideKnownFiles">
<Properties> <Properties>
@ -192,6 +204,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.dataSourcesHideKnownCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.dataSourcesHideKnownCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="dataSourcesHideKnownCBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="viewsHideKnownCB"> <Component class="javax.swing.JCheckBox" name="viewsHideKnownCB">
<Properties> <Properties>
@ -199,6 +214,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.viewsHideKnownCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.viewsHideKnownCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewsHideKnownCBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="jLabelNumThreads"> <Component class="javax.swing.JLabel" name="jLabelNumThreads">
<Properties> <Properties>
@ -213,6 +231,9 @@
<StringArray count="0"/> <StringArray count="0"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="numberOfFileIngestThreadsComboBoxActionPerformed"/>
</Events>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;Integer&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;Integer&gt;"/>
</AuxValues> </AuxValues>
@ -257,6 +278,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.jFormattedTextFieldProcTimeOutHrs.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/corecomponents/Bundle.properties" key="AutopsyOptionsPanel.jFormattedTextFieldProcTimeOutHrs.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jFormattedTextFieldProcTimeOutHrsActionPerformed"/>
</Events>
<AuxValues> <AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JFormattedTextField(NumberFormat.getIntegerInstance());"/> <AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new JFormattedTextField(NumberFormat.getIntegerInstance());"/>
</AuxValues> </AuxValues>

View File

@ -18,9 +18,14 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.text.NumberFormat; import java.text.NumberFormat;
import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultComboBoxModel;
import javax.swing.JFormattedTextField; import javax.swing.JFormattedTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.core.UserPreferences; import org.sleuthkit.autopsy.core.UserPreferences;
@ -29,7 +34,9 @@ import org.sleuthkit.autopsy.core.UserPreferences;
*/ */
final class AutopsyOptionsPanel extends javax.swing.JPanel { final class AutopsyOptionsPanel extends javax.swing.JPanel {
AutopsyOptionsPanel(AutopsyOptionsPanelController controller) { private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
AutopsyOptionsPanel() {
initComponents(); initComponents();
int availableProcessors = Runtime.getRuntime().availableProcessors(); int availableProcessors = Runtime.getRuntime().availableProcessors();
Integer fileIngestThreadCountChoices[]; Integer fileIngestThreadCountChoices[];
@ -71,6 +78,25 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
numberOfFileIngestThreadsComboBox.setModel(new DefaultComboBoxModel<>(fileIngestThreadCountChoices)); numberOfFileIngestThreadsComboBox.setModel(new DefaultComboBoxModel<>(fileIngestThreadCountChoices));
restartRequiredLabel.setText(NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text", recommendedFileIngestThreadCount)); restartRequiredLabel.setText(NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text", recommendedFileIngestThreadCount));
// TODO listen to changes in form fields and call controller.changed() // TODO listen to changes in form fields and call controller.changed()
DocumentListener docListener = new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void removeUpdate(DocumentEvent e) {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
@Override
public void changedUpdate(DocumentEvent e) {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
};
this.jFormattedTextFieldProcTimeOutHrs.getDocument().addDocumentListener(docListener);
} }
void load() { void load() {
@ -113,6 +139,16 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
} }
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
boolean valid() { boolean valid() {
return true; return true;
} }
@ -148,10 +184,20 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
useBestViewerRB.setSelected(true); useBestViewerRB.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(useBestViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(useBestViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.text")); // NOI18N
useBestViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.toolTipText")); // NOI18N useBestViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useBestViewerRB.toolTipText")); // NOI18N
useBestViewerRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
useBestViewerRBActionPerformed(evt);
}
});
buttonGroup1.add(keepCurrentViewerRB); buttonGroup1.add(keepCurrentViewerRB);
org.openide.awt.Mnemonics.setLocalizedText(keepCurrentViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(keepCurrentViewerRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.text")); // NOI18N
keepCurrentViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.toolTipText")); // NOI18N keepCurrentViewerRB.setToolTipText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.keepCurrentViewerRB.toolTipText")); // NOI18N
keepCurrentViewerRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
keepCurrentViewerRBActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelSelectFile, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelSelectFile.text")); // NOI18N
@ -160,18 +206,44 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
buttonGroup3.add(useLocalTimeRB); buttonGroup3.add(useLocalTimeRB);
useLocalTimeRB.setSelected(true); useLocalTimeRB.setSelected(true);
org.openide.awt.Mnemonics.setLocalizedText(useLocalTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useLocalTimeRB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(useLocalTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useLocalTimeRB.text")); // NOI18N
useLocalTimeRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
useLocalTimeRBActionPerformed(evt);
}
});
buttonGroup3.add(useGMTTimeRB); buttonGroup3.add(useGMTTimeRB);
org.openide.awt.Mnemonics.setLocalizedText(useGMTTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useGMTTimeRB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(useGMTTimeRB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.useGMTTimeRB.text")); // NOI18N
useGMTTimeRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
useGMTTimeRBActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabelHideKnownFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideKnownFiles.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelHideKnownFiles, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelHideKnownFiles.text")); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideKnownCB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(dataSourcesHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.dataSourcesHideKnownCB.text")); // NOI18N
dataSourcesHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
dataSourcesHideKnownCBActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(viewsHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideKnownCB.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(viewsHideKnownCB, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.viewsHideKnownCB.text")); // NOI18N
viewsHideKnownCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
viewsHideKnownCBActionPerformed(evt);
}
});
org.openide.awt.Mnemonics.setLocalizedText(jLabelNumThreads, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelNumThreads.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelNumThreads, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelNumThreads.text")); // NOI18N
numberOfFileIngestThreadsComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
numberOfFileIngestThreadsComboBoxActionPerformed(evt);
}
});
restartRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N restartRequiredLabel.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/corecomponents/warning16.png"))); // NOI18N
org.openide.awt.Mnemonics.setLocalizedText(restartRequiredLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(restartRequiredLabel, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.restartRequiredLabel.text")); // NOI18N
@ -187,6 +259,11 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
org.openide.awt.Mnemonics.setLocalizedText(jLabelProcessTimeOutUnits, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelProcessTimeOutUnits.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(jLabelProcessTimeOutUnits, org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jLabelProcessTimeOutUnits.text")); // NOI18N
jFormattedTextFieldProcTimeOutHrs.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jFormattedTextFieldProcTimeOutHrs.text")); // NOI18N jFormattedTextFieldProcTimeOutHrs.setText(org.openide.util.NbBundle.getMessage(AutopsyOptionsPanel.class, "AutopsyOptionsPanel.jFormattedTextFieldProcTimeOutHrs.text")); // NOI18N
jFormattedTextFieldProcTimeOutHrs.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jFormattedTextFieldProcTimeOutHrsActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout); this.setLayout(layout);
@ -271,8 +348,41 @@ final class AutopsyOptionsPanel extends javax.swing.JPanel {
private void jCheckBoxEnableProcTimeoutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxEnableProcTimeoutActionPerformed private void jCheckBoxEnableProcTimeoutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jCheckBoxEnableProcTimeoutActionPerformed
jFormattedTextFieldProcTimeOutHrs.setEditable(jCheckBoxEnableProcTimeout.isSelected()); jFormattedTextFieldProcTimeOutHrs.setEditable(jCheckBoxEnableProcTimeout.isSelected());
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_jCheckBoxEnableProcTimeoutActionPerformed }//GEN-LAST:event_jCheckBoxEnableProcTimeoutActionPerformed
private void useBestViewerRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useBestViewerRBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_useBestViewerRBActionPerformed
private void keepCurrentViewerRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_keepCurrentViewerRBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_keepCurrentViewerRBActionPerformed
private void dataSourcesHideKnownCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSourcesHideKnownCBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_dataSourcesHideKnownCBActionPerformed
private void viewsHideKnownCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewsHideKnownCBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_viewsHideKnownCBActionPerformed
private void useLocalTimeRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useLocalTimeRBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_useLocalTimeRBActionPerformed
private void useGMTTimeRBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_useGMTTimeRBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_useGMTTimeRBActionPerformed
private void numberOfFileIngestThreadsComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_numberOfFileIngestThreadsComboBoxActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_numberOfFileIngestThreadsComboBoxActionPerformed
private void jFormattedTextFieldProcTimeOutHrsActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jFormattedTextFieldProcTimeOutHrsActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_jFormattedTextFieldProcTimeOutHrsActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.ButtonGroup buttonGroup1; private javax.swing.ButtonGroup buttonGroup1;
private javax.swing.ButtonGroup buttonGroup3; private javax.swing.ButtonGroup buttonGroup3;

View File

@ -18,15 +18,13 @@
*/ */
package org.sleuthkit.autopsy.corecomponents; package org.sleuthkit.autopsy.corecomponents;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import javax.swing.JComponent; import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_General", @OptionsPanelController.TopLevelRegistration(categoryName = "#OptionsCategory_Name_General",
@ -41,18 +39,33 @@ public final class AutopsyOptionsPanelController extends OptionsPanelController
private boolean changed; private boolean changed;
private static final Logger logger = Logger.getLogger(AutopsyOptionsPanelController.class.getName()); private static final Logger logger = Logger.getLogger(AutopsyOptionsPanelController.class.getName());
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
getPanel().store(); if (changed) {
changed = false; getPanel().store();
changed = false;
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
} }
@ -62,6 +75,12 @@ public final class AutopsyOptionsPanelController extends OptionsPanelController
return getPanel().valid(); return getPanel().valid();
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -89,7 +108,15 @@ public final class AutopsyOptionsPanelController extends OptionsPanelController
private AutopsyOptionsPanel getPanel() { private AutopsyOptionsPanel getPanel() {
if (panel == null) { if (panel == null) {
panel = new AutopsyOptionsPanel(this); panel = new AutopsyOptionsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
}
});
} }
return panel; return panel;
} }
@ -97,26 +124,9 @@ public final class AutopsyOptionsPanelController extends OptionsPanelController
void changed() { void changed() {
if (!changed) { if (!changed) {
changed = true; changed = true;
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "GeneralOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "GeneralOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
} }

View File

@ -42,7 +42,6 @@ import org.openide.util.NbBundle;
import org.openide.util.Utilities; import org.openide.util.Utilities;
import org.openide.util.lookup.Lookups; import org.openide.util.lookup.Lookups;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corecomponents.DataContentTopComponent;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.Report; import org.sleuthkit.datamodel.Report;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -111,7 +110,7 @@ public final class Reports implements AutopsyVisitableItem {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String eventType = evt.getPropertyName(); String eventType = evt.getPropertyName();
if (eventType.equals(Case.Events.REPORT_ADDED.toString())) { if (eventType.equals(Case.Events.REPORT_ADDED.toString()) || eventType.equals(Case.Events.REPORT_DELETED.toString())) {
/** /**
* Checking for a current case is a stop gap measure * Checking for a current case is a stop gap measure
* until a different way of handling the closing of * until a different way of handling the closing of
@ -272,7 +271,6 @@ public final class Reports implements AutopsyVisitableItem {
JOptionPane.YES_NO_OPTION) == 0) { JOptionPane.YES_NO_OPTION) == 0) {
try { try {
Case.getCurrentCase().deleteReports(selectedReportsCollection, checkbox.isSelected()); Case.getCurrentCase().deleteReports(selectedReportsCollection, checkbox.isSelected());
DataContentTopComponent.findInstance().repaint();
} catch (TskCoreException | IllegalStateException ex) { } catch (TskCoreException | IllegalStateException ex) {
Logger.getLogger(DeleteReportAction.class.getName()).log(Level.INFO, "Error deleting the reports. ", ex); // NON-NLS - Provide solution to the user? Logger.getLogger(DeleteReportAction.class.getName()).log(Level.INFO, "Error deleting the reports. ", ex); // NON-NLS - Provide solution to the user?
} }

View File

@ -114,4 +114,3 @@ IngestJobSettingsPanel.globalSettingsButton.text=Global Settings
gest gest
IngestJobSettingsPanel.globalSettingsButton.actionCommand=Advanced IngestJobSettingsPanel.globalSettingsButton.actionCommand=Advanced
IngestJobSettingsPanel.globalSettingsButton.text=Global Settings IngestJobSettingsPanel.globalSettingsButton.text=Global Settings
IngestJobSettingsPanel.descriptionLabel.text=dummmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy texxxxxxxxxxxxxxxttttttttttttttttttttttttttttttttttttttttttt

View File

@ -1032,7 +1032,7 @@ final class DataSourceIngestJob {
*/ */
private void logIngestModuleErrors(List<IngestModuleError> errors) { private void logIngestModuleErrors(List<IngestModuleError> errors) {
for (IngestModuleError error : errors) { for (IngestModuleError error : errors) {
DataSourceIngestJob.logger.log(Level.SEVERE, String.format("%s experienced an error analyzing %s (jobId=%d)", error.getModuleDisplayName(), dataSource.getName(), this.id), error.getModuleError()); //NON-NLS DataSourceIngestJob.logger.log(Level.SEVERE, String.format("%s experienced an error analyzing %s (jobId=%d)", error.getModuleDisplayName(), dataSource.getName(), this.id), error.getThrowable()); //NON-NLS
} }
} }

View File

@ -181,9 +181,7 @@
</Component> </Component>
<Component class="javax.swing.JLabel" name="descriptionLabel"> <Component class="javax.swing.JLabel" name="descriptionLabel">
<Properties> <Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.i18n.form.FormI18nStringEditor"> <Property name="text" type="java.lang.String" value="DO NOT REMOVE. This dummy text is used to anchor the inner panel&apos;s size to the outer panel, while still being expandable. Without this the expandability behavior doesn&apos;t work well. This text will never be shown, as it would only be shown when no module is selected (which is not possible)."/>
<ResourceString bundle="org/sleuthkit/autopsy/ingest/Bundle.properties" key="IngestJobSettingsPanel.descriptionLabel.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property>
</Properties> </Properties>
</Component> </Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1"> <Container class="javax.swing.JScrollPane" name="jScrollPane1">

View File

@ -184,7 +184,7 @@ public final class IngestJobSettingsPanel extends javax.swing.JPanel {
} }
}); });
descriptionLabel.setText(org.openide.util.NbBundle.getMessage(IngestJobSettingsPanel.class, "IngestJobSettingsPanel.descriptionLabel.text")); // NOI18N descriptionLabel.setText("DO NOT REMOVE. This dummy text is used to anchor the inner panel's size to the outer panel, while still being expandable. Without this the expandability behavior doesn't work well. This text will never be shown, as it would only be shown when no module is selected (which is not possible).");
jScrollPane1.setBorder(null); jScrollPane1.setBorder(null);
jScrollPane1.setPreferredSize(new java.awt.Dimension(250, 180)); jScrollPane1.setPreferredSize(new java.awt.Dimension(250, 180));

View File

@ -65,12 +65,6 @@ import org.sleuthkit.datamodel.Content;
* Manages the creation and execution of ingest jobs, i.e., the processing of * Manages the creation and execution of ingest jobs, i.e., the processing of
* data sources by ingest modules. * data sources by ingest modules.
*/ */
@NbBundle.Messages({
"# {0} - error message", "IngestManager.StartIngestJobsTask.run.startupErr.dlgErrorList=\nErrors: \n{0}",
"IngestManager.StartIngestJobsTask.run.startupErr.dlgSolution=Please disable the failed modules or fix the errors and then restart ingest \nby right clicking on the data source and selecting Run Ingest Modules.",
"IngestManager.StartIngestJobsTask.run.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest job cancelled.",
"IngestManager.StartIngestJobsTask.run.startupErr.dlgTitle=Ingest Failure"
})
public class IngestManager { public class IngestManager {
private static final Logger logger = Logger.getLogger(IngestManager.class.getName()); private static final Logger logger = Logger.getLogger(IngestManager.class.getName());
@ -532,6 +526,12 @@ public class IngestManager {
* @return The IngestJobStartResult describing the results of attempting to * @return The IngestJobStartResult describing the results of attempting to
* start the ingest job. * start the ingest job.
*/ */
@NbBundle.Messages({
"IngestManager.startupErr.dlgTitle=Ingest Module Startup Failure",
"IngestManager.startupErr.dlgMsg=Unable to start up one or more ingest modules, ingest cancelled.",
"IngestManager.startupErr.dlgSolution=Please disable the failed modules or fix the errors before restarting ingest.",
"IngestManager.startupErr.dlgErrorList=Errors:"
})
private IngestJobStartResult startIngestJob(IngestJob job) { private IngestJobStartResult startIngestJob(IngestJob job) {
List<IngestModuleError> errors = null; List<IngestModuleError> errors = null;
if (this.jobCreationIsEnabled) { if (this.jobCreationIsEnabled) {
@ -576,47 +576,29 @@ public class IngestManager {
this.jobsById.remove(job.getId()); this.jobsById.remove(job.getId());
} }
for (IngestModuleError error : errors) { for (IngestModuleError error : errors) {
logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getModuleError()); //NON-NLS logger.log(Level.SEVERE, String.format("Error starting %s ingest module for job %d", error.getModuleDisplayName(), job.getId()), error.getThrowable()); //NON-NLS
} }
IngestManager.logger.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS IngestManager.logger.log(Level.SEVERE, "Ingest job {0} could not be started", job.getId()); //NON-NLS
if (RuntimeProperties.coreComponentsAreActive()) { if (RuntimeProperties.coreComponentsAreActive()) {
final StringBuilder message = new StringBuilder();
StringBuilder moduleStartUpErrors = new StringBuilder(); message.append(Bundle.IngestManager_startupErr_dlgMsg()).append("\n");
message.append(Bundle.IngestManager_startupErr_dlgSolution()).append("\n\n");
message.append(Bundle.IngestManager_startupErr_dlgErrorList()).append("\n");
for (IngestModuleError error : errors) { for (IngestModuleError error : errors) {
String moduleName = error.getModuleDisplayName(); String moduleName = error.getModuleDisplayName();
moduleStartUpErrors.append(moduleName); String errorMessage = error.getThrowable().getLocalizedMessage();
moduleStartUpErrors.append(": "); message.append(moduleName).append(": ").append(errorMessage).append("\n");
moduleStartUpErrors.append(error.getModuleError().getLocalizedMessage());
moduleStartUpErrors.append("\n");
} }
EventQueue.invokeLater(new NotifyUserOfErrors(moduleStartUpErrors.toString())); message.append("\n\n");
EventQueue.invokeLater(() -> {
JOptionPane.showMessageDialog(null, message, Bundle.IngestManager_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
});
} }
} }
} }
return new IngestJobStartResult(job, null, errors); return new IngestJobStartResult(job, null, errors);
} }
class NotifyUserOfErrors implements Runnable {
private String startupErrors;
NotifyUserOfErrors(String startupErrors) {
this.startupErrors = startupErrors;
}
@Override
public void run() {
StringBuilder notifyMessage = new StringBuilder();
notifyMessage.append(Bundle.IngestManager_StartIngestJobsTask_run_startupErr_dlgMsg());
notifyMessage.append("\n");
notifyMessage.append(Bundle.IngestManager_StartIngestJobsTask_run_startupErr_dlgSolution());
notifyMessage.append("\n");
notifyMessage.append(Bundle.IngestManager_StartIngestJobsTask_run_startupErr_dlgErrorList(startupErrors));
notifyMessage.append("\n\n");
JOptionPane.showMessageDialog(null, notifyMessage.toString(), Bundle.IngestManager_StartIngestJobsTask_run_startupErr_dlgTitle(), JOptionPane.ERROR_MESSAGE);
}
}
synchronized void finishIngestJob(IngestJob job) { synchronized void finishIngestJob(IngestJob job) {
long jobId = job.getId(); long jobId = job.getId();
synchronized (jobsById) { synchronized (jobsById) {

View File

@ -19,24 +19,54 @@
package org.sleuthkit.autopsy.ingest; package org.sleuthkit.autopsy.ingest;
/** /**
* Encapsulates an exception thrown by an ingest module during an operation such * Encapsulates a Throwable thrown by an ingest module with the display name of
* as startup or shut down with an exception object for the error that occurred. * the module for logging purposes.
*/ */
public final class IngestModuleError { public final class IngestModuleError {
private final String moduleDisplayName; private final String moduleDisplayName;
private final Throwable error; private final Throwable throwable;
IngestModuleError(String moduleDisplayName, Throwable error) { /**
* Constructs an object that encapsulates a Throwable thrown by an ingest
* module with the display name of the module for logging purposes.
*
* @param moduleDisplayName The display name of the module.
* @param throwable The throwable.
*/
IngestModuleError(String moduleDisplayName, Throwable throwable) {
this.moduleDisplayName = moduleDisplayName; this.moduleDisplayName = moduleDisplayName;
this.error = error; this.throwable = throwable;
} }
/**
* Gets the module display name.
*
* @return The module display name.
*/
public String getModuleDisplayName() { public String getModuleDisplayName() {
return this.moduleDisplayName; return this.moduleDisplayName;
} }
/**
* Gets the throwable.
*
* @return The Throwable
*/
public Throwable getThrowable() {
return this.throwable;
}
/**
* Gets the throwable.
*
* @return The Throwable
*
* @deprecated Use getThrowable instead.
*
*/
@Deprecated
public Throwable getModuleError() { public Throwable getModuleError() {
return this.error; return this.throwable;
} }
} }

View File

@ -4,15 +4,13 @@
*/ */
package org.sleuthkit.autopsy.modules.fileextmismatch; package org.sleuthkit.autopsy.modules.fileextmismatch;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import javax.swing.JComponent; import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration( @OptionsPanelController.TopLevelRegistration(
@ -28,19 +26,33 @@ public final class FileExtMismatchOptionsPanelController extends OptionsPanelCon
private boolean changed; private boolean changed;
private static final Logger logger = Logger.getLogger(FileExtMismatchOptionsPanelController.class.getName()); private static final Logger logger = Logger.getLogger(FileExtMismatchOptionsPanelController.class.getName());
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
//getPanel().store(); if (changed) {
getPanel().ok(); getPanel().ok();
changed = false; changed = false;
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
getPanel().cancel(); getPanel().cancel();
@ -51,6 +63,12 @@ public final class FileExtMismatchOptionsPanelController extends OptionsPanelCon
return getPanel().valid(); return getPanel().valid();
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -79,6 +97,14 @@ public final class FileExtMismatchOptionsPanelController extends OptionsPanelCon
private FileExtMismatchSettingsPanel getPanel() { private FileExtMismatchSettingsPanel getPanel() {
if (panel == null) { if (panel == null) {
panel = new FileExtMismatchSettingsPanel(); panel = new FileExtMismatchSettingsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
}
});
} }
return panel; return panel;
} }
@ -87,25 +113,9 @@ public final class FileExtMismatchOptionsPanelController extends OptionsPanelCon
if (!changed) { if (!changed) {
changed = true; changed = true;
try { pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "FileExtMismatchOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "FileExtMismatchOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "FileExtMismatchOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
try { pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "FileExtMismatchOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "FileExtMismatchOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "FileExtMismatchOptionsPanelController.moduleErr.msg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
} }

View File

@ -19,6 +19,8 @@
package org.sleuthkit.autopsy.modules.fileextmismatch; package org.sleuthkit.autopsy.modules.fileextmismatch;
import java.awt.Color; import java.awt.Color;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
@ -30,6 +32,7 @@ import javax.swing.ListSelectionModel;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -54,6 +57,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
private String selectedExt = ""; private String selectedExt = "";
ListSelectionModel lsm = null; ListSelectionModel lsm = null;
private FileTypeDetector fileTypeDetector; private FileTypeDetector fileTypeDetector;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
public FileExtMismatchSettingsPanel() { public FileExtMismatchSettingsPanel() {
mimeTableModel = new MimeTableModel(); mimeTableModel = new MimeTableModel();
@ -132,6 +136,16 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
addExtButton.setEnabled(false); addExtButton.setEnabled(false);
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
private void clearErrLabels() { private void clearErrLabels() {
mimeErrLabel.setText(" "); mimeErrLabel.setText(" ");
extErrorLabel.setText(" "); extErrorLabel.setText(" ");
@ -378,6 +392,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
updateExtList(); updateExtList();
extTableModel.resync(); extTableModel.resync();
this.userExtTextField.setText(""); this.userExtTextField.setText("");
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_addExtButtonActionPerformed }//GEN-LAST:event_addExtButtonActionPerformed
private void addTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addTypeButtonActionPerformed private void addTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addTypeButtonActionPerformed
@ -400,7 +415,17 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
return; return;
} }
boolean mimeTypeDetectable = (null != fileTypeDetector) ? fileTypeDetector.isDetectable(newMime) : false; FileTypeDetector detector;
try {
detector = new FileTypeDetector();
} catch (FileTypeDetector.FileTypeDetectorInitException ex) {
mimeErrLabel.setForeground(Color.red);
mimeErrLabel.setText(NbBundle.getMessage(this.getClass(),
"FileExtMismatchConfigPanel.addTypeButton.mimeTypeNotDetectable"));
logger.log(Level.WARNING, "Couldn't create file type detector for file ext mismatch settings.", ex);
return;
}
boolean mimeTypeDetectable = (null != detector) ? detector.isDetectable(newMime) : false;
if (!mimeTypeDetectable) { if (!mimeTypeDetectable) {
mimeErrLabel.setForeground(Color.red); mimeErrLabel.setForeground(Color.red);
mimeErrLabel.setText(NbBundle.getMessage(this.getClass(), mimeErrLabel.setText(NbBundle.getMessage(this.getClass(),
@ -414,6 +439,8 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
updateMimeList(); updateMimeList();
mimeTableModel.resync(); mimeTableModel.resync();
userTypeTextField.setText(""); userTypeTextField.setText("");
this.clearErrLabels();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_addTypeButtonActionPerformed }//GEN-LAST:event_addTypeButtonActionPerformed
private void userExtTextFieldFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_userExtTextFieldFocusGained private void userExtTextFieldFocusGained(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_userExtTextFieldFocusGained
@ -438,6 +465,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
// Refresh table // Refresh table
updateMimeList(); updateMimeList();
mimeTableModel.resync(); mimeTableModel.resync();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_removeTypeButtonActionPerformed }//GEN-LAST:event_removeTypeButtonActionPerformed
private void removeExtButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeExtButtonActionPerformed private void removeExtButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeExtButtonActionPerformed
@ -465,6 +493,7 @@ final class FileExtMismatchSettingsPanel extends IngestModuleGlobalSettingsPanel
// Refresh tables // Refresh tables
updateExtList(); updateExtList();
extTableModel.resync(); extTableModel.resync();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_removeExtButtonActionPerformed }//GEN-LAST:event_removeExtButtonActionPerformed
private void updateMimeList() { private void updateMimeList() {

View File

@ -25,6 +25,8 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter; import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent; import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.JButton; import javax.swing.JButton;
import javax.swing.JDialog; import javax.swing.JDialog;
@ -36,7 +38,7 @@ import org.openide.util.NbBundle.Messages;
/** /**
* Dialog used for editing or adding file types. * Dialog used for editing or adding file types.
*/ */
public class AddFileTypeDialog extends JDialog { class AddFileTypeDialog extends JDialog {
/** /**
* Enum used for letting creator of this dialog know whether or not OK was * Enum used for letting creator of this dialog know whether or not OK was
@ -51,6 +53,8 @@ public class AddFileTypeDialog extends JDialog {
private FileType fileType; private FileType fileType;
private AddFileTypePanel addMimeTypePanel; private AddFileTypePanel addMimeTypePanel;
private BUTTON_PRESSED result; private BUTTON_PRESSED result;
private JButton okButton;
private JButton closeButton;
/** /**
* Creates a dialog for creating a file type * Creates a dialog for creating a file type
@ -100,13 +104,12 @@ public class AddFileTypeDialog extends JDialog {
add(this.addMimeTypePanel, BorderLayout.PAGE_START); add(this.addMimeTypePanel, BorderLayout.PAGE_START);
// Add the add/done button. // Add the add/done button.
JButton addButton;
if (add) { if (add) {
addButton = new JButton(Bundle.AddMimeTypeDialog_addButton_title()); okButton = new JButton(Bundle.AddMimeTypeDialog_addButton_title());
} else { } else {
addButton = new JButton(Bundle.AddMimeTypeDialog_addButton_title2()); okButton = new JButton(Bundle.AddMimeTypeDialog_addButton_title2());
} }
addButton.addActionListener(new ActionListener() { okButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
doButtonAction(true); doButtonAction(true);
@ -114,7 +117,7 @@ public class AddFileTypeDialog extends JDialog {
}); });
// Add a close button. // Add a close button.
JButton closeButton = new JButton(Bundle.AddMimeTypeDialog_cancelButton_title()); closeButton = new JButton(Bundle.AddMimeTypeDialog_cancelButton_title());
closeButton.addActionListener(new ActionListener() { closeButton.addActionListener(new ActionListener() {
@Override @Override
public void actionPerformed(ActionEvent e) { public void actionPerformed(ActionEvent e) {
@ -126,7 +129,7 @@ public class AddFileTypeDialog extends JDialog {
JPanel buttonPanel = new JPanel(); JPanel buttonPanel = new JPanel();
buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS)); buttonPanel.setLayout(new BoxLayout(buttonPanel, BoxLayout.LINE_AXIS));
buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10))); buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
buttonPanel.add(addButton); buttonPanel.add(okButton);
buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10))); buttonPanel.add(new javax.swing.Box.Filler(new Dimension(10, 10), new Dimension(10, 10), new Dimension(10, 10)));
buttonPanel.add(closeButton); buttonPanel.add(closeButton);
add(buttonPanel, BorderLayout.LINE_START); add(buttonPanel, BorderLayout.LINE_START);
@ -141,13 +144,22 @@ public class AddFileTypeDialog extends JDialog {
doButtonAction(false); doButtonAction(false);
} }
}); });
this.addMimeTypePanel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(AddFileTypePanel.EVENT.SIG_LIST_CHANGED.toString())) {
enableOkButton();
}
}
});
enableOkButton();
/** /**
* Show the dialog. * Show the dialog.
*/ */
pack(); pack();
setResizable(false); setResizable(false);
setVisible(true); setVisible(true);
} }
/** /**
@ -189,4 +201,8 @@ public class AddFileTypeDialog extends JDialog {
return result; return result;
} }
private void enableOkButton() {
this.okButton.setEnabled(addMimeTypePanel.hasSignature());
}
} }

View File

@ -18,6 +18,8 @@
*/ */
package org.sleuthkit.autopsy.modules.filetypeid; package org.sleuthkit.autopsy.modules.filetypeid;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import javax.swing.DefaultListModel; import javax.swing.DefaultListModel;
@ -26,18 +28,20 @@ import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.openide.util.NbBundle.Messages; import org.openide.util.NbBundle.Messages;
import static org.sleuthkit.autopsy.modules.filetypeid.AddFileTypePanel.EVENT.SIG_LIST_CHANGED;
import org.sleuthkit.autopsy.modules.filetypeid.AddFileTypeSignatureDialog.BUTTON_PRESSED; import org.sleuthkit.autopsy.modules.filetypeid.AddFileTypeSignatureDialog.BUTTON_PRESSED;
import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature; import org.sleuthkit.autopsy.modules.filetypeid.FileType.Signature;
/** /**
* Panel for adding or editing file types. * Panel for adding or editing file types.
*/ */
public class AddFileTypePanel extends javax.swing.JPanel { class AddFileTypePanel extends javax.swing.JPanel {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private AddFileTypeSignatureDialog addSigDialog; private AddFileTypeSignatureDialog addSigDialog;
private DefaultListModel<FileType.Signature> signaturesListModel; private DefaultListModel<FileType.Signature> signaturesListModel;
private PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates a panel for a new file type. * Creates a panel for a new file type.
@ -49,6 +53,10 @@ public class AddFileTypePanel extends javax.swing.JPanel {
this.addTypeListSelectionListener(); this.addTypeListSelectionListener();
this.enableButtons(); this.enableButtons();
} }
enum EVENT {
SIG_LIST_CHANGED
}
/** /**
* Creates a panel for editing a file type. * Creates a panel for editing a file type.
@ -136,6 +144,20 @@ public class AddFileTypePanel extends javax.swing.JPanel {
deleteSigButton.setEnabled(true); deleteSigButton.setEnabled(true);
} }
} }
boolean hasSignature() {
return !this.signaturesListModel.isEmpty();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
@ -257,6 +279,7 @@ public class AddFileTypePanel extends javax.swing.JPanel {
if (!this.signaturesListModel.isEmpty()) { if (!this.signaturesListModel.isEmpty()) {
signatureList.setSelectedIndex(0); signatureList.setSelectedIndex(0);
} }
pcs.firePropertyChange(SIG_LIST_CHANGED.toString(), null, null);
} }
}//GEN-LAST:event_deleteSigButtonActionPerformed }//GEN-LAST:event_deleteSigButtonActionPerformed
@ -266,6 +289,7 @@ public class AddFileTypePanel extends javax.swing.JPanel {
if (addSigDialog.getResult() == AddFileTypeSignatureDialog.BUTTON_PRESSED.OK) { if (addSigDialog.getResult() == AddFileTypeSignatureDialog.BUTTON_PRESSED.OK) {
signaturesListModel.addElement(this.addSigDialog.getSignature()); signaturesListModel.addElement(this.addSigDialog.getSignature());
} }
pcs.firePropertyChange(SIG_LIST_CHANGED.toString(), null, null);
} }
}//GEN-LAST:event_addSigButtonActionPerformed }//GEN-LAST:event_addSigButtonActionPerformed

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.modules.filetypeid;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import javax.swing.DefaultComboBoxModel; import javax.swing.DefaultComboBoxModel;
@ -28,6 +29,7 @@ import javax.swing.DefaultListModel;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
@ -67,6 +69,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
// modules obtained and shared a per data source ingest job snapshot of the // modules obtained and shared a per data source ingest job snapshot of the
// file type definitions. // file type definitions.
IngestJobEventPropertyChangeListener ingestJobEventsListener; IngestJobEventPropertyChangeListener ingestJobEventsListener;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates a panel to allow a user to make custom file type definitions. * Creates a panel to allow a user to make custom file type definitions.
@ -92,6 +95,16 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
populateTypeDetailsComponents(); populateTypeDetailsComponents();
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
/** /**
* Sets the list model for the list of file types. * Sets the list model for the list of file types.
*/ */
@ -451,6 +464,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
if (result == AddFileTypeDialog.BUTTON_PRESSED.OK) { if (result == AddFileTypeDialog.BUTTON_PRESSED.OK) {
fileTypes.add(dialog.getFileType()); fileTypes.add(dialog.getFileType());
updateFileTypesListModel(); updateFileTypesListModel();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
}//GEN-LAST:event_newTypeButtonActionPerformed }//GEN-LAST:event_newTypeButtonActionPerformed
@ -462,6 +476,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
if (!typesListModel.isEmpty()) { if (!typesListModel.isEmpty()) {
typesList.setSelectedIndex(0); typesList.setSelectedIndex(0);
} }
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_deleteTypeButtonActionPerformed }//GEN-LAST:event_deleteTypeButtonActionPerformed
private void editTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editTypeButtonActionPerformed private void editTypeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editTypeButtonActionPerformed
@ -472,6 +487,7 @@ final class FileTypeIdGlobalSettingsPanel extends IngestModuleGlobalSettingsPane
this.fileTypes.remove(selected); this.fileTypes.remove(selected);
this.fileTypes.add(selected, dialog.getFileType()); this.fileTypes.add(selected, dialog.getFileType());
updateFileTypesListModel(); updateFileTypesListModel();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
}//GEN-LAST:event_editTypeButtonActionPerformed }//GEN-LAST:event_editTypeButtonActionPerformed

View File

@ -5,6 +5,7 @@
*/ */
package org.sleuthkit.autopsy.modules.filetypeid; package org.sleuthkit.autopsy.modules.filetypeid;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -19,26 +20,41 @@ import org.openide.util.Lookup;
keywordsCategory = "FileTypeId", keywordsCategory = "FileTypeId",
position = 6 position = 6
) )
// moved messages to Bundle.properties
//@org.openide.util.NbBundle.Messages({"OptionsCategory_Name_FileTypeId=FileTypeId", "OptionsCategory_Keywords_FileTypeId=FileTypeId"})
public final class FileTypeIdOptionsPanelController extends OptionsPanelController { public final class FileTypeIdOptionsPanelController extends OptionsPanelController {
private FileTypeIdGlobalSettingsPanel panel; private FileTypeIdGlobalSettingsPanel panel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed; private boolean changed;
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
getPanel().store(); if (changed) {
changed = false; getPanel().store();
changed = false;
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
} }
@ -48,6 +64,12 @@ public final class FileTypeIdOptionsPanelController extends OptionsPanelControll
return true; return true;
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -76,6 +98,14 @@ public final class FileTypeIdOptionsPanelController extends OptionsPanelControll
private FileTypeIdGlobalSettingsPanel getPanel() { private FileTypeIdGlobalSettingsPanel getPanel() {
if (panel == null) { if (panel == null) {
panel = new FileTypeIdGlobalSettingsPanel(); panel = new FileTypeIdGlobalSettingsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
}
});
} }
return panel; return panel;
} }

View File

@ -24,9 +24,6 @@ import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration( @OptionsPanelController.TopLevelRegistration(
@ -45,18 +42,33 @@ public final class HashDatabaseOptionsPanelController extends OptionsPanelContro
private boolean changed; private boolean changed;
private static final Logger logger = Logger.getLogger(HashDatabaseOptionsPanelController.class.getName()); private static final Logger logger = Logger.getLogger(HashDatabaseOptionsPanelController.class.getName());
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
getPanel().store(); if (changed) {
changed = false; getPanel().store();
changed = false;
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
getPanel().cancel(); getPanel().cancel();
@ -67,6 +79,12 @@ public final class HashDatabaseOptionsPanelController extends OptionsPanelContro
return getPanel().valid(); return getPanel().valid();
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -103,25 +121,9 @@ public final class HashDatabaseOptionsPanelController extends OptionsPanelContro
if (!changed) { if (!changed) {
changed = true; changed = true;
try { pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "HashDatabaseOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "HashDatabaseOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "HashDatabaseOptionsPanelController.moduleErrMsg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
try { pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "HashDatabaseOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "HashDatabaseOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "HashDatabaseOptionsPanelController.moduleErrMsg"),
MessageNotifyUtil.MessageType.ERROR);
}
} }
} }

View File

@ -729,7 +729,7 @@ public class HashDbManager implements PropertyChangeListener {
return SleuthkitJNI.hashDatabaseHasLookupIndex(handle); return SleuthkitJNI.hashDatabaseHasLookupIndex(handle);
} }
boolean hasIndexOnly() throws TskCoreException { public boolean hasIndexOnly() throws TskCoreException {
return SleuthkitJNI.hashDatabaseIsIndexOnly(handle); return SleuthkitJNI.hashDatabaseIsIndexOnly(handle);
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.modules.interestingitems; package org.sleuthkit.autopsy.modules.interestingitems;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import javax.swing.JComponent; import javax.swing.JComponent;
@ -39,23 +40,38 @@ public final class InterestingItemDefsOptionsPanelController extends OptionsPane
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this); private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
private boolean changed; private boolean changed;
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
SwingUtilities.invokeLater(new Runnable() { if (changed) {
@Override SwingUtilities.invokeLater(new Runnable() {
public void run() { @Override
getPanel().store(); public void run() {
changed = false; getPanel().store();
} changed = false;
}); }
});
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
// need not do anything special, if no changes have been persisted yet // need not do anything special, if no changes have been persisted yet
@ -66,6 +82,12 @@ public final class InterestingItemDefsOptionsPanelController extends OptionsPane
return true; return true;
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -94,6 +116,14 @@ public final class InterestingItemDefsOptionsPanelController extends OptionsPane
private InterestingItemDefsPanel getPanel() { private InterestingItemDefsPanel getPanel() {
if (panel == null) { if (panel == null) {
panel = new InterestingItemDefsPanel(); panel = new InterestingItemDefsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
}
});
} }
return panel; return panel;
} }

View File

@ -124,7 +124,7 @@
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="equalitySignComboBox" min="-2" pref="38" max="-2" attributes="0"/> <Component id="equalitySignComboBox" min="-2" pref="38" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="jSpinner1" min="-2" pref="104" max="-2" attributes="0"/> <Component id="fileSizeSpinner" min="-2" pref="104" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="fileSizeUnitComboBox" min="-2" pref="83" max="-2" attributes="0"/> <Component id="fileSizeUnitComboBox" min="-2" pref="83" max="-2" attributes="0"/>
</Group> </Group>
@ -259,7 +259,7 @@
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel8" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="jLabel8" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="equalitySignComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="equalitySignComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jSpinner1" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="fileSizeSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="fileSizeUnitComboBox" alignment="3" min="-2" max="-2" attributes="0"/> <Component id="fileSizeUnitComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -802,7 +802,7 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/> <AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues> </AuxValues>
</Component> </Component>
<Component class="javax.swing.JSpinner" name="jSpinner1"> <Component class="javax.swing.JSpinner" name="fileSizeSpinner">
<Properties> <Properties>
<Property name="enabled" type="boolean" value="false"/> <Property name="enabled" type="boolean" value="false"/>
</Properties> </Properties>

View File

@ -19,7 +19,8 @@
package org.sleuthkit.autopsy.modules.interestingitems; package org.sleuthkit.autopsy.modules.interestingitems;
import java.awt.EventQueue; import java.awt.EventQueue;
import java.awt.Font; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
@ -35,15 +36,14 @@ import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import org.apache.tika.mime.MediaType; import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypes;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil; import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector; import org.sleuthkit.autopsy.modules.filetypeid.FileTypeDetector;
import org.apache.tika.mime.MediaType;
import org.apache.tika.mime.MimeTypes;
import org.openide.util.Exceptions;
import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* A panel that allows a user to make interesting item definitions. * A panel that allows a user to make interesting item definitions.
@ -74,6 +74,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
// definitions manager. Note that it is a tree map to aid in displaying // definitions manager. Note that it is a tree map to aid in displaying
// files sets in sorted order by name. // files sets in sorted order by name.
private TreeMap<String, FilesSet> filesSets; private TreeMap<String, FilesSet> filesSets;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Constructs an interesting item definitions panel. * Constructs an interesting item definitions panel.
@ -121,6 +122,16 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.fileSizeUnitComboBox.setSelectedIndex(1); this.fileSizeUnitComboBox.setSelectedIndex(1);
this.equalitySignComboBox.setSelectedIndex(2); this.equalitySignComboBox.setSelectedIndex(2);
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
/** /**
* @inheritDoc * @inheritDoc
@ -197,6 +208,10 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
this.filesRadioButton.setSelected(true); this.filesRadioButton.setSelected(true);
this.rulePathConditionTextField.setText(""); this.rulePathConditionTextField.setText("");
this.rulePathConditionRegexCheckBox.setSelected(false); this.rulePathConditionRegexCheckBox.setSelected(false);
this.mimeTypeComboBox.setSelectedIndex(0);
this.equalitySignComboBox.setSelectedIndex(2);
this.fileSizeUnitComboBox.setSelectedIndex(1);
this.fileSizeSpinner.setValue(0);
this.newRuleButton.setEnabled(!this.setsListModel.isEmpty()); this.newRuleButton.setEnabled(!this.setsListModel.isEmpty());
this.editRuleButton.setEnabled(false); this.editRuleButton.setEnabled(false);
this.deleteRuleButton.setEnabled(false); this.deleteRuleButton.setEnabled(false);
@ -306,11 +321,11 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
if (fileSizeCondition != null) { if (fileSizeCondition != null) {
InterestingItemDefsPanel.this.fileSizeUnitComboBox.setSelectedItem(fileSizeCondition.getUnit().getName()); InterestingItemDefsPanel.this.fileSizeUnitComboBox.setSelectedItem(fileSizeCondition.getUnit().getName());
InterestingItemDefsPanel.this.equalitySignComboBox.setSelectedItem(fileSizeCondition.getComparator().getSymbol()); InterestingItemDefsPanel.this.equalitySignComboBox.setSelectedItem(fileSizeCondition.getComparator().getSymbol());
InterestingItemDefsPanel.this.jSpinner1.setValue(fileSizeCondition.getSizeValue()); InterestingItemDefsPanel.this.fileSizeSpinner.setValue(fileSizeCondition.getSizeValue());
} else { } else {
InterestingItemDefsPanel.this.fileSizeUnitComboBox.setSelectedIndex(1); InterestingItemDefsPanel.this.fileSizeUnitComboBox.setSelectedIndex(1);
InterestingItemDefsPanel.this.equalitySignComboBox.setSelectedIndex(2); InterestingItemDefsPanel.this.equalitySignComboBox.setSelectedIndex(2);
InterestingItemDefsPanel.this.jSpinner1.setValue(0); InterestingItemDefsPanel.this.fileSizeSpinner.setValue(0);
} }
// Enable the new, edit and delete rule buttons. // Enable the new, edit and delete rule buttons.
@ -482,12 +497,12 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
editRuleButton = new javax.swing.JButton(); editRuleButton = new javax.swing.JButton();
rulesListLabel = new javax.swing.JLabel(); rulesListLabel = new javax.swing.JLabel();
rulesListScrollPane = new javax.swing.JScrollPane(); rulesListScrollPane = new javax.swing.JScrollPane();
rulesList = new javax.swing.JList<FilesSet.Rule>(); rulesList = new javax.swing.JList<>();
setDescScrollPanel = new javax.swing.JScrollPane(); setDescScrollPanel = new javax.swing.JScrollPane();
setDescriptionTextArea = new javax.swing.JTextArea(); setDescriptionTextArea = new javax.swing.JTextArea();
editSetButton = new javax.swing.JButton(); editSetButton = new javax.swing.JButton();
setsListScrollPane = new javax.swing.JScrollPane(); setsListScrollPane = new javax.swing.JScrollPane();
setsList = new javax.swing.JList<FilesSet>(); setsList = new javax.swing.JList<>();
fileNameExtensionRadioButton = new javax.swing.JRadioButton(); fileNameExtensionRadioButton = new javax.swing.JRadioButton();
jLabel3 = new javax.swing.JLabel(); jLabel3 = new javax.swing.JLabel();
fileNameTextField = new javax.swing.JTextField(); fileNameTextField = new javax.swing.JTextField();
@ -510,10 +525,10 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
jScrollPane2 = new javax.swing.JScrollPane(); jScrollPane2 = new javax.swing.JScrollPane();
jTextArea1 = new javax.swing.JTextArea(); jTextArea1 = new javax.swing.JTextArea();
jLabel7 = new javax.swing.JLabel(); jLabel7 = new javax.swing.JLabel();
mimeTypeComboBox = new javax.swing.JComboBox<String>(); mimeTypeComboBox = new javax.swing.JComboBox<>();
jLabel8 = new javax.swing.JLabel(); jLabel8 = new javax.swing.JLabel();
equalitySignComboBox = new javax.swing.JComboBox<String>(); equalitySignComboBox = new javax.swing.JComboBox<String>();
jSpinner1 = new javax.swing.JSpinner(); fileSizeSpinner = new javax.swing.JSpinner();
fileSizeUnitComboBox = new javax.swing.JComboBox<String>(); fileSizeUnitComboBox = new javax.swing.JComboBox<String>();
setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11)); setFont(getFont().deriveFont(getFont().getStyle() & ~java.awt.Font.BOLD, 11));
@ -712,7 +727,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
equalitySignComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "=", ">", "", "<", "" })); equalitySignComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { "=", ">", "", "<", "" }));
equalitySignComboBox.setEnabled(false); equalitySignComboBox.setEnabled(false);
jSpinner1.setEnabled(false); fileSizeSpinner.setEnabled(false);
fileSizeUnitComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { Bundle.InterestingItemDefsPanel_bytes(), Bundle.InterestingItemDefsPanel_kiloBytes(), Bundle.InterestingItemDefsPanel_megaBytes(), Bundle.InterestingItemDefsPanel_gigaBytes() })); fileSizeUnitComboBox.setModel(new javax.swing.DefaultComboBoxModel<String>(new String[] { Bundle.InterestingItemDefsPanel_bytes(), Bundle.InterestingItemDefsPanel_kiloBytes(), Bundle.InterestingItemDefsPanel_megaBytes(), Bundle.InterestingItemDefsPanel_gigaBytes() }));
fileSizeUnitComboBox.setEnabled(false); fileSizeUnitComboBox.setEnabled(false);
@ -769,7 +784,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
.addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup() .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createSequentialGroup()
.addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 38, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(fileSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 104, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE)) .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 83, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(mimeTypeComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 245, Short.MAX_VALUE) .addComponent(mimeTypeComboBox, javax.swing.GroupLayout.Alignment.LEADING, 0, 245, Short.MAX_VALUE)
@ -878,7 +893,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel8) .addComponent(jLabel8)
.addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(equalitySignComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jSpinner1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(fileSizeSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))) .addComponent(fileSizeUnitComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap()) .addContainerGap())
); );
@ -907,6 +922,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
private void newSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSetButtonActionPerformed private void newSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newSetButtonActionPerformed
this.doFileSetsDialog(null); this.doFileSetsDialog(null);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_newSetButtonActionPerformed }//GEN-LAST:event_newSetButtonActionPerformed
private void deleteRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteRuleButtonActionPerformed private void deleteRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteRuleButtonActionPerformed
@ -919,6 +935,12 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
FilesSet.Rule selectedRule = this.rulesList.getSelectedValue(); FilesSet.Rule selectedRule = this.rulesList.getSelectedValue();
rules.remove(selectedRule.getUuid()); rules.remove(selectedRule.getUuid());
this.replaceFilesSet(oldSet, oldSet.getName(), oldSet.getDescription(), oldSet.ignoresKnownFiles(), rules); this.replaceFilesSet(oldSet, oldSet.getName(), oldSet.getDescription(), oldSet.ignoresKnownFiles(), rules);
if (!this.rulesListModel.isEmpty()) {
this.rulesList.setSelectedIndex(0);
} else {
this.resetRuleComponents();
}
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_deleteRuleButtonActionPerformed }//GEN-LAST:event_deleteRuleButtonActionPerformed
private void deleteSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteSetButtonActionPerformed private void deleteSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteSetButtonActionPerformed
@ -933,6 +955,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
} else { } else {
this.resetComponents(); this.resetComponents();
} }
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_deleteSetButtonActionPerformed }//GEN-LAST:event_deleteSetButtonActionPerformed
private void ignoreKnownFilesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ignoreKnownFilesCheckboxActionPerformed private void ignoreKnownFilesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ignoreKnownFilesCheckboxActionPerformed
@ -941,14 +964,17 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
private void editSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editSetButtonActionPerformed private void editSetButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editSetButtonActionPerformed
this.doFileSetsDialog(this.setsList.getSelectedValue()); this.doFileSetsDialog(this.setsList.getSelectedValue());
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_editSetButtonActionPerformed }//GEN-LAST:event_editSetButtonActionPerformed
private void editRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editRuleButtonActionPerformed private void editRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editRuleButtonActionPerformed
this.doFilesSetRuleDialog(this.rulesList.getSelectedValue()); this.doFilesSetRuleDialog(this.rulesList.getSelectedValue());
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_editRuleButtonActionPerformed }//GEN-LAST:event_editRuleButtonActionPerformed
private void newRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newRuleButtonActionPerformed private void newRuleButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newRuleButtonActionPerformed
this.doFilesSetRuleDialog(null); this.doFilesSetRuleDialog(null);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_newRuleButtonActionPerformed }//GEN-LAST:event_newRuleButtonActionPerformed
private void fileNameTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileNameTextFieldActionPerformed private void fileNameTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fileNameTextFieldActionPerformed
@ -968,6 +994,7 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
private javax.swing.JRadioButton fileNameRadioButton; private javax.swing.JRadioButton fileNameRadioButton;
private javax.swing.JCheckBox fileNameRegexCheckbox; private javax.swing.JCheckBox fileNameRegexCheckbox;
private javax.swing.JTextField fileNameTextField; private javax.swing.JTextField fileNameTextField;
private javax.swing.JSpinner fileSizeSpinner;
private javax.swing.JComboBox<String> fileSizeUnitComboBox; private javax.swing.JComboBox<String> fileSizeUnitComboBox;
private javax.swing.JRadioButton filesRadioButton; private javax.swing.JRadioButton filesRadioButton;
private javax.swing.JCheckBox ignoreKnownFilesCheckbox; private javax.swing.JCheckBox ignoreKnownFilesCheckbox;
@ -982,7 +1009,6 @@ final class InterestingItemDefsPanel extends IngestModuleGlobalSettingsPanel imp
private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1; private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2; private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JSpinner jSpinner1;
private javax.swing.JTextArea jTextArea1; private javax.swing.JTextArea jTextArea1;
private javax.swing.JComboBox<String> mimeTypeComboBox; private javax.swing.JComboBox<String> mimeTypeComboBox;
private javax.swing.JButton newRuleButton; private javax.swing.JButton newRuleButton;

View File

@ -87,9 +87,6 @@
<ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.taggedResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/report/Bundle.properties" key="ReportVisualPanel2.taggedResultsRadioButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="taggedResultsRadioButtonStateChanged"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="allResultsRadioButton"> <Component class="javax.swing.JRadioButton" name="allResultsRadioButton">
<Properties> <Properties>

View File

@ -22,7 +22,6 @@ import java.awt.Component;
import java.awt.event.MouseAdapter; import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent; import java.awt.event.MouseEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.EnumMap;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
@ -36,12 +35,13 @@ import javax.swing.JList;
import javax.swing.JPanel; import javax.swing.JPanel;
import javax.swing.ListCellRenderer; import javax.swing.ListCellRenderer;
import javax.swing.ListModel; import javax.swing.ListModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.ListDataListener; import javax.swing.event.ListDataListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.BlackboardArtifact;
import org.sleuthkit.datamodel.BlackboardArtifact.ARTIFACT_TYPE;
import org.sleuthkit.datamodel.TagName; import org.sleuthkit.datamodel.TagName;
import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskCoreException;
@ -68,6 +68,26 @@ final class ReportVisualPanel2 extends JPanel {
deselectAllButton.setEnabled(false); deselectAllButton.setEnabled(false);
allResultsRadioButton.setSelected(true); allResultsRadioButton.setSelected(true);
this.wizPanel = wizPanel; this.wizPanel = wizPanel;
this.allResultsRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
tagsList.setEnabled(taggedResultsRadioButton.isSelected());
selectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
deselectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
advancedButton.setEnabled(!taggedResultsRadioButton.isSelected());
updateFinishButton();
}
});
this.taggedResultsRadioButton.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
tagsList.setEnabled(taggedResultsRadioButton.isSelected());
selectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
deselectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
advancedButton.setEnabled(!taggedResultsRadioButton.isSelected());
updateFinishButton();
}
});
} }
// Initialize the list of Tags // Initialize the list of Tags
@ -164,21 +184,11 @@ final class ReportVisualPanel2 extends JPanel {
return result; return result;
} }
private boolean areArtifactsSelected() {
boolean result = false;
for (Entry<BlackboardArtifact.Type, Boolean> entry : artifactStates.entrySet()) {
if (entry.getValue()) {
result = true;
}
}
return result;
}
private void updateFinishButton() { private void updateFinishButton() {
if (taggedResultsRadioButton.isSelected()) { if (taggedResultsRadioButton.isSelected()) {
wizPanel.setFinish(areTagsSelected()); wizPanel.setFinish(areTagsSelected());
} else { } else {
wizPanel.setFinish(areArtifactsSelected()); wizPanel.setFinish(true);
} }
} }
@ -211,11 +221,6 @@ final class ReportVisualPanel2 extends JPanel {
optionsButtonGroup.add(taggedResultsRadioButton); optionsButtonGroup.add(taggedResultsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(taggedResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.taggedResultsRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(taggedResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.taggedResultsRadioButton.text")); // NOI18N
taggedResultsRadioButton.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
taggedResultsRadioButtonStateChanged(evt);
}
});
optionsButtonGroup.add(allResultsRadioButton); optionsButtonGroup.add(allResultsRadioButton);
org.openide.awt.Mnemonics.setLocalizedText(allResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.allResultsRadioButton.text")); // NOI18N org.openide.awt.Mnemonics.setLocalizedText(allResultsRadioButton, org.openide.util.NbBundle.getMessage(ReportVisualPanel2.class, "ReportVisualPanel2.allResultsRadioButton.text")); // NOI18N
@ -293,14 +298,6 @@ final class ReportVisualPanel2 extends JPanel {
); );
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void taggedResultsRadioButtonStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_taggedResultsRadioButtonStateChanged
tagsList.setEnabled(taggedResultsRadioButton.isSelected());
selectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
deselectAllButton.setEnabled(taggedResultsRadioButton.isSelected());
advancedButton.setEnabled(!taggedResultsRadioButton.isSelected());
updateFinishButton();
}//GEN-LAST:event_taggedResultsRadioButtonStateChanged
private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed private void selectAllButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllButtonActionPerformed
for (String tag : tags) { for (String tag : tags) {
tagStates.put(tag, Boolean.TRUE); tagStates.put(tag, Boolean.TRUE);
@ -319,8 +316,8 @@ final class ReportVisualPanel2 extends JPanel {
private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed private void advancedButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_advancedButtonActionPerformed
artifactStates = dialog.display(); artifactStates = dialog.display();
wizPanel.setFinish(areArtifactsSelected());
}//GEN-LAST:event_advancedButtonActionPerformed }//GEN-LAST:event_advancedButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton advancedButton; private javax.swing.JButton advancedButton;
private javax.swing.JRadioButton allResultsRadioButton; private javax.swing.JRadioButton allResultsRadioButton;

Binary file not shown.

After

Width:  |  Height:  |  Size: 924 B

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-15 Basis Technology Corp. * Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,6 +19,7 @@
package org.sleuthkit.autopsy.timeline.ui; package org.sleuthkit.autopsy.timeline.ui;
import com.google.common.eventbus.Subscribe; import com.google.common.eventbus.Subscribe;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
@ -30,28 +31,33 @@ import javafx.application.Platform;
import javafx.beans.InvalidationListener; import javafx.beans.InvalidationListener;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding; import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener; import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList; import javafx.collections.ObservableList;
import javafx.concurrent.Service; import javafx.collections.transformation.SortedList;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.geometry.Pos; import javafx.geometry.Pos;
import javafx.scene.Cursor; import javafx.scene.Cursor;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.chart.Axis; import javafx.scene.chart.Axis;
import javafx.scene.chart.Chart;
import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.OverrunStyle; import javafx.scene.control.OverrunStyle;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.effect.Effect; import javafx.scene.layout.Border;
import javafx.scene.layout.BorderPane; import javafx.scene.layout.BorderPane;
import javafx.scene.layout.BorderStroke;
import javafx.scene.layout.BorderStrokeStyle;
import javafx.scene.layout.BorderWidths;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane; import javafx.scene.layout.Pane;
import javafx.scene.layout.Region; import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane; import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox; import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font; import javafx.scene.text.Font;
import javafx.scene.text.FontWeight; import javafx.scene.text.FontWeight;
import javafx.scene.text.Text; import javafx.scene.text.Text;
@ -59,7 +65,6 @@ import javafx.scene.text.TextAlignment;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.controlsfx.control.MaskerPane; import org.controlsfx.control.MaskerPane;
import org.joda.time.Interval;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.LoggedTask; import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -70,153 +75,258 @@ import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent; import org.sleuthkit.autopsy.timeline.events.RefreshRequestedEvent;
/** /**
* Abstract base class for {@link Chart} based {@link TimeLineView}s used in the * Abstract base class for TimeLineChart based visualizations.
* main visualization area.
* *
* @param <X> the type of data plotted along the x axis * @param <X> The type of data plotted along the x axis
* @param <Y> the type of data plotted along the y axis * @param <Y> The type of data plotted along the y axis
* @param <NodeType> the type of nodes used to represent data items * @param <NodeType> The type of nodes used to represent data items
* @param <ChartType> the type of the {@link XYChart<X,Y>} this class uses to * @param <ChartType> The type of the TimeLineChart<X> this class uses to plot
* plot the data. * the data. Must extend Region.
* *
* TODO: this is becoming (too?) closely tied to the notion that their is a * TODO: this is becoming (too?) closely tied to the notion that their is a
* {@link XYChart} doing the rendering. Is this a good idea? -jm TODO: pull up * XYChart doing the rendering. Is this a good idea? -jm
* common history context menu items out of derived classes? -jm *
* TODO: pull up common history context menu items out of derived classes? -jm
*/ */
public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, ChartType extends Region & TimeLineChart<X>> extends BorderPane { public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, ChartType extends Region & TimeLineChart<X>> extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(AbstractVisualizationPane.class.getName());
@NbBundle.Messages("AbstractVisualization.Default_Tooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.") @NbBundle.Messages("AbstractVisualization.Default_Tooltip.text=Drag the mouse to select a time interval to zoom into.\nRight-click for more actions.")
private static final Tooltip DEFAULT_TOOLTIP = new Tooltip(Bundle.AbstractVisualization_Default_Tooltip_text()); private static final Tooltip DEFAULT_TOOLTIP = new Tooltip(Bundle.AbstractVisualization_Default_Tooltip_text());
private static final Logger LOGGER = Logger.getLogger(AbstractVisualizationPane.class.getName()); private static final Border ONLY_LEFT_BORDER = new Border(new BorderStroke(Color.BLACK, BorderStrokeStyle.SOLID, CornerRadii.EMPTY, new BorderWidths(0, 0, 0, 1)));
/**
* Get the tool tip to use for this visualization when no more specific
* tooltip is needed.
*
* @return The default tooltip.
*/
public static Tooltip getDefaultTooltip() { public static Tooltip getDefaultTooltip() {
return DEFAULT_TOOLTIP; return DEFAULT_TOOLTIP;
} }
protected final SimpleBooleanProperty hasEvents = new SimpleBooleanProperty(true);
/** private final ReadOnlyBooleanWrapper hasVisibleEvents = new ReadOnlyBooleanWrapper(true);
/*
* access to chart data via series * access to chart data via series
*/ */
protected final ObservableList<XYChart.Series<X, Y>> dataSeries = FXCollections.<XYChart.Series<X, Y>>observableArrayList(); protected final ObservableList<XYChart.Series<X, Y>> dataSeries = FXCollections.<XYChart.Series<X, Y>>observableArrayList();
protected final Map<EventType, XYChart.Series<X, Y>> eventTypeToSeriesMap = new HashMap<>(); protected final Map<EventType, XYChart.Series<X, Y>> eventTypeToSeriesMap = new HashMap<>();
protected ChartType chart; private ChartType chart;
//// replacement axis label componenets //// replacement axis label componenets
private final Pane leafPane = new Pane(); // container for the leaf lables in the declutterd axis private final Pane specificLabelPane = new Pane(); // container for the specfic labels in the decluttered axis
private final Pane branchPane = new Pane();// container for the branch lables in the declutterd axis private final Pane contextLabelPane = new Pane();// container for the contextual labels in the decluttered axis
protected final Region spacer = new Region(); private final Region spacer = new Region();
/** /**
* task used to reload the content of this visualization * task used to reload the content of this visualization
*/ */
private Task<Boolean> updateTask; private Task<Boolean> updateTask;
final protected TimeLineController controller; final private TimeLineController controller;
final private FilteredEventsModel filteredEvents;
final protected FilteredEventsModel filteredEvents;
final private ObservableList<NodeType> selectedNodes = FXCollections.observableArrayList();
final protected ObservableList<NodeType> selectedNodes = FXCollections.observableArrayList();
private InvalidationListener updateListener = any -> update();
private InvalidationListener invalidationListener = (Observable observable) -> {
update(); /**
}; * The visualization nodes that are selected.
*
public ObservableList<NodeType> getSelectedNodes() { * @return An ObservableList<NodeType> of the nodes that are selected in
* this visualization.
*/
protected ObservableList<NodeType> getSelectedNodes() {
return selectedNodes; return selectedNodes;
} }
/** /**
* list of {@link Node}s to insert into the toolbar. This should be set in * List of Nodes to insert into the toolbar. This should be set in an
* an implementations constructor. * implementations constructor.
*/ */
protected List<Node> settingsNodes; private List<Node> settingsNodes;
public TimeLineController getController() {
return controller;
}
/** /**
* @return the list of nodes containing settings widgets to insert into this * Get a List of nodes containing settings widgets to insert into this
* visualization's header * visualization's header.
*
* @return The List of settings Nodes.
*/ */
protected List<Node> getSettingsNodes() { protected List<Node> getSettingsNodes() {
return Collections.unmodifiableList(settingsNodes); return Collections.unmodifiableList(settingsNodes);
} }
/** /**
* @param value a value along this visualization's x axis * Set the List of nodes containing settings widgets to insert into this
* visualization's header.
* *
* @return true if the tick label for the given value should be bold ( has *
* relevant data), false* otherwise * @param settingsNodes The List of nodes containing settings widgets to
* insert into this visualization's header.
*/
protected void setSettingsNodes(List<Node> settingsNodes) {
this.settingsNodes = new ArrayList<>(settingsNodes);
}
/**
* Get the TimelineController for this visualization.
*
* @return The TimelineController for this visualization.
*/
protected TimeLineController getController() {
return controller;
}
/**
* Get the CharType that implements this visualization.
*
* @return The CharType that implements this visualization.
*/
protected ChartType getChart() {
return chart;
}
/**
* Get the FilteredEventsModel for this visualization.
*
* @return The FilteredEventsModel for this visualization.
*/
protected FilteredEventsModel getEventsModel() {
return filteredEvents;
}
/**
* Set the ChartType that implements this visualization.
*
* @param chart The ChartType that implements this visualization.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
protected void setChart(ChartType chart) {
this.chart = chart;
setCenter(chart);
}
/**
* A property that indicates whether there are any events visible in this
* visualization with the current view parameters.
*
* @return A property that indicates whether there are any events visible in
* this visualization with the current view parameters.
*/
ReadOnlyBooleanProperty hasVisibleEventsProperty() {
return hasVisibleEvents.getReadOnlyProperty();
}
/**
* Apply this visualization's 'selection effect' to the given node.
*
* @param node The node to apply the 'effect' to.
*/
protected void applySelectionEffect(NodeType node) {
applySelectionEffect(node, true);
}
/**
* Remove this visualization's 'selection effect' from the given node.
*
* @param node The node to remvoe the 'effect' from.
*/
protected void removeSelectionEffect(NodeType node) {
applySelectionEffect(node, Boolean.FALSE);
}
/**
* Should the tick mark at the given value be bold, because it has
* interesting data associated with it?
*
* @param value A value along this visualization's x axis
*
* @return True if the tick label for the given value should be bold ( has
* relevant data), false otherwise
*/ */
abstract protected Boolean isTickBold(X value); abstract protected Boolean isTickBold(X value);
/** /**
* apply this visualization's 'selection effect' to the given node * Apply this visualization's 'selection effect' to the given node, if
* applied is true. If applied is false, remove the affect
* *
* @param node the node to apply the 'effect' to * @param node The node to apply the 'effect' to
* @param applied true if the effect should be applied, false if the effect * @param applied True if the effect should be applied, false if the effect
* should * should not
*/ */
abstract protected void applySelectionEffect(NodeType node, Boolean applied); abstract protected void applySelectionEffect(NodeType node, Boolean applied);
/** /**
* @return a task to execute on a background thread to reload this * Get a new background Task that fetches the appropriate data and loads it
* into this visualization.
*
* @return A new task to execute on a background thread to reload this
* visualization with different data. * visualization with different data.
*/ */
abstract protected Task<Boolean> getUpdateTask(); abstract protected Task<Boolean> getNewUpdateTask();
/** /**
* @return return the {@link Effect} applied to 'selected nodes' in this * Get the label that should be used for a tick mark at the given value.
* visualization, or null if selection is visualized via another
* mechanism
*/
abstract protected Effect getSelectionEffect();
/**
* @param tickValue
* *
* @return a String to use for a tick mark label given a tick value * @param tickValue The value to get a label for.
*
* @return a String to use for a tick mark label given a tick value.
*/ */
abstract protected String getTickMarkLabel(X tickValue); abstract protected String getTickMarkLabel(X tickValue);
/** /**
* the spacing (in pixels) between tick marks of the horizontal axis. This * Get the spacing, in pixels, between tick marks of the horizontal axis.
* will be used to layout the decluttered replacement labels. * This will be used to layout the decluttered replacement labels.
* *
* @return the spacing in pixels between tick marks of the horizontal axis * @return The spacing, in pixels, between tick marks of the horizontal axis
*/ */
abstract protected double getTickSpacing(); abstract protected double getTickSpacing();
/** /**
* @return the horizontal axis used by this Visualization's chart * Get the X-Axis of this Visualization's chart
*
* @return The horizontal axis used by this Visualization's chart
*/ */
abstract protected Axis<X> getXAxis(); abstract protected Axis<X> getXAxis();
/** /**
* @return the vertical axis used by this Visualization's chart * Get the Y-Axis of this Visualization's chart
*
* @return The vertical axis used by this Visualization's chart
*/ */
abstract protected Axis<Y> getYAxis(); abstract protected Axis<Y> getYAxis();
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
abstract protected void resetData();
/** /**
* update this visualization based on current state of zoom / filters. * Get the total amount of space (in pixels) the x-axis uses to pad the left
* Primarily this invokes the background {@link VisualizationUpdateTask} * and right sides. This value is used to keep decluttered axis aligned
* returned by {@link #getUpdateTask()}, which derived classes must * correctly.
* implement.
* *
* TODO: replace this logic with a {@link Service} ? -jm * @return The x-axis margin (in pixels)
*/ */
final synchronized public void update() { abstract protected double getAxisMargin();
/**
* Clear all data items from this chart.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
abstract protected void clearChartData();
/**
* Update this visualization based on current state of zoom / filters.
* Primarily this invokes the background VisualizationUpdateTask returned by
* getUpdateTask(), which derived classes must implement.
*
* TODO: replace this logic with a javafx Service ? -jm
*/
protected final synchronized void update() {
if (updateTask != null) { if (updateTask != null) {
updateTask.cancel(true); updateTask.cancel(true);
updateTask = null; updateTask = null;
} }
updateTask = getUpdateTask(); updateTask = getNewUpdateTask();
updateTask.stateProperty().addListener((Observable observable) -> { updateTask.stateProperty().addListener((Observable observable) -> {
switch (updateTask.getState()) { switch (updateTask.getState()) {
case CANCELLED: case CANCELLED:
@ -227,26 +337,36 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
break; break;
case SUCCEEDED: case SUCCEEDED:
try { try {
this.hasEvents.set(updateTask.get()); this.hasVisibleEvents.set(updateTask.get());
} catch (InterruptedException | ExecutionException ex) { } catch (InterruptedException | ExecutionException ex) {
LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NOI18N NON-NLS LOGGER.log(Level.SEVERE, "Unexpected exception updating visualization", ex); //NON-NLS
} }
break; break;
} }
}); });
controller.monitorTask(updateTask); controller.monitorTask(updateTask);
} }
final synchronized public void dispose() { /**
* Dispose of this visualization and any resources it holds onto.
*/
final synchronized void dispose() {
//cancel and gc updateTask
if (updateTask != null) { if (updateTask != null) {
updateTask.cancel(true); updateTask.cancel(true);
updateTask = null;
} }
this.filteredEvents.zoomParametersProperty().removeListener(invalidationListener); //remvoe and gc updateListener
invalidationListener = null; this.filteredEvents.zoomParametersProperty().removeListener(updateListener);
TimeLineController.getTimeZone().removeListener(updateListener);
updateListener = null;
filteredEvents.unRegisterForEvents(this);
} }
/** /**
* make a series for each event type in a consistent order * Make a series for each event type in a consistent order.
*/ */
protected final void createSeries() { protected final void createSeries() {
for (EventType eventType : EventType.allTypes) { for (EventType eventType : EventType.allTypes) {
@ -258,134 +378,143 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
} }
/** /**
* Get the series for the given EventType.
* *
* @param et the EventType to get the series for * @param et The EventType to get the series for
* *
* @return a Series object to contain all the events with the given * @return A Series object to contain all the events with the given
* EventType * EventType
*/ */
protected final XYChart.Series<X, Y> getSeries(final EventType et) { protected final XYChart.Series<X, Y> getSeries(final EventType et) {
return eventTypeToSeriesMap.get(et); return eventTypeToSeriesMap.get(et);
} }
/**
* Constructor
*
* @param controller The TimelineController for this visualization.
*/
protected AbstractVisualizationPane(TimeLineController controller) { protected AbstractVisualizationPane(TimeLineController controller) {
this.controller = controller; this.controller = controller;
this.filteredEvents = controller.getEventsModel(); this.filteredEvents = controller.getEventsModel();
this.filteredEvents.registerForEvents(this); this.filteredEvents.registerForEvents(this);
this.filteredEvents.zoomParametersProperty().addListener(invalidationListener); this.filteredEvents.zoomParametersProperty().addListener(updateListener);
Platform.runLater(() -> { Platform.runLater(() -> {
VBox vBox = new VBox(leafPane, branchPane); VBox vBox = new VBox(specificLabelPane, contextLabelPane);
vBox.setFillWidth(false); vBox.setFillWidth(false);
HBox hBox = new HBox(spacer, vBox); HBox hBox = new HBox(spacer, vBox);
hBox.setFillHeight(false); hBox.setFillHeight(false);
setBottom(hBox); setBottom(hBox);
DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin());//getXAxis().startMarginProperty().multiply(2)); DoubleBinding spacerSize = getYAxis().widthProperty().add(getYAxis().tickLengthProperty()).add(getAxisMargin());
spacer.minWidthProperty().bind(spacerSize); spacer.minWidthProperty().bind(spacerSize);
spacer.prefWidthProperty().bind(spacerSize); spacer.prefWidthProperty().bind(spacerSize);
spacer.maxWidthProperty().bind(spacerSize); spacer.maxWidthProperty().bind(spacerSize);
}); });
createSeries(); createSeries();
selectedNodes.addListener((ListChangeListener.Change<? extends NodeType> c) -> { selectedNodes.addListener((ListChangeListener.Change<? extends NodeType> change) -> {
while (c.next()) { while (change.next()) {
c.getRemoved().forEach(n -> applySelectionEffect(n, false)); change.getRemoved().forEach(node -> applySelectionEffect(node, false));
c.getAddedSubList().forEach(n -> applySelectionEffect(n, true)); change.getAddedSubList().forEach(node -> applySelectionEffect(node, true));
} }
}); });
TimeLineController.getTimeZone().addListener(invalidationListener); TimeLineController.getTimeZone().addListener(updateListener);
//show tooltip text in status bar //show tooltip text in status bar
hoverProperty().addListener(observable -> controller.setStatus(isHover() ? DEFAULT_TOOLTIP.getText() : "")); hoverProperty().addListener(hoverProp -> controller.setStatus(isHover() ? DEFAULT_TOOLTIP.getText() : ""));
} }
/**
* Handle a RefreshRequestedEvent from the events model by updating the
* visualization.
*
* @param event The RefreshRequestedEvent to handle.
*/
@Subscribe @Subscribe
public void handleRefreshRequested(RefreshRequestedEvent event) { public void handleRefreshRequested(RefreshRequestedEvent event) {
update(); update();
} }
/** /**
* iterate through the list of tick-marks building a two level structure of * Iterate through the list of tick-marks building a two level structure of
* replacement tick marl labels. (Visually) upper level has most * replacement tick mark labels. (Visually) upper level has most
* detailed/highest frequency part of date/time. Second level has rest of * detailed/highest frequency part of date/time (specific label). Second
* date/time grouped by unchanging part. eg: * level has rest of date/time grouped by unchanging part (contextual
* label).
* *
* eg:
* *
* october-30_october-31_september-01_september-02_september-03 * October-October-31_September-01_September-02_September-03
* *
* becomes * becomes:
* *
* _________30_________31___________01___________02___________03 * _________30_________31___________01___________02___________03
* *
* _________october___________|_____________september___________ * _________October___________|_____________September___________
* *
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
public synchronized void layoutDateLabels() { protected synchronized void layoutDateLabels() {
//clear old labels //clear old labels
branchPane.getChildren().clear(); contextLabelPane.getChildren().clear();
leafPane.getChildren().clear(); specificLabelPane.getChildren().clear();
//since the tickmarks aren't necessarily in value/position order, //since the tickmarks aren't necessarily in value/position order,
//make a clone of the list sorted by position along axis //make a copy of the list sorted by position along axis
ObservableList<Axis.TickMark<X>> tickMarks = FXCollections.observableArrayList(getXAxis().getTickMarks()); SortedList<Axis.TickMark<X>> tickMarks = getXAxis().getTickMarks().sorted(Comparator.comparing(Axis.TickMark::getPosition));
tickMarks.sort(Comparator.comparing(Axis.TickMark::getPosition));
if (tickMarks.isEmpty() == false) { if (tickMarks.isEmpty() == false) {
//get the spacing between ticks in the underlying axis //get the spacing between ticks in the underlying axis
double spacing = getTickSpacing(); double spacing = getTickSpacing();
//initialize values from first tick //initialize values from first tick
TwoPartDateTime dateTime = new TwoPartDateTime(getTickMarkLabel(tickMarks.get(0).getValue())); TwoPartDateTime dateTime = new TwoPartDateTime(getTickMarkLabel(tickMarks.get(0).getValue()));
String lastSeenBranchLabel = dateTime.branch; String lastSeenContextLabel = dateTime.context;
//cumulative width of the current branch label
//x-positions (pixels) of the current branch and leaf labels //x-positions (pixels) of the current branch and leaf labels
double leafLabelX = 0; double specificLabelX = 0;
if (dateTime.branch.isEmpty()) { if (dateTime.context.isEmpty()) {
//if there is only one part to the date (ie only year), just add a label for each tick //if there is only one part to the date (ie only year), just add a label for each tick
for (Axis.TickMark<X> t : tickMarks) { for (Axis.TickMark<X> t : tickMarks) {
assignLeafLabel(new TwoPartDateTime(getTickMarkLabel(t.getValue())).leaf, addSpecificLabel(new TwoPartDateTime(getTickMarkLabel(t.getValue())).specifics,
spacing, spacing,
leafLabelX, specificLabelX,
isTickBold(t.getValue()) isTickBold(t.getValue())
); );
specificLabelX += spacing; //increment x
leafLabelX += spacing; //increment x
} }
} else { } else {
//there are two parts so ... //there are two parts so ...
//initialize additional state //initialize additional state
double branchLabelX = 0; double contextLabelX = 0;
double branchLabelWidth = 0; double contextLabelWidth = 0;
for (Axis.TickMark<X> t : tickMarks) { //for each tick
for (Axis.TickMark<X> t : tickMarks) {
//split the label into a TwoPartDateTime //split the label into a TwoPartDateTime
dateTime = new TwoPartDateTime(getTickMarkLabel(t.getValue())); dateTime = new TwoPartDateTime(getTickMarkLabel(t.getValue()));
//if we are still on the same branch //if we are still in the same context
if (lastSeenBranchLabel.equals(dateTime.branch)) { if (lastSeenContextLabel.equals(dateTime.context)) {
//increment branch width //increment context width
branchLabelWidth += spacing; contextLabelWidth += spacing;
} else {// we are on to a new branch, so ... } else {// we are on to a new context, so ...
assignBranchLabel(lastSeenBranchLabel, branchLabelWidth, branchLabelX); addContextLabel(lastSeenContextLabel, contextLabelWidth, contextLabelX);
//and then update label, x-pos, and width //and then update label, x-pos, and width
lastSeenBranchLabel = dateTime.branch; lastSeenContextLabel = dateTime.context;
branchLabelX += branchLabelWidth; contextLabelX += contextLabelWidth;
branchLabelWidth = spacing; contextLabelWidth = spacing;
} }
//add the label for the leaf (highest frequency part) //add the specific label (highest frequency part)
assignLeafLabel(dateTime.leaf, spacing, leafLabelX, isTickBold(t.getValue())); addSpecificLabel(dateTime.specifics, spacing, specificLabelX, isTickBold(t.getValue()));
//increment leaf position //increment specific position
leafLabelX += spacing; specificLabelX += spacing;
} }
//we have reached end so add branch label for current branch //we have reached end so add label for current context
assignBranchLabel(lastSeenBranchLabel, branchLabelWidth, branchLabelX); addContextLabel(lastSeenContextLabel, contextLabelWidth, contextLabelX);
} }
} }
//request layout since we have modified scene graph structure //request layout since we have modified scene graph structure
@ -393,46 +522,48 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
} }
/** /**
* add a {@link Text} node to the leaf container for the decluttered axis * Add a Text Node to the specific label container for the decluttered axis
* labels * labels.
* *
* @param labelText the string to add * @param labelText The String to add.
* @param labelWidth the width of the space available for the text * @param labelWidth The width, in pixels, of the space available for the
* @param labelX the horizontal position in the partPane of the text * text.
* @param bold true if the text should be bold, false otherwise * @param labelX The horizontal position, in pixels, in the specificPane
* of the text.
* @param bold True if the text should be bold, false otherwise.
*/ */
private synchronized void assignLeafLabel(String labelText, double labelWidth, double labelX, boolean bold) { private synchronized void addSpecificLabel(String labelText, double labelWidth, double labelX, boolean bold) {
Text label = new Text(" " + labelText + " "); //NON-NLS
Text label = new Text(" " + labelText + " "); //NOI18N
label.setTextAlignment(TextAlignment.CENTER); label.setTextAlignment(TextAlignment.CENTER);
label.setFont(Font.font(null, bold ? FontWeight.BOLD : FontWeight.NORMAL, 10)); label.setFont(Font.font(null, bold ? FontWeight.BOLD : FontWeight.NORMAL, 10));
//position label accounting for width //position label accounting for width
label.relocate(labelX + labelWidth / 2 - label.getBoundsInLocal().getWidth() / 2, 0); label.relocate(labelX + labelWidth / 2 - label.getBoundsInLocal().getWidth() / 2, 0);
label.autosize(); label.autosize();
if (leafPane.getChildren().isEmpty()) { if (specificLabelPane.getChildren().isEmpty()) {
//just add first label //just add first label
leafPane.getChildren().add(label); specificLabelPane.getChildren().add(label);
} else { } else {
//otherwise don't actually add the label if it would intersect with previous label //otherwise don't actually add the label if it would intersect with previous label
final Text lastLabel = (Text) leafPane.getChildren().get(leafPane.getChildren().size() - 1);
final Node lastLabel = specificLabelPane.getChildren().get(specificLabelPane.getChildren().size() - 1);
if (!lastLabel.getBoundsInParent().intersects(label.getBoundsInParent())) {
leafPane.getChildren().add(label); if (false == lastLabel.getBoundsInParent().intersects(label.getBoundsInParent())) {
specificLabelPane.getChildren().add(label);
} }
} }
} }
/** /**
* add a {@link Label} node to the branch container for the decluttered axis * Add a Label Node to the contextual label container for the decluttered
* labels * axis labels.
* *
* @param labelText the string to add * @param labelText The String to add.
* @param labelWidth the width of the space to use for the label * @param labelWidth The width, in pixels, of the space to use for the label
* @param labelX the horizontal position in the partPane of the text * @param labelX The horizontal position, in pixels, in the specificPane
* of the text
*/ */
private synchronized void assignBranchLabel(String labelText, double labelWidth, double labelX) { private synchronized void addContextLabel(String labelText, double labelWidth, double labelX) {
Label label = new Label(labelText); Label label = new Label(labelText);
label.setAlignment(Pos.CENTER); label.setAlignment(Pos.CENTER);
label.setTextAlignment(TextAlignment.CENTER); label.setTextAlignment(TextAlignment.CENTER);
@ -445,67 +576,76 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
label.setPrefWidth(labelWidth); label.setPrefWidth(labelWidth);
label.setMaxWidth(labelWidth); label.setMaxWidth(labelWidth);
label.relocate(labelX, 0); label.relocate(labelX, 0);
if (labelX == 0) { // first label has no border if (labelX == 0) { // first label has no border
label.setStyle("-fx-border-width: 0 0 0 0 ; -fx-border-color:black;"); // NON-NLS //NOI18N label.setBorder(null);
} else { // subsequent labels have border on left to create dividers } else { // subsequent labels have border on left to create dividers
label.setStyle("-fx-border-width: 0 0 0 1; -fx-border-color:black;"); // NON-NLS //NOI18N label.setBorder(ONLY_LEFT_BORDER);
} }
branchPane.getChildren().add(label); contextLabelPane.getChildren().add(label);
} }
public abstract double getAxisMargin();
/** /**
* A simple data object used to represent a partial date as up to two parts. * A simple data object used to represent a partial date as up to two parts.
* A low frequency part (branch) containing all but the most specific * A low frequency part (context) containing all but the most specific
* element, and a highest frequency part (leaf) containing the most specific * element, and a highest frequency part containing the most specific
* element. The branch and leaf names come from thinking of the space of all * element. If there is only one part, it will be in the context and the
* date times as a tree with higher frequency information further from the * specifics will equal an empty string
* root. If there is only one part, it will be in the branch and the leaf
* will equal an empty string
*/ */
@Immutable @Immutable
private static final class TwoPartDateTime { private static final class TwoPartDateTime {
/** /**
* the low frequency part of a date/time eg 2001-May-4 * The low frequency part of a date/time eg 2001-May-4
*/ */
private final String branch; private final String context;
/** /**
* the highest frequency part of a date/time eg 14 (2pm) * The highest frequency part of a date/time eg 14 (2pm)
*/
private final String specifics;
/**
* Constructor
*
* @param dateString The Date/Time to represent, formatted as per
* RangeDivisionInfo.getTickFormatter().
*/ */
private final String leaf;
TwoPartDateTime(String dateString) { TwoPartDateTime(String dateString) {
//find index of separator to spit on //find index of separator to split on
int splitIndex = StringUtils.lastIndexOfAny(dateString, " ", "-", ":"); //NOI18N int splitIndex = StringUtils.lastIndexOfAny(dateString, " ", "-", ":"); //NON-NLS
if (splitIndex < 0) { // there is only one part if (splitIndex < 0) { // there is only one part
leaf = dateString; specifics = dateString;
branch = ""; //NOI18N context = ""; //NON-NLS
} else { //split at index } else { //split at index
leaf = StringUtils.substring(dateString, splitIndex + 1); specifics = StringUtils.substring(dateString, splitIndex + 1);
branch = StringUtils.substring(dateString, 0, splitIndex); context = StringUtils.substring(dateString, 0, splitIndex);
} }
} }
} }
protected Interval getTimeRange() {
return filteredEvents.timeRangeProperty().get();
}
/** /**
* Base class for Tasks that update a visualization when the view settings * Base class for Tasks that update a visualization when the view settings
* change. * change.
* *
* @param <AxisValuesType> the type of data displayed along the X-Axis. * @param <AxisValuesType> The type of a single object that can represent
* the range of data displayed along the X-Axis.
*/ */
abstract protected class VisualizationUpdateTask<AxisValuesType> extends LoggedTask<Boolean> { abstract protected class VisualizationUpdateTask<AxisValuesType> extends LoggedTask<Boolean> {
private final Node center;
/**
* Constructor
*
* @param taskName The name of this task.
* @param logStateChanges Whether or not task state chanes should be
* logged.
*/
protected VisualizationUpdateTask(String taskName, boolean logStateChanges) { protected VisualizationUpdateTask(String taskName, boolean logStateChanges) {
super(taskName, logStateChanges); super(taskName, logStateChanges);
this.center = getCenter();
} }
/** /**
@ -513,9 +653,10 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
* indicator over the visualization. Derived Tasks should be sure to * indicator over the visualization. Derived Tasks should be sure to
* call this as part of their call() implementation. * call this as part of their call() implementation.
* *
* @return true * @return True
* *
* @throws Exception * @throws Exception If there is an unhandled exception during the
* background operation
*/ */
@NbBundle.Messages({"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"}) @NbBundle.Messages({"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"})
@Override @Override
@ -529,13 +670,12 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
setCenter(new StackPane(center, maskerPane)); setCenter(new StackPane(center, maskerPane));
setCursor(Cursor.WAIT); setCursor(Cursor.WAIT);
}); });
return true; return true;
} }
private final Node center = getCenter();
/** /**
* updates the horisontal axis and removes the blocking progress * Updates the horizontal axis and removes the blocking progress
* indicator. Derived Tasks should be sure to call this as part of their * indicator. Derived Tasks should be sure to call this as part of their
* succeeded() implementation. * succeeded() implementation.
*/ */
@ -543,28 +683,58 @@ public abstract class AbstractVisualizationPane<X, Y, NodeType extends Node, Cha
protected void succeeded() { protected void succeeded() {
super.succeeded(); super.succeeded();
layoutDateLabels(); layoutDateLabels();
cleanup();
Platform.runLater(() -> {
setCenter(center); //clear masker pane
setCursor(Cursor.DEFAULT);
});
} }
/** /**
* Clears the chart data and sets the horisontal axis range. For use * Removes the blocking progress indicator. Derived Tasks should be sure
* to call this as part of their cancelled() implementation.
*/
@Override
protected void cancelled() {
super.cancelled();
cleanup();
}
/**
* Removes the blocking progress indicator. Derived Tasks should be sure
* to call this as part of their failed() implementation.
*/
@Override
protected void failed() {
super.failed();
cleanup();
}
/**
* Removes the blocking progress indicator and reset the cursor to the
* default.
*/
private void cleanup() {
setCenter(center); //clear masker pane installed in call()
setCursor(Cursor.DEFAULT);
}
/**
* Clears the chart data and sets the horizontal axis range. For use
* within the derived implementation of the call() method. * within the derived implementation of the call() method.
* *
* @param axisValues * @param axisValues
*/ */
@ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI) @ThreadConfined(type = ThreadConfined.ThreadType.NOT_UI)
protected void resetChart(AxisValuesType axisValues) { protected void resetChart(AxisValuesType axisValues) {
Platform.runLater(() -> { Platform.runLater(() -> {
resetData(); clearChartData();
setDateAxisValues(axisValues); setDateAxisValues(axisValues);
}); });
} }
/**
* Set the horizontal range that this chart will show.
*
* @param values A single object representing the range that this chart
* will show.
*/
abstract protected void setDateAxisValues(AxisValuesType values); abstract protected void setDateAxisValues(AxisValuesType values);
} }
} }

View File

@ -379,7 +379,7 @@ final public class VisualizationPanel extends BorderPane {
eventsTree.setDetailViewPane((DetailViewPane) visualization); eventsTree.setDetailViewPane((DetailViewPane) visualization);
}); });
} }
visualization.hasEvents.addListener((observable, oldValue, newValue) -> { visualization.hasVisibleEventsProperty().addListener((observable, oldValue, newValue) -> {
if (newValue == false) { if (newValue == false) {
notificationPane.setContent( notificationPane.setContent(

View File

@ -1,24 +0,0 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2013-15 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.
*/
#this is the label for the vetical axis
CountsChartPane.numberOfEvents=Number of Events
CountsViewPane.scaleLabel.text=Scale\:
CountsViewPane.logRadio.text=Logarithmic
CountsViewPane.linearRadio.text=Linear

View File

@ -1,14 +1,14 @@
CountsChartPane.numberOfEvents=\u30A4\u30D9\u30F3\u30C8\u6570 CountsViewPane.numberOfEvents=\u30a4\u30d9\u30f3\u30c8\u6570
CountsViewPane.detailSwitchMessage=\u79D2\u3088\u308A\u5C0F\u3055\u3044\u5358\u4F4D\u306F\u3042\u308A\u307E\u305B\u3093\u3002\n\u8A73\u7D30\u30D3\u30E5\u30FC\u306B\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F CountsViewPane.detailSwitchMessage=\u79d2\u3088\u308a\u5c0f\u3055\u3044\u5358\u4f4d\u306f\u3042\u308a\u307e\u305b\u3093\u3002\n\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f
CountsViewPane.detailSwitchTitle=\u8A73\u7D30\u30D3\u30E5\u30FC\u306B\u5909\u66F4\u3057\u307E\u3059\u304B\uFF1F CountsViewPane.detailSwitchTitle=\u8a73\u7d30\u30d3\u30e5\u30fc\u306b\u5909\u66f4\u3057\u307e\u3059\u304b\uff1f
Timeline.ui.countsview.menuItem.selectEventType=\u30A4\u30D9\u30F3\u30C8\u30BF\u30A4\u30D7\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectEventType=\u30a4\u30d9\u30f3\u30c8\u30bf\u30a4\u30d7\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.selectTimeandType=\u6642\u9593\u3068\u30BF\u30A4\u30D7\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectTimeandType=\u6642\u9593\u3068\u30bf\u30a4\u30d7\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.selectTimeRange=\u6642\u9593\u7BC4\u56F2\u3092\u9078\u629E Timeline.ui.countsview.menuItem.selectTimeRange=\u6642\u9593\u7bc4\u56f2\u3092\u9078\u629e
Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7BC4\u56F2\u3078\u30BA\u30FC\u30E0\u30A4\u30F3 Timeline.ui.countsview.menuItem.zoomIntoTimeRange=\u6642\u9593\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\u30a4\u30f3
CountsViewPane.loggedTask.name=\u30AB\u30A6\u30F3\u30C8\u30D3\u30E5\u30FC\u3092\u66F4\u65B0\u4E2D CountsViewPane.loggedTask.name=\u30ab\u30a6\u30f3\u30c8\u30d3\u30e5\u30fc\u3092\u66f4\u65b0\u4e2d
CountsViewPane.loggedTask.updatingCounts=\u30D3\u30B8\u30E5\u30A2\u30E9\u30A4\u30BC\u30FC\u30B7\u30E7\u30F3\uFF08\u53EF\u8996\u5316\uFF09\u3092\u5165\u529B\u4E2D CountsViewPane.loggedTask.updatingCounts=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\uff08\u53ef\u8996\u5316\uff09\u3092\u5165\u529b\u4e2d
CountsViewPane.tooltip.text={0} {1} \u30A4\u30D9\u30F3\u30C8\n{2}\u3068\n{3}\u306E\u9593 CountsViewPane.tooltip.text={0} {1} \u30a4\u30d9\u30f3\u30c8\n{2}\u3068\n{3}\u306e\u9593
CountsViewPane.linearRadio.text=\u30EA\u30CB\u30A2 CountsViewPane.linearRadio.text=\u30ea\u30cb\u30a2
CountsViewPane.logRadio.text=\u5BFE\u6570\u7684 CountsViewPane.logRadio.text=\u5bfe\u6570\u7684
CountsViewPane.scaleLabel.text=\u30B9\u30B1\u30FC\u30EB\uFF1A CountsViewPane.scaleLabel.text=\u30b9\u30b1\u30fc\u30eb\uff1a
*=Autopsy\u30D5\u30A9\u30EC\u30F3\u30B8\u30C3\u30AF\u30D6\u30E9\u30A6\u30B6 *=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d6\u30e9\u30a6\u30b6

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014 Basis Technology Corp. * Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -19,27 +19,36 @@
package org.sleuthkit.autopsy.timeline.ui.countsview; package org.sleuthkit.autopsy.timeline.ui.countsview;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.function.Function; import java.util.function.Function;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.beans.Observable; import javafx.beans.Observable;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.SimpleObjectProperty; import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections; import javafx.collections.FXCollections;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import javafx.fxml.FXML; import javafx.fxml.FXML;
import javafx.geometry.Insets;
import javafx.scene.Cursor;
import javafx.scene.Node; import javafx.scene.Node;
import javafx.scene.chart.CategoryAxis; import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis; import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart; import javafx.scene.chart.XYChart;
import javafx.scene.control.Label; import javafx.scene.control.Label;
import javafx.scene.control.RadioButton; import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.control.Tooltip; import javafx.scene.control.Tooltip;
import javafx.scene.effect.Effect; import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.controlsfx.control.PopOver;
import org.joda.time.Interval; import org.joda.time.Interval;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -49,29 +58,24 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel; import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType; import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
import org.sleuthkit.autopsy.timeline.ui.detailview.DetailViewPane; import static org.sleuthkit.autopsy.timeline.ui.countsview.Bundle.*;
import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo; import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
/** /**
* FXML Controller class for a {@link StackedBarChart<String,Number>} based * FXML Controller class for a StackedBarChart<String,Number> based
* implementation of a {@link TimeLineView}. * implementation of a TimeLineChart.
* *
* This class listens to changes in the assigned {@link FilteredEventsModel} and * This class listens to changes in the assigned FilteredEventsModel and updates
* updates the internal {@link StackedBarChart} to reflect the currently * the internal EventCountsChart to reflect the currently requested events.
* requested events.
* *
* This class captures input from the user in the form of mouse clicks on graph * This class captures input from the user in the form of mouse clicks on graph
* bars, and forwards them to the assigned {@link TimeLineController} * * bars, and forwards them to the assigned TimeLineController
* *
* Concurrency Policy: Access to the private members stackedBarChart, countAxis, * Concurrency Policy: Access to the private members stackedBarChart, countAxis,
* dateAxis, EventTypeMap, and dataSets affects the stackedBarChart so they all * dateAxis, EventTypeMap, and dataSets affects the stackedBarChart so they all
* must only be manipulated on the JavaFx thread (through {@link Platform#runLater(java.lang.Runnable)} * must only be manipulated on the JavaFx thread (through
* * Platform.runLater(java.lang.Runnable). The FilteredEventsModel should
* {@link CountsChartPane#filteredEvents} should encapsulate all need * encapsulate all need synchronization internally.
* synchronization internally.
*
* TODO: refactor common code out of this class and {@link DetailViewPane} into
* {@link AbstractVisualizationPane}
*/ */
public class CountsViewPane extends AbstractVisualizationPane<String, Number, Node, EventCountsChart> { public class CountsViewPane extends AbstractVisualizationPane<String, Number, Node, EventCountsChart> {
@ -80,7 +84,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
private final NumberAxis countAxis = new NumberAxis(); private final NumberAxis countAxis = new NumberAxis();
private final CategoryAxis dateAxis = new CategoryAxis(FXCollections.<String>observableArrayList()); private final CategoryAxis dateAxis = new CategoryAxis(FXCollections.<String>observableArrayList());
private final SimpleObjectProperty<ScaleType> scale = new SimpleObjectProperty<>(ScaleType.LOGARITHMIC); private final SimpleObjectProperty<Scale> scaleProp = new SimpleObjectProperty<>(Scale.LOGARITHMIC);
@Override @Override
protected String getTickMarkLabel(String labelValueString) { protected String getTickMarkLabel(String labelValueString) {
@ -94,32 +98,44 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
@Override @Override
protected Task<Boolean> getUpdateTask() { protected Task<Boolean> getNewUpdateTask() {
return new CountsUpdateTask(); return new CountsUpdateTask();
} }
/**
* Constructor
*
* @param controller The TimelineController for this visualization.
* @param specificPane The container for the specific axis labels.
* @param contextPane The container for the contextual axis labels.
* @param spacer The Region to use as a spacer to keep the axis labels
* aligned.
*/
@NbBundle.Messages({
"# {0} - scale name",
"CountsViewPane.numberOfEvents=Number of Events ({0})"})
public CountsViewPane(TimeLineController controller) { public CountsViewPane(TimeLineController controller) {
super(controller); super(controller);
chart = new EventCountsChart(controller, dateAxis, countAxis, selectedNodes); setChart(new EventCountsChart(controller, dateAxis, countAxis, getSelectedNodes()));
getChart().setData(dataSeries);
Tooltip.install(getChart(), getDefaultTooltip());
chart.setData(dataSeries); setSettingsNodes(new CountsViewSettingsPane().getChildrenUnmodifiable());
setCenter(chart);
Tooltip.install(chart, getDefaultTooltip()); dateAxis.getTickMarks().addListener((Observable tickMarks) -> layoutDateLabels());
dateAxis.categorySpacingProperty().addListener((Observable spacing) -> layoutDateLabels());
dateAxis.getCategories().addListener((Observable categories) -> layoutDateLabels());
settingsNodes = new ArrayList<>(new CountsViewSettingsPane().getChildrenUnmodifiable()); //bind tick visibility to scaleProp
BooleanBinding scaleIsLinear = scaleProp.isEqualTo(Scale.LINEAR);
dateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels()); countAxis.tickLabelsVisibleProperty().bind(scaleIsLinear);
dateAxis.categorySpacingProperty().addListener((Observable observable) -> layoutDateLabels()); countAxis.tickMarkVisibleProperty().bind(scaleIsLinear);
dateAxis.getCategories().addListener((Observable observable) -> layoutDateLabels()); countAxis.minorTickVisibleProperty().bind(scaleIsLinear);
scaleProp.addListener(scale -> {
scale.addListener(o -> {
countAxis.tickLabelsVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
countAxis.tickMarkVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
countAxis.minorTickVisibleProperty().bind(scale.isEqualTo(ScaleType.LINEAR));
update(); update();
syncAxisScaleLabel();
}); });
syncAxisScaleLabel();
} }
@Override @Override
@ -138,33 +154,86 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
@Override @Override
protected Effect getSelectionEffect() { protected void applySelectionEffect(Node c1, Boolean applied) {
return chart.getSelectionEffect(); c1.setEffect(applied ? getChart().getSelectionEffect() : null);
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void applySelectionEffect(Node c1, Boolean applied) { protected void clearChartData() {
if (applied) { for (XYChart.Series<String, Number> series : dataSeries) {
c1.setEffect(getSelectionEffect()); series.getData().clear();
} else { }
c1.setEffect(null); dataSeries.clear();
eventTypeToSeriesMap.clear();
createSeries();
}
/**
* Set the appropriate label on the vertical axis, depending on the selected
* scale.
*/
private void syncAxisScaleLabel() {
countAxis.setLabel(Bundle.CountsViewPane_numberOfEvents(scaleProp.get().getDisplayName()));
}
/**
* Enum for the Scales available in the Counts View.
*/
@NbBundle.Messages({
"ScaleType.Linear=Linear",
"ScaleType.Logarithmic=Logarithmic"})
private static enum Scale implements Function<Long, Double> {
LINEAR(Bundle.ScaleType_Linear()) {
@Override
public Double apply(Long inValue) {
return inValue.doubleValue();
}
},
LOGARITHMIC(Bundle.ScaleType_Logarithmic()) {
@Override
public Double apply(Long inValue) {
return Math.log10(inValue) + 1;
}
};
private final String displayName;
/**
* Constructor
*
* @param displayName The display name for this Scale.
*/
Scale(String displayName) {
this.displayName = displayName;
}
/**
* Get the display name of this ScaleType
*
* @return The display name.
*/
private String getDisplayName() {
return displayName;
} }
} }
@Override @Override
public double getAxisMargin() { protected double getAxisMargin() {
return dateAxis.getStartMargin() + dateAxis.getEndMargin(); return dateAxis.getStartMargin() + dateAxis.getEndMargin();
} }
/*
* A Pane that contains widgets to adjust settings specific to a
* CountsViewPane
*/
private class CountsViewSettingsPane extends HBox { private class CountsViewSettingsPane extends HBox {
@FXML @FXML
private RadioButton logRadio; private RadioButton logRadio;
@FXML @FXML
private RadioButton linearRadio; private RadioButton linearRadio;
@FXML @FXML
private ToggleGroup scaleGroup; private ToggleGroup scaleGroup;
@ -172,58 +241,68 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
private Label scaleLabel; private Label scaleLabel;
@FXML @FXML
private ImageView helpImageView;
@FXML
@NbBundle.Messages({
"CountsViewPane.logRadio.text=Logarithmic",
"CountsViewPane.scaleLabel.text=Scale:",
"CountsViewPane.scaleHelp.label.text=Scales: ",
"CountsViewPane.linearRadio.text=Linear",
"CountsViewPane.scaleHelp=The default linear scale is good for many use cases. When this scale is selected, the height of the bars represents the counts in a linear, one-to-one fashion, and the y-axis is labeled with values. When the range of values is very large, date ranges with relatively low counts have a bar that may be too small to see. To help avoid the misperception of this as no events, the labels for date ranges with events are bold. To see bars that are too small, there are three options: adjust the window size so that the visualization area has more vertical space, adjust the time range shown so that time periods with relatively much larger bars are excluded, or adjust the scale setting to logarithmic.\n\nThe logarithmic scale represents the number of events in a non-linear way that compresses the difference between very large and very small numbers. Note that even with the logarithmic scale, an extremely large difference in counts may still produce bars too small to see. In this case the only option may be to exclude events to reduce the difference in counts. NOTE: Because the logarithmic scale is applied to each event type separately, the height of the combined bar is not very meaningful, and to emphasize this, no labels are shown on the y-axis. The logarithmic scale should be used to quickly compare the counts ",
"CountsViewPane.scaleHelp2=across time within a type, or across types for one time period, but not both.",
"CountsViewPane.scaleHelp3= The exact numbers (available in tooltips or the result viewer) should be used for absolute comparisons. Use the logarithmic scale with care."})
void initialize() { void initialize() {
assert logRadio != null : "fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS assert logRadio != null : "fx:id=\"logRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
assert linearRadio != null : "fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS assert linearRadio != null : "fx:id=\"linearRadio\" was not injected: check your FXML file 'CountsViewSettingsPane.fxml'."; // NON-NLS
logRadio.setSelected(true); scaleLabel.setText(CountsViewPane_scaleLabel_text());
linearRadio.setText(CountsViewPane_linearRadio_text());
logRadio.setText(CountsViewPane_logRadio_text());
scaleGroup.selectedToggleProperty().addListener(observable -> { scaleGroup.selectedToggleProperty().addListener(observable -> {
if (scaleGroup.getSelectedToggle() == linearRadio) { if (scaleGroup.getSelectedToggle() == linearRadio) {
scale.set(ScaleType.LINEAR); scaleProp.set(Scale.LINEAR);
} } else if (scaleGroup.getSelectedToggle() == logRadio) {
if (scaleGroup.getSelectedToggle() == logRadio) { scaleProp.set(Scale.LOGARITHMIC);
scale.set(ScaleType.LOGARITHMIC);
} }
}); });
logRadio.setSelected(true);
logRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.logRadio.text")); //make a popup hrlp window with descriptions of the scales.
linearRadio.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.linearRadio.text")); helpImageView.setCursor(Cursor.HAND);
scaleLabel.setText(NbBundle.getMessage(CountsViewPane.class, "CountsViewPane.scaleLabel.text")); helpImageView.setOnMouseClicked(clicked -> {
Text text = new Text(Bundle.CountsViewPane_scaleHelp());
Text text2 = new Text(Bundle.CountsViewPane_scaleHelp2());
Font baseFont = text.getFont();
text2.setFont(Font.font(baseFont.getFamily(), FontWeight.BOLD, FontPosture.ITALIC, baseFont.getSize()));
Text text3 = new Text(Bundle.CountsViewPane_scaleHelp3());
Pane borderPane = new BorderPane(null, null, new ImageView(helpImageView.getImage()),
new TextFlow(text, text2, text3),
new Label(Bundle.CountsViewPane_scaleHelp_label_text()));
borderPane.setPadding(new Insets(10));
borderPane.setPrefWidth(500);
PopOver popOver = new PopOver(borderPane);
popOver.setDetachable(false);
popOver.setArrowLocation(PopOver.ArrowLocation.TOP_CENTER);
popOver.show(helpImageView);
});
} }
/**
* Constructor
*/
CountsViewSettingsPane() { CountsViewSettingsPane() {
FXMLConstructor.construct(this, "CountsViewSettingsPane.fxml"); // NON-NLS FXMLConstructor.construct(this, "CountsViewSettingsPane.fxml"); // NON-NLS
} }
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) /**
@Override * Task that clears the Chart, fetches new data according to the current
protected void resetData() { * ZoomParams and loads it into the Chart
for (XYChart.Series<String, Number> s : dataSeries) { *
s.getData().clear(); */
}
dataSeries.clear();
eventTypeToSeriesMap.clear();
createSeries();
}
private static enum ScaleType implements Function<Long, Double> {
LINEAR(Long::doubleValue),
LOGARITHMIC(t -> Math.log10(t) + 1);
private final Function<Long, Double> func;
ScaleType(Function<Long, Double> func) {
this.func = func;
}
@Override
public Double apply(Long t) {
return func.apply(t);
}
}
@NbBundle.Messages({ @NbBundle.Messages({
"CountsViewPane.loggedTask.name=Updating Counts View", "CountsViewPane.loggedTask.name=Updating Counts View",
"CountsViewPane.loggedTask.updatingCounts=Populating visualization"}) "CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
@ -239,27 +318,25 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
FilteredEventsModel eventsModel = getEventsModel();
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(getTimeRange()); final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(eventsModel.getTimeRange());
chart.setRangeInfo(rangeInfo); //do we need this. It seems like a hack. getChart().setRangeInfo(rangeInfo); //do we need this. It seems like a hack.
List<Interval> intervals = rangeInfo.getIntervals(); List<Interval> intervals = rangeInfo.getIntervals();
List<String> categories = Lists.transform(intervals, rangeInfo::formatForTick);
//clear old data, and reset ranges and series //clear old data, and reset ranges and series
resetChart(categories); resetChart(Lists.transform(intervals, rangeInfo::formatForTick));
updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts()); updateMessage(Bundle.CountsViewPane_loggedTask_updatingCounts());
int chartMax = 0; int chartMax = 0;
int numIntervals = intervals.size(); int numIntervals = intervals.size();
Scale activeScale = scaleProp.get();
/* /*
* for each interval query database for event counts and add to * For each interval, query the database for event counts and add
* chart. * the counts to the chart. Doing this in chunks might seem
* * inefficient but it lets us reuse more cached results as the user
* Doing this in chunks might seem inefficient but it lets us reuse * navigates to overlapping views.
* more cached results as the user navigates to overlapping viewws
*
* //TODO: implement similar chunked caching in DetailsView -jm
*/ */
for (int i = 0; i < numIntervals; i++) { for (int i = 0; i < numIntervals; i++) {
if (isCancelled()) { if (isCancelled()) {
@ -270,7 +347,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
int maxPerInterval = 0; int maxPerInterval = 0;
//query for current interval //query for current interval
Map<EventType, Long> eventCounts = filteredEvents.getEventCounts(interval); Map<EventType, Long> eventCounts = eventsModel.getEventCounts(interval);
//for each type add data to graph //for each type add data to graph
for (final EventType eventType : eventCounts.keySet()) { for (final EventType eventType : eventCounts.keySet()) {
@ -281,7 +358,7 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
final Long count = eventCounts.get(eventType); final Long count = eventCounts.get(eventType);
if (count > 0) { if (count > 0) {
final String intervalCategory = rangeInfo.formatForTick(interval); final String intervalCategory = rangeInfo.formatForTick(interval);
final double adjustedCount = scale.get().apply(count); final double adjustedCount = activeScale.apply(count);
final XYChart.Data<String, Number> dataItem = final XYChart.Data<String, Number> dataItem =
new XYChart.Data<>(intervalCategory, adjustedCount, new XYChart.Data<>(intervalCategory, adjustedCount,
@ -292,9 +369,10 @@ public class CountsViewPane extends AbstractVisualizationPane<String, Number, No
} }
chartMax = Math.max(chartMax, maxPerInterval); chartMax = Math.max(chartMax, maxPerInterval);
} }
//adjust vertical axis according to scale type and max counts //adjust vertical axis according to scale type and max counts
double countAxisUpperbound = 1 + chartMax * 1.2; double countAxisUpperbound = 1 + chartMax * 1.2;
double tickUnit = ScaleType.LINEAR.equals(scale.get()) double tickUnit = Scale.LINEAR.equals(activeScale)
? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1)) ? Math.pow(10, Math.max(0, Math.floor(Math.log10(chartMax)) - 1))
: Double.MAX_VALUE; : Double.MAX_VALUE;
Platform.runLater(() -> { Platform.runLater(() -> {

View File

@ -1,28 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?> <?import javafx.geometry.Insets?>
<?import javafx.geometry.*?> <?import javafx.scene.control.Label?>
<?import javafx.scene.control.*?> <?import javafx.scene.control.RadioButton?>
<?import javafx.scene.layout.*?> <?import javafx.scene.control.ToggleGroup?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<fx:root alignment="CENTER_LEFT" type="HBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1"> <fx:root alignment="CENTER_LEFT" type="HBox" xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1">
<children> <children>
<HBox alignment="CENTER_LEFT" spacing="5.0"> <HBox alignment="CENTER_LEFT" spacing="5.0">
<children> <children>
<Label fx:id="scaleLabel" minHeight="-Infinity" minWidth="-Infinity"> <Label fx:id="scaleLabel" minHeight="-Infinity" minWidth="-Infinity" text="Scale:">
<HBox.margin> <HBox.margin>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" /> <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
</HBox.margin> </HBox.margin>
</Label> </Label>
<RadioButton fx:id="logRadio" mnemonicParsing="false" selected="true">
<toggleGroup> <RadioButton fx:id="logRadio" mnemonicParsing="false" selected="true" styleClass="toggle-butto" text="Logarithmic">
<ToggleGroup fx:id="scaleGroup" /> <toggleGroup>
</toggleGroup> <ToggleGroup fx:id="scaleGroup" />
</RadioButton> </toggleGroup>
<RadioButton fx:id="linearRadio" mnemonicParsing="false" toggleGroup="$scaleGroup" /> </RadioButton>
</children> <RadioButton fx:id="linearRadio" mnemonicParsing="false" text="Linear" toggleGroup="$scaleGroup" />
<HBox.margin>
<Insets bottom="3.0" left="3.0" right="3.0" top="3.0" />
</HBox.margin> <ImageView fx:id="helpImageView" fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
</HBox> <image>
</children></fx:root> <Image url="@../../images/question-frame.png" />
</image>
</ImageView>
</children>
</HBox>
</children>
</fx:root>

View File

@ -1,7 +1,7 @@
/* /*
* Autopsy Forensic Browser * Autopsy Forensic Browser
* *
* Copyright 2014-15 Basis Technology Corp. * Copyright 2014-16 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org * Contact: carrier <at> sleuthkit <dot> org
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
@ -87,7 +87,6 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
dateAxis.setTickLabelsVisible(false); dateAxis.setTickLabelsVisible(false);
dateAxis.setTickLabelGap(0); dateAxis.setTickLabelGap(0);
countAxis.setLabel(NbBundle.getMessage(CountsViewPane.class, "CountsChartPane.numberOfEvents"));
countAxis.setAutoRanging(false); countAxis.setAutoRanging(false);
countAxis.setLowerBound(0); countAxis.setLowerBound(0);
countAxis.setAnimated(true); countAxis.setAnimated(true);
@ -167,6 +166,7 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
return new CountsIntervalSelector(this); return new CountsIntervalSelector(this);
} }
@Override
public ObservableList<Node> getSelectedNodes() { public ObservableList<Node> getSelectedNodes() {
return selectedNodes; return selectedNodes;
} }

View File

@ -18,7 +18,6 @@
*/ */
package org.sleuthkit.autopsy.timeline.ui.detailview; package org.sleuthkit.autopsy.timeline.ui.detailview;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -40,7 +39,6 @@ import javafx.scene.control.RadioButton;
import javafx.scene.control.Slider; import javafx.scene.control.Slider;
import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup; import javafx.scene.control.ToggleGroup;
import javafx.scene.effect.Effect;
import javafx.scene.layout.HBox; import javafx.scene.layout.HBox;
import javafx.stage.Modality; import javafx.stage.Modality;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -53,6 +51,7 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined;
import org.sleuthkit.autopsy.timeline.FXMLConstructor; import org.sleuthkit.autopsy.timeline.FXMLConstructor;
import org.sleuthkit.autopsy.timeline.TimeLineController; import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.EventStripe; import org.sleuthkit.autopsy.timeline.datamodel.EventStripe;
import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent; import org.sleuthkit.autopsy.timeline.datamodel.TimeLineEvent;
import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane; import org.sleuthkit.autopsy.timeline.ui.AbstractVisualizationPane;
import org.sleuthkit.autopsy.timeline.utils.MappedList; import org.sleuthkit.autopsy.timeline.utils.MappedList;
@ -104,21 +103,20 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
this.selectedEvents = new MappedList<>(getSelectedNodes(), EventNodeBase<?>::getEvent); this.selectedEvents = new MappedList<>(getSelectedNodes(), EventNodeBase<?>::getEvent);
//initialize chart; //initialize chart;
chart = new DetailsChart(controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis, getSelectedNodes()); setChart(new DetailsChart(controller, detailsChartDateAxis, pinnedDateAxis, verticalAxis, getSelectedNodes()));
setCenter(chart); setSettingsNodes(new DetailViewSettingsPane(getChart().getLayoutSettings()).getChildrenUnmodifiable());
settingsNodes = new ArrayList<>(new DetailViewSettingsPane(chart.getLayoutSettings()).getChildrenUnmodifiable());
//bind layout fo axes and spacers //bind layout fo axes and spacers
detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels()); detailsChartDateAxis.getTickMarks().addListener((Observable observable) -> layoutDateLabels());
detailsChartDateAxis.getTickSpacing().addListener(observable -> layoutDateLabels()); detailsChartDateAxis.getTickSpacing().addListener(observable -> layoutDateLabels());
verticalAxis.setAutoRanging(false); //prevent XYChart.updateAxisRange() from accessing dataSeries on JFX thread causing ConcurrentModificationException verticalAxis.setAutoRanging(false); //prevent XYChart.updateAxisRange() from accessing dataSeries on JFX thread causing ConcurrentModificationException
selectedNodes.addListener((Observable observable) -> { getSelectedNodes().addListener((Observable observable) -> {
//update selected nodes highlight //update selected nodes highlight
chart.setHighlightPredicate(selectedNodes::contains); getChart().setHighlightPredicate(getSelectedNodes()::contains);
//update controllers list of selected event ids when view's selection changes. //update controllers list of selected event ids when view's selection changes.
getController().selectEventIDs(selectedNodes.stream() getController().selectEventIDs(getSelectedNodes().stream()
.flatMap(detailNode -> detailNode.getEventIDs().stream()) .flatMap(detailNode -> detailNode.getEventIDs().stream())
.collect(Collectors.toList())); .collect(Collectors.toList()));
}); });
@ -130,7 +128,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
* contain no interesting non-time related information. * contain no interesting non-time related information.
*/ */
public ObservableList<TimeLineEvent> getAllNestedEvents() { public ObservableList<TimeLineEvent> getAllNestedEvents() {
return chart.getAllNestedEvents(); return getChart().getAllNestedEvents();
} }
/* /*
@ -162,13 +160,13 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
return eventNode -> StringUtils.equalsIgnoreCase(eventNode.getDescription(), description); return eventNode -> StringUtils.equalsIgnoreCase(eventNode.getDescription(), description);
} }
})// => predicates that match strings agains the descriptions of the events in highlightedEvents })// => predicates that match strings agains the descriptions of the events in highlightedEvents
.reduce(selectedNodes::contains, Predicate::or); // => predicate that matches an of the descriptions or selected nodes .reduce(getSelectedNodes()::contains, Predicate::or); // => predicate that matches an of the descriptions or selected nodes
chart.setHighlightPredicate(highlightPredicate); //use this predicate to highlight nodes getChart().setHighlightPredicate(highlightPredicate); //use this predicate to highlight nodes
}); });
} }
@Override @Override
final public DateAxis getXAxis() { final protected DateAxis getXAxis() {
return detailsChartDateAxis; return detailsChartDateAxis;
} }
@ -181,7 +179,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
* @return a new Action that will unhide events with the given description. * @return a new Action that will unhide events with the given description.
*/ */
public Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD) { public Action newUnhideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
return new UnhideDescriptionAction(description, descriptionLoD, chart); return new UnhideDescriptionAction(description, descriptionLoD, getChart());
} }
/** /**
@ -193,13 +191,13 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
* @return a new Action that will hide events with the given description. * @return a new Action that will hide events with the given description.
*/ */
public Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD) { public Action newHideDescriptionAction(String description, DescriptionLoD descriptionLoD) {
return new HideDescriptionAction(description, descriptionLoD, chart); return new HideDescriptionAction(description, descriptionLoD, getChart());
} }
@ThreadConfined(type = ThreadConfined.ThreadType.JFX) @ThreadConfined(type = ThreadConfined.ThreadType.JFX)
@Override @Override
protected void resetData() { protected void clearChartData() {
chart.reset(); getChart().reset();
} }
@Override @Override
@ -223,22 +221,17 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
} }
@Override @Override
protected Task<Boolean> getUpdateTask() { protected Task<Boolean> getNewUpdateTask() {
return new DetailsUpdateTask(); return new DetailsUpdateTask();
} }
@Override
protected Effect getSelectionEffect() {
return null;
}
@Override @Override
protected void applySelectionEffect(EventNodeBase<?> c1, Boolean selected) { protected void applySelectionEffect(EventNodeBase<?> c1, Boolean selected) {
c1.applySelectionEffect(selected); c1.applySelectionEffect(selected);
} }
@Override @Override
public double getAxisMargin() { protected double getAxisMargin() {
return 0; return 0;
} }
@ -363,14 +356,15 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
if (isCancelled()) { if (isCancelled()) {
return null; return null;
} }
FilteredEventsModel eventsModel = getEventsModel();
//clear the chart and set the horixontal axis //clear the chart and set the horixontal axis
resetChart(getTimeRange()); resetChart(eventsModel.getTimeRange());
updateMessage(Bundle.DetailViewPane_loggedTask_queryDb()); updateMessage(Bundle.DetailViewPane_loggedTask_queryDb());
//get the event stripes to be displayed //get the event stripes to be displayed
List<EventStripe> eventStripes = filteredEvents.getEventStripes(); List<EventStripe> eventStripes = eventsModel.getEventStripes();
final int size = eventStripes.size(); final int size = eventStripes.size();
//if there are too many stipes show a confirmation dialog //if there are too many stipes show a confirmation dialog
if (size > 2000) { if (size > 2000) {
@ -405,7 +399,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
} }
updateProgress(i, size); updateProgress(i, size);
final EventStripe stripe = eventStripes.get(i); final EventStripe stripe = eventStripes.get(i);
Platform.runLater(() -> chart.addStripe(stripe)); Platform.runLater(() -> getChart().addStripe(stripe));
} }
return eventStripes.isEmpty() == false; return eventStripes.isEmpty() == false;
@ -414,7 +408,7 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
@Override @Override
protected void cancelled() { protected void cancelled() {
super.cancelled(); super.cancelled();
controller.retreat(); getController().retreat();
} }
@Override @Override
@ -423,5 +417,4 @@ public class DetailViewPane extends AbstractVisualizationPane<DateTime, EventStr
pinnedDateAxis.setRange(timeRange, true); pinnedDateAxis.setRange(timeRange, true);
} }
} }
} }

View File

@ -313,6 +313,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.deleteListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchEditListPanel.deleteListButton.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="deleteListButtonActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JButton" name="saveListButton"> <Component class="javax.swing.JButton" name="saveListButton">
<Properties> <Properties>

View File

@ -23,6 +23,7 @@ import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -39,6 +40,7 @@ import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableColumn; import javax.swing.table.TableColumn;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@ -52,6 +54,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
private static final Logger logger = Logger.getLogger(GlobalEditListPanel.class.getName()); private static final Logger logger = Logger.getLogger(GlobalEditListPanel.class.getName());
private KeywordTableModel tableModel; private KeywordTableModel tableModel;
private KeywordList currentKeywordList; private KeywordList currentKeywordList;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form GlobalEditListPanel * Creates new form GlobalEditListPanel
@ -141,6 +144,16 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
}); });
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
void setButtonStates() { void setButtonStates() {
boolean isIngestRunning = IngestManager.getInstance().isIngestRunning(); boolean isIngestRunning = IngestManager.getInstance().isIngestRunning();
boolean isListSelected = currentKeywordList != null; boolean isListSelected = currentKeywordList != null;
@ -297,6 +310,11 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
deleteListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N deleteListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/delete16.png"))); // NOI18N
deleteListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteListButton.text")); // NOI18N deleteListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.deleteListButton.text")); // NOI18N
deleteListButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
deleteListButtonActionPerformed(evt);
}
});
saveListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/save16.png"))); // NOI18N saveListButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/save16.png"))); // NOI18N
saveListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.saveListButton.text")); // NOI18N saveListButton.setText(org.openide.util.NbBundle.getMessage(GlobalEditListPanel.class, "KeywordSearchEditListPanel.saveListButton.text")); // NOI18N
@ -423,6 +441,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
XmlKeywordSearchList.getCurrent().addList(currentKeywordList); XmlKeywordSearchList.getCurrent().addList(currentKeywordList);
chRegex.setSelected(false); chRegex.setSelected(false);
addWordField.setText(""); addWordField.setText("");
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
setButtonStates(); setButtonStates();
}//GEN-LAST:event_addWordButtonActionPerformed }//GEN-LAST:event_addWordButtonActionPerformed
@ -433,6 +452,7 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
tableModel.deleteSelected(keywordTable.getSelectedRows()); tableModel.deleteSelected(keywordTable.getSelectedRows());
XmlKeywordSearchList.getCurrent().addList(currentKeywordList); XmlKeywordSearchList.getCurrent().addList(currentKeywordList);
setButtonStates(); setButtonStates();
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} }
}//GEN-LAST:event_deleteWordButtonActionPerformed }//GEN-LAST:event_deleteWordButtonActionPerformed
@ -495,7 +515,13 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
currentKeywordList.setIngestMessages(ingestMessagesCheckbox.isSelected()); currentKeywordList.setIngestMessages(ingestMessagesCheckbox.isSelected());
XmlKeywordSearchList updater = XmlKeywordSearchList.getCurrent(); XmlKeywordSearchList updater = XmlKeywordSearchList.getCurrent();
updater.addList(currentKeywordList); updater.addList(currentKeywordList);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_ingestMessagesCheckboxActionPerformed }//GEN-LAST:event_ingestMessagesCheckboxActionPerformed
private void deleteListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_deleteListButtonActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_deleteListButtonActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JPanel addKeywordPanel; private javax.swing.JPanel addKeywordPanel;
private javax.swing.JButton addWordButton; private javax.swing.JButton addWordButton;

View File

@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.util.List; import java.util.List;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
@ -109,6 +110,18 @@ final class GlobalListSettingsPanel extends javax.swing.JPanel implements Option
mainSplitPane.revalidate(); mainSplitPane.revalidate();
mainSplitPane.repaint(); mainSplitPane.repaint();
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
listsManagementPanel.addPropertyChangeListener(l);
editListPanel.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
listsManagementPanel.removePropertyChangeListener(l);
editListPanel.removePropertyChangeListener(l);
}
@Override @Override
public void store() { public void store() {

View File

@ -19,17 +19,20 @@
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import javax.swing.JFileChooser; import javax.swing.JFileChooser;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.event.ListSelectionListener; import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
/** /**
* A panel to manage all keyword lists created/imported in Autopsy. * A panel to manage all keyword lists created/imported in Autopsy.
@ -38,6 +41,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
private Logger logger = Logger.getLogger(GlobalListsManagementPanel.class.getName()); private Logger logger = Logger.getLogger(GlobalListsManagementPanel.class.getName());
private KeywordListTableModel tableModel; private KeywordListTableModel tableModel;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
GlobalListsManagementPanel() { GlobalListsManagementPanel() {
tableModel = new KeywordListTableModel(); tableModel = new KeywordListTableModel();
@ -74,6 +78,16 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
* listsTable.getSelectionModel().clearSelection(); } } } }); * listsTable.getSelectionModel().clearSelection(); } } } });
*/ */
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
@ -197,6 +211,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
listsTable.getSelectionModel().addSelectionInterval(i, i); listsTable.getSelectionModel().addSelectionInterval(i, i);
} }
} }
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_newListButtonActionPerformed }//GEN-LAST:event_newListButtonActionPerformed
private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed private void importButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_importButtonActionPerformed
@ -291,6 +306,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
} }
} }
} }
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_importButtonActionPerformed }//GEN-LAST:event_importButtonActionPerformed
private void listsTableKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_listsTableKeyPressed private void listsTableKeyPressed(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_listsTableKeyPressed
if (evt.getKeyCode() == KeyEvent.VK_DELETE) { if (evt.getKeyCode() == KeyEvent.VK_DELETE) {
@ -300,6 +316,7 @@ class GlobalListsManagementPanel extends javax.swing.JPanel implements OptionsPa
} else if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) { } else if (KeywordSearchUtil.displayConfirmDialog(NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.title"), NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel1.customizeComponents.body"), KeywordSearchUtil.DIALOG_MESSAGE_TYPE.WARN)) {
String listName = (String) listsTable.getModel().getValueAt(selected[0], 0); String listName = (String) listsTable.getModel().getValueAt(selected[0], 0);
XmlKeywordSearchList.getCurrent().deleteList(listName); XmlKeywordSearchList.getCurrent().deleteList(listName);
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
} else { } else {
return; return;
} }

View File

@ -21,11 +21,14 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.awt.GridLayout; import java.awt.GridLayout;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.swing.JCheckBox; import javax.swing.JCheckBox;
import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.StringExtract; import org.sleuthkit.autopsy.coreutils.StringExtract;
import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT;
@ -39,6 +42,7 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem
private final Map<String, StringExtract.StringExtractUnicodeTable.SCRIPT> scripts = new HashMap<>(); private final Map<String, StringExtract.StringExtractUnicodeTable.SCRIPT> scripts = new HashMap<>();
private ActionListener updateLanguagesAction; private ActionListener updateLanguagesAction;
private List<SCRIPT> toUpdate; private List<SCRIPT> toUpdate;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
KeywordSearchGlobalLanguageSettingsPanel() { KeywordSearchGlobalLanguageSettingsPanel() {
initComponents(); initComponents();
@ -65,6 +69,16 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem
reloadScriptsCheckBoxes(); reloadScriptsCheckBoxes();
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
private void activateScriptsCheckboxes(boolean activate) { private void activateScriptsCheckboxes(boolean activate) {
final int components = checkPanel.getComponentCount(); final int components = checkPanel.getComponentCount();
for (int i = 0; i < components; ++i) { for (int i = 0; i < components; ++i) {
@ -87,6 +101,12 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem
for (StringExtract.StringExtractUnicodeTable.SCRIPT s : supportedScripts) { for (StringExtract.StringExtractUnicodeTable.SCRIPT s : supportedScripts) {
String text = getLangText(s); String text = getLangText(s);
JCheckBox ch = new JCheckBox(text); JCheckBox ch = new JCheckBox(text);
ch.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}
});
ch.addActionListener(updateLanguagesAction); ch.addActionListener(updateLanguagesAction);
checkPanel.add(ch); checkPanel.add(ch);
ch.setSelected(false); ch.setSelected(false);
@ -227,6 +247,7 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem
boolean selected = this.enableUTF8Checkbox.isSelected(); boolean selected = this.enableUTF8Checkbox.isSelected();
activateScriptsCheckboxes(selected || this.enableUTF16Checkbox.isSelected()); activateScriptsCheckboxes(selected || this.enableUTF16Checkbox.isSelected());
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_enableUTF8CheckboxActionPerformed }//GEN-LAST:event_enableUTF8CheckboxActionPerformed
@ -235,6 +256,7 @@ class KeywordSearchGlobalLanguageSettingsPanel extends javax.swing.JPanel implem
boolean selected = this.enableUTF16Checkbox.isSelected(); boolean selected = this.enableUTF16Checkbox.isSelected();
activateScriptsCheckboxes(selected || this.enableUTF8Checkbox.isSelected()); activateScriptsCheckboxes(selected || this.enableUTF8Checkbox.isSelected());
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_enableUTF16CheckboxActionPerformed }//GEN-LAST:event_enableUTF16CheckboxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables

View File

@ -120,6 +120,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="skipNSRLCheckBoxActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="filesIndexedLabel"> <Component class="javax.swing.JLabel" name="filesIndexedLabel">
<Properties> <Properties>
@ -186,6 +189,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="timeRadioButton1ActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="timeRadioButton2"> <Component class="javax.swing.JRadioButton" name="timeRadioButton2">
<Properties> <Properties>
@ -196,6 +202,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="timeRadioButton2ActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="timeRadioButton3"> <Component class="javax.swing.JRadioButton" name="timeRadioButton3">
<Properties> <Properties>
@ -206,6 +215,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="timeRadioButton3ActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="timeRadioButton4"> <Component class="javax.swing.JRadioButton" name="timeRadioButton4">
<Properties> <Properties>
@ -216,6 +228,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="timeRadioButton4ActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JCheckBox" name="showSnippetsCB"> <Component class="javax.swing.JCheckBox" name="showSnippetsCB">
<Properties> <Properties>
@ -223,6 +238,9 @@
<ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/> <ResourceString bundle="org/sleuthkit/autopsy/keywordsearch/Bundle.properties" key="KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
</Property> </Property>
</Properties> </Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showSnippetsCBActionPerformed"/>
</Events>
</Component> </Component>
<Component class="javax.swing.JRadioButton" name="timeRadioButton5"> <Component class="javax.swing.JRadioButton" name="timeRadioButton5">
<Properties> <Properties>

View File

@ -20,9 +20,11 @@ package org.sleuthkit.autopsy.keywordsearch;
import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.logging.Level; import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.netbeans.spi.options.OptionsPanelController;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager; import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.autopsy.keywordsearch.KeywordSearchIngestModule.UpdateFrequency; import org.sleuthkit.autopsy.keywordsearch.KeywordSearchIngestModule.UpdateFrequency;
@ -32,6 +34,7 @@ import org.sleuthkit.autopsy.keywordsearch.KeywordSearchIngestModule.UpdateFrequ
class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implements OptionsPanel { class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implements OptionsPanel {
private final Logger logger = Logger.getLogger(KeywordSearchGlobalSearchSettingsPanel.class.getName()); private final Logger logger = Logger.getLogger(KeywordSearchGlobalSearchSettingsPanel.class.getName());
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
/** /**
* Creates new form KeywordSearchConfigurationPanel2 * Creates new form KeywordSearchConfigurationPanel2
@ -73,6 +76,16 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen
} }
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
pcs.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
pcs.removePropertyChangeListener(l);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always
@ -102,6 +115,11 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen
skipNSRLCheckBox.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.text")); // NOI18N skipNSRLCheckBox.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.text")); // NOI18N
skipNSRLCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText")); // NOI18N skipNSRLCheckBox.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.skipNSRLCheckBox.toolTipText")); // NOI18N
skipNSRLCheckBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
skipNSRLCheckBoxActionPerformed(evt);
}
});
filesIndexedLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.filesIndexedLabel.text")); // NOI18N filesIndexedLabel.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.filesIndexedLabel.text")); // NOI18N
@ -120,17 +138,42 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen
timeRadioButton1.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text")); // NOI18N timeRadioButton1.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.text")); // NOI18N
timeRadioButton1.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText")); // NOI18N timeRadioButton1.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton1.toolTipText")); // NOI18N
timeRadioButton1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
timeRadioButton1ActionPerformed(evt);
}
});
timeRadioButton2.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text")); // NOI18N timeRadioButton2.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.text")); // NOI18N
timeRadioButton2.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText")); // NOI18N timeRadioButton2.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton2.toolTipText")); // NOI18N
timeRadioButton2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
timeRadioButton2ActionPerformed(evt);
}
});
timeRadioButton3.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.text")); // NOI18N timeRadioButton3.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.text")); // NOI18N
timeRadioButton3.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText")); // NOI18N timeRadioButton3.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton3.toolTipText")); // NOI18N
timeRadioButton3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
timeRadioButton3ActionPerformed(evt);
}
});
timeRadioButton4.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1")); // NOI18N timeRadioButton4.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.text_1")); // NOI18N
timeRadioButton4.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText")); // NOI18N timeRadioButton4.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton4.toolTipText")); // NOI18N
timeRadioButton4.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
timeRadioButton4ActionPerformed(evt);
}
});
showSnippetsCB.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text")); // NOI18N showSnippetsCB.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.showSnippetsCB.text")); // NOI18N
showSnippetsCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
showSnippetsCBActionPerformed(evt);
}
});
timeRadioButton5.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text")); // NOI18N timeRadioButton5.setText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.text")); // NOI18N
timeRadioButton5.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText")); // NOI18N timeRadioButton5.setToolTipText(org.openide.util.NbBundle.getMessage(KeywordSearchGlobalSearchSettingsPanel.class, "KeywordSearchGlobalSearchSettingsPanel.timeRadioButton5.toolTipText")); // NOI18N
@ -219,9 +262,33 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen
}// </editor-fold>//GEN-END:initComponents }// </editor-fold>//GEN-END:initComponents
private void timeRadioButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton5ActionPerformed private void timeRadioButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton5ActionPerformed
// TODO add your handling code here: pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_timeRadioButton5ActionPerformed }//GEN-LAST:event_timeRadioButton5ActionPerformed
private void skipNSRLCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_skipNSRLCheckBoxActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_skipNSRLCheckBoxActionPerformed
private void showSnippetsCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_showSnippetsCBActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_showSnippetsCBActionPerformed
private void timeRadioButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton1ActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_timeRadioButton1ActionPerformed
private void timeRadioButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton2ActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_timeRadioButton2ActionPerformed
private void timeRadioButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton3ActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_timeRadioButton3ActionPerformed
private void timeRadioButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_timeRadioButton4ActionPerformed
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, null, null);
}//GEN-LAST:event_timeRadioButton4ActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables // Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel chunksLabel; private javax.swing.JLabel chunksLabel;
private javax.swing.JLabel chunksValLabel; private javax.swing.JLabel chunksValLabel;
@ -297,22 +364,22 @@ class KeywordSearchGlobalSearchSettingsPanel extends javax.swing.JPanel implemen
KeywordSearch.addNumIndexedFilesChangeListener( KeywordSearch.addNumIndexedFilesChangeListener(
new PropertyChangeListener() { new PropertyChangeListener() {
@Override @Override
public void propertyChange(PropertyChangeEvent evt) { public void propertyChange(PropertyChangeEvent evt) {
String changed = evt.getPropertyName(); String changed = evt.getPropertyName();
Object newValue = evt.getNewValue(); Object newValue = evt.getNewValue();
if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) { if (changed.equals(KeywordSearch.NUM_FILES_CHANGE_EVT)) {
int newFilesIndexed = ((Integer) newValue).intValue(); int newFilesIndexed = ((Integer) newValue).intValue();
filesIndexedValue.setText(Integer.toString(newFilesIndexed)); filesIndexedValue.setText(Integer.toString(newFilesIndexed));
try { try {
chunksValLabel.setText(Integer.toString(KeywordSearch.getServer().queryNumIndexedChunks())); chunksValLabel.setText(Integer.toString(KeywordSearch.getServer().queryNumIndexedChunks()));
} catch (KeywordSearchModuleException | NoOpenCoreException ex) { } catch (KeywordSearchModuleException | NoOpenCoreException ex) {
logger.log(Level.WARNING, "Could not get number of indexed chunks"); //NON-NLS logger.log(Level.WARNING, "Could not get number of indexed chunks"); //NON-NLS
}
}
} }
}); }
}
});
} }
} }

View File

@ -18,6 +18,7 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.beans.PropertyChangeListener;
import org.openide.util.NbBundle; import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel; import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel; import org.sleuthkit.autopsy.ingest.IngestModuleGlobalSettingsPanel;
@ -50,6 +51,20 @@ final class KeywordSearchGlobalSettingsPanel extends IngestModuleGlobalSettingsP
generalPanel, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.genTabToolTip"), 2); generalPanel, NbBundle.getMessage(this.getClass(), "KeywordSearchConfigurationPanel.customizeComponents.genTabToolTip"), 2);
} }
@Override
public void addPropertyChangeListener(PropertyChangeListener l) {
listsPanel.addPropertyChangeListener(l);
languagesPanel.addPropertyChangeListener(l);
generalPanel.addPropertyChangeListener(l);
}
@Override
public void removePropertyChangeListener(PropertyChangeListener l) {
listsPanel.removePropertyChangeListener(l);
languagesPanel.removePropertyChangeListener(l);
generalPanel.removePropertyChangeListener(l);
}
/** /**
* This method is called from within the constructor to initialize the form. * This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always * WARNING: Do NOT modify this code. The content of this method is always

View File

@ -18,15 +18,13 @@
*/ */
package org.sleuthkit.autopsy.keywordsearch; package org.sleuthkit.autopsy.keywordsearch;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener; import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport; import java.beans.PropertyChangeSupport;
import javax.swing.JComponent; import javax.swing.JComponent;
import org.netbeans.spi.options.OptionsPanelController; import org.netbeans.spi.options.OptionsPanelController;
import org.openide.util.HelpCtx; import org.openide.util.HelpCtx;
import org.openide.util.Lookup; import org.openide.util.Lookup;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.MessageNotifyUtil;
import java.util.logging.Level;
import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.Logger;
@OptionsPanelController.TopLevelRegistration( @OptionsPanelController.TopLevelRegistration(
@ -42,18 +40,33 @@ public final class KeywordSearchOptionsPanelController extends OptionsPanelContr
private boolean changed; private boolean changed;
private static final Logger logger = Logger.getLogger(KeywordSearchGlobalSettingsPanel.class.getName()); private static final Logger logger = Logger.getLogger(KeywordSearchGlobalSettingsPanel.class.getName());
/**
* Component should load its data here.
*/
@Override @Override
public void update() { public void update() {
getPanel().load(); getPanel().load();
changed = false; changed = false;
} }
/**
* This method is called when both the Ok and Apply buttons are pressed. It
* applies to any of the panels that have been opened in the process of
* using the options pane.
*/
@Override @Override
public void applyChanges() { public void applyChanges() {
getPanel().store(); if (changed) {
changed = false; getPanel().store();
changed = false;
}
} }
/**
* This method is called when the Cancel button is pressed. It applies to
* any of the panels that have been opened in the process of using the
* options pane.
*/
@Override @Override
public void cancel() { public void cancel() {
getPanel().cancel(); getPanel().cancel();
@ -64,6 +77,12 @@ public final class KeywordSearchOptionsPanelController extends OptionsPanelContr
return getPanel().valid(); return getPanel().valid();
} }
/**
* Used to determine whether any changes have been made to this controller's
* panel.
*
* @return Whether or not a change has been made.
*/
@Override @Override
public boolean isChanged() { public boolean isChanged() {
return changed; return changed;
@ -92,6 +111,14 @@ public final class KeywordSearchOptionsPanelController extends OptionsPanelContr
private KeywordSearchGlobalSettingsPanel getPanel() { private KeywordSearchGlobalSettingsPanel getPanel() {
if (panel == null) { if (panel == null) {
panel = new KeywordSearchGlobalSettingsPanel(); panel = new KeywordSearchGlobalSettingsPanel();
panel.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getPropertyName().equals(OptionsPanelController.PROP_CHANGED)) {
changed();
}
}
});
} }
return panel; return panel;
} }
@ -99,25 +126,8 @@ public final class KeywordSearchOptionsPanelController extends OptionsPanelContr
void changed() { void changed() {
if (!changed) { if (!changed) {
changed = true; changed = true;
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
try {
pcs.firePropertyChange(OptionsPanelController.PROP_CHANGED, false, true);
} catch (Exception e) {
logger.log(Level.SEVERE, "KeywordSearchOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "KeywordSearchOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "KeywordSearchOptionsPanelController.moduleErr.msg1"),
MessageNotifyUtil.MessageType.ERROR);
}
}
try {
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} catch (Exception e) {
logger.log(Level.SEVERE, "KeywordSearchOptionsPanelController listener threw exception", e); //NON-NLS
MessageNotifyUtil.Notify.show(
NbBundle.getMessage(this.getClass(), "KeywordSearchOptionsPanelController.moduleErr"),
NbBundle.getMessage(this.getClass(), "KeywordSearchOptionsPanelController.moduleErr.msg2"),
MessageNotifyUtil.MessageType.ERROR);
} }
pcs.firePropertyChange(OptionsPanelController.PROP_VALID, null, null);
} }
} }

View File

@ -136,9 +136,9 @@ Visualization Area: Counts View
------------------------------- -------------------------------
The Counts View shows a stacked bar chart with time periods along the x-axis and event counts along the y-axis. The height of each bar represents the number of events that occurred in that time period. The different colored segments represent different event types. Right clicking the bars brings up a context menu with selection and zooming actions. The Counts View shows a stacked bar chart with time periods along the x-axis and event counts along the y-axis. The height of each bar represents the number of events that occurred in that time period. The different colored segments represent different event types. Right clicking the bars brings up a context menu with selection and zooming actions.
The only setting specific to the Counts View is what kind of vertical scale to use. The default linear scale is good for many use cases. When this scale is selected, the height of the bars represents the counts in a linear, one-to-one fashion, and the y-axis is labelled with values. When the range of count values is very large, date ranges with relatively low counts have a bar that may be too small to see. To help avoid the misperception of this as no events, the labels for time periods with events are bold relative to the labels for time periods with no events. The only setting specific to the Counts View is what kind of vertical scale to use. The default linear scale is good for many use cases. When this scale is selected, the height of the bars represents the counts in a linear, one-to-one fashion, and the y-axis is labeled with values. When the range of count values is very large, date ranges with relatively low counts have a bar that may be too small to see. To help avoid the misperception of this as no events, the labels for time periods with events are bold relative to the labels for time periods with no events.
To see the events when the bar for a period is too small, there are three options: adjust the window size so that the visualization area has more vertical space, adjust the time range shown so that time periods with relatively much larger bars are excluded, or adjust the scale setting to square root or logarithmic. The square root and logarithmic scales represent the number of events in a non linear way that compresses the difference between very large and very small numbers. Note that even with the logarithmic scale, an extremely large difference in counts may still produce bars too small to see. In this case the only option may be to exclude events to reduce the difference in counts. To see the events when the bar for a period is too small, there are three options: adjust the window size so that the visualization area has more vertical space, adjust the time range shown so that time periods with relatively much larger bars are excluded, or adjust the scale setting to logarithmic. The logarithmic scale represents the number of events in a non-linear way that compresses the difference between very large and very small numbers. Note that even with the logarithmic scale, an extremely large difference in counts may still produce bars too small to see. In this case the only option may be to exclude events to reduce the difference in counts.
Because the square root and logarithmic scales are applied to each event type separately, the height of the combined bar is not very meaningful, and to emphasize this, no labels are shown on the y-axis. The non-linear scales should be used to quickly compare the counts relative across _time within a type, or across types for one time period, but not both_. The exact numbers (available in tooltips or the result viewer) should be used for absolute comparisons. Use the non-linear scales with care. Because the logarithmic scale is applied to each event type separately, the height of the combined bar is not very meaningful, and to emphasize this, no labels are shown on the y-axis. The logarithmic scale should be used to quickly compare the counts relative across _time within a type, or across types for one time period, but not both_. The exact numbers (available in tooltips or the result viewer) should be used for absolute comparisons. Use the logarithmic scales with care.