diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
index 22f3d74c28..57670e96aa 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.form
@@ -16,13 +16,25 @@
-
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -32,176 +44,34 @@
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -215,5 +85,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
index 041cd3fc31..62af39f3a2 100644
--- a/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/casemodule/CueBannerPanel.java
@@ -20,6 +20,9 @@
package org.sleuthkit.autopsy.casemodule;
import java.awt.*;
+import java.awt.Dialog;
+import java.awt.Dimension;
+import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.ImageIcon;
@@ -64,16 +67,16 @@ public class CueBannerPanel extends javax.swing.JPanel {
// //GEN-BEGIN:initComponents
private void initComponents() {
- closeButton = new javax.swing.JButton();
- editorPanel = new javax.swing.JPanel();
+ autopsyLogo = new javax.swing.JLabel();
+ this.autopsyLogo.setText("");
newCaseButton = new javax.swing.JButton();
openRecentButton = new javax.swing.JButton();
createNewLabel = new javax.swing.JLabel();
openRecentLabel = new javax.swing.JLabel();
openCaseButton = new javax.swing.JButton();
openLabel = new javax.swing.JLabel();
- autopsyLogo = new javax.swing.JLabel();
- this.autopsyLogo.setText("");
+ closeButton = new javax.swing.JButton();
+ jSeparator1 = new javax.swing.JSeparator();
closeButton.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.closeButton.text")); // NOI18N
@@ -82,7 +85,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
newCaseButton.setBorder(null);
newCaseButton.setBorderPainted(false);
newCaseButton.setContentAreaFilled(false);
- newCaseButton.setPreferredSize(new java.awt.Dimension(70, 70));
+ newCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
newCaseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
newCaseButtonActionPerformed(evt);
@@ -94,7 +97,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
openRecentButton.setBorder(null);
openRecentButton.setBorderPainted(false);
openRecentButton.setContentAreaFilled(false);
- openRecentButton.setPreferredSize(new java.awt.Dimension(70, 70));
+ openRecentButton.setPreferredSize(new java.awt.Dimension(64, 64));
openRecentButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openRecentButtonActionPerformed(evt);
@@ -113,7 +116,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
openCaseButton.setBorderPainted(false);
openCaseButton.setContentAreaFilled(false);
openCaseButton.setMargin(new java.awt.Insets(1, 1, 1, 1));
- openCaseButton.setPreferredSize(new java.awt.Dimension(70, 70));
+ openCaseButton.setPreferredSize(new java.awt.Dimension(64, 64));
openCaseButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
openCaseButtonActionPerformed(evt);
@@ -123,46 +126,10 @@ public class CueBannerPanel extends javax.swing.JPanel {
openLabel.setFont(openLabel.getFont().deriveFont(Font.PLAIN, 13));
openLabel.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.openLabel.text")); // NOI18N
- javax.swing.GroupLayout editorPanelLayout = new javax.swing.GroupLayout(editorPanel);
- editorPanel.setLayout(editorPanelLayout);
- editorPanelLayout.setHorizontalGroup(
- editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(editorPanelLayout.createSequentialGroup()
- .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(editorPanelLayout.createSequentialGroup()
- .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(createNewLabel))
- .addGroup(editorPanelLayout.createSequentialGroup()
- .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(openRecentLabel))
- .addGroup(editorPanelLayout.createSequentialGroup()
- .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addComponent(openLabel)))
- .addContainerGap(60, Short.MAX_VALUE))
- );
- editorPanelLayout.setVerticalGroup(
- editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addGroup(editorPanelLayout.createSequentialGroup()
- .addGap(32, 32, 32)
- .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
- .addComponent(createNewLabel)
- .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
- .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
- .addComponent(openRecentLabel)
- .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addGap(0, 0, Short.MAX_VALUE)
- .addGroup(editorPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
- .addComponent(openLabel)
- .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addContainerGap(25, Short.MAX_VALUE))
- );
autopsyLogo.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/casemodule/welcome_logo.png"))); // NOI18N NON-NLS
autopsyLogo.setText(org.openide.util.NbBundle.getMessage(CueBannerPanel.class, "CueBannerPanel.autopsyLogo.text")); // NOI18N
+ jSeparator1.setOrientation(javax.swing.SwingConstants.VERTICAL);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
@@ -171,23 +138,44 @@ public class CueBannerPanel extends javax.swing.JPanel {
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(autopsyLogo)
- .addGap(29, 29, 29)
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
- .addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addComponent(closeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
- .addContainerGap())
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 5, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(createNewLabel)
+ .addComponent(openRecentLabel)
+ .addComponent(openLabel))
+ .addComponent(closeButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.PREFERRED_SIZE, 73, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addGap(15, 15, 15))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
- .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
- .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
- .addComponent(editorPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
- .addGap(18, 18, 18)
- .addComponent(closeButton)))
- .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(newCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 56, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(createNewLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(openRecentButton, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(openRecentLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.CENTER)
+ .addComponent(openCaseButton, javax.swing.GroupLayout.PREFERRED_SIZE, 58, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addComponent(openLabel))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(closeButton))
+ .addComponent(jSeparator1)
+ .addComponent(autopsyLogo, javax.swing.GroupLayout.PREFERRED_SIZE, 257, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addContainerGap())
);
}// //GEN-END:initComponents
@@ -235,7 +223,7 @@ public class CueBannerPanel extends javax.swing.JPanel {
private javax.swing.JLabel autopsyLogo;
private javax.swing.JButton closeButton;
private javax.swing.JLabel createNewLabel;
- private javax.swing.JPanel editorPanel;
+ private javax.swing.JSeparator jSeparator1;
private javax.swing.JButton newCaseButton;
private javax.swing.JButton openCaseButton;
private javax.swing.JLabel openLabel;
diff --git a/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png b/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png
index 5bac99e039..220f6f3394 100644
Binary files a/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png and b/Core/src/org/sleuthkit/autopsy/casemodule/welcome_logo.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java
index 0b6c28e7e8..96f38aaae5 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/DataContentViewerMedia.java
@@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
- * Copyright 2011-2013 Basis Technology Corp.
+ * Copyright 2011-2014 Basis Technology Corp.
* Contact: carrier sleuthkit org
*s
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -21,22 +21,20 @@ package org.sleuthkit.autopsy.corecomponents;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.Dimension;
-import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.Set;
+import java.util.SortedSet;
+import java.util.TreeSet;
import java.util.logging.Level;
-import javax.imageio.ImageIO;
-
-import org.openide.util.NbBundle;
-import org.sleuthkit.autopsy.coreutils.Logger;
import org.openide.nodes.Node;
+import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
import org.openide.util.lookup.ServiceProviders;
import org.sleuthkit.autopsy.corecomponentinterfaces.DataContentViewer;
import org.sleuthkit.autopsy.coreutils.ImageUtils;
+import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.AbstractFile;
-import org.sleuthkit.datamodel.BlackboardAttribute;
-import org.sleuthkit.datamodel.TskCoreException;
+import org.sleuthkit.datamodel.AbstractFile.MimeMatchEnum;
import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
/**
@@ -47,14 +45,15 @@ import org.sleuthkit.datamodel.TskData.TSK_FS_NAME_FLAG_ENUM;
})
public class DataContentViewerMedia extends javax.swing.JPanel implements DataContentViewer {
- private static final String[] AUDIO_EXTENSIONS = new String[]{".mp3", ".wav", ".wma"}; //NON-NLS
+ private static final Set AUDIO_EXTENSIONS = new TreeSet<>(Arrays.asList(".mp3", ".wav", ".wma")); //NON-NLS
private static final Logger logger = Logger.getLogger(DataContentViewerMedia.class.getName());
private AbstractFile lastFile;
//UI
private final MediaViewVideoPanel videoPanel;
- private final String[] videoExtensions; // get them from the panel
- private String[] imageExtensions; // use javafx supported
- private final List supportedMimes;
+ private final SortedSet videoExtensions; // get them from the panel
+ private final SortedSet imageExtensions;
+ private final SortedSet videoMimes;
+ private final SortedSet imageMimes;
private final MediaViewImagePanel imagePanel;
private boolean videoPanelInited;
private boolean imagePanelInited;
@@ -70,34 +69,24 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
// get the right panel for our platform
videoPanel = MediaViewVideoPanel.createVideoPanel();
+ videoPanelInited = videoPanel.isInited();
+ videoExtensions = new TreeSet<>(Arrays.asList(videoPanel.getExtensions()));
+ videoMimes = new TreeSet<>(videoPanel.getMimeTypes());
imagePanel = new MediaViewImagePanel();
- videoPanelInited = videoPanel.isInited();
imagePanelInited = imagePanel.isInited();
-
- videoExtensions = videoPanel.getExtensions();
- supportedMimes = videoPanel.getMimeTypes();
+ imageMimes = new TreeSet<>(imagePanel.getMimeTypes());
+ imageExtensions = new TreeSet<>(imagePanel.getExtensions());
+
customizeComponents();
logger.log(Level.INFO, "Created MediaView instance: " + this); //NON-NLS
}
private void customizeComponents() {
- //initialize supported image types
- //TODO use mime-types instead once we have support
- String[] fxSupportedImagesSuffixes = ImageIO.getReaderFileSuffixes();
- imageExtensions = new String[fxSupportedImagesSuffixes.length];
- //logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
- for (int i = 0; i < fxSupportedImagesSuffixes.length; ++i) {
- String suffix = fxSupportedImagesSuffixes[i];
- //logger.log(Level.INFO, "suffix: " + suffix);
- imageExtensions[i] = "." + suffix;
- }
-
add(imagePanel, IMAGE_VIEWER_LAYER);
add(videoPanel, VIDEO_VIEWER_LAYER);
- switchPanels(false);
-
+ showVideoPanel(false);
}
/**
@@ -137,19 +126,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
final Dimension dims = DataContentViewerMedia.this.getSize();
//logger.info("setting node on media viewer"); //NON-NLS
- if (imagePanelInited && containsExt(file.getName(), imageExtensions)) {
+ if (imagePanelInited && isImageSupported(file)) {
imagePanel.showImageFx(file, dims);
- this.switchPanels(false);
- } else if (imagePanelInited && ImageUtils.isJpegFileHeader(file)) {
-
- imagePanel.showImageFx(file, dims);
- this.switchPanels(false);
-
- } else if (videoPanelInited
- && containsMimeType(selectedNode,supportedMimes)&&(containsExt(file.getName(), videoExtensions) || containsExt(file.getName(), AUDIO_EXTENSIONS))) {
+ this.showVideoPanel(false);
+ } else if (videoPanelInited && isVideoSupported(file)) {
videoPanel.setupVideo(file, dims);
- switchPanels(true);
-
+ this.showVideoPanel(true);
}
} catch (Exception e) {
logger.log(Level.SEVERE, "Exception while setting node", e); //NON-NLS
@@ -161,7 +143,7 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
*
* @param showVideo true if video panel, false if image panel
*/
- private void switchPanels(boolean showVideo) {
+ private void showVideoPanel(boolean showVideo) {
CardLayout layout = (CardLayout) this.getLayout();
if (showVideo) {
layout.show(this, VIDEO_VIEWER_LAYER);
@@ -197,6 +179,57 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
lastFile = null;
}
+ /**
+ *
+ * @param file
+ * @return True if a video file that can be displayed
+ */
+ private boolean isVideoSupported(AbstractFile file) {
+ String name = file.getName().toLowerCase();
+
+ if ((containsExt(name, AUDIO_EXTENSIONS) || containsExt(name, videoExtensions)) &&
+ (!videoMimes.isEmpty() && file.isMimeType(videoMimes) == MimeMatchEnum.TRUE)) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ *
+ * @param file
+ * @return True if an image file that can be displayed
+ */
+ private boolean isImageSupported(AbstractFile file) {
+ String name = file.getName().toLowerCase();
+
+ // blackboard
+ if (!imageMimes.isEmpty()) {
+ MimeMatchEnum mimeMatch = file.isMimeType(imageMimes);
+ if (mimeMatch == MimeMatchEnum.TRUE) {
+ return true;
+ }
+ else if (mimeMatch == MimeMatchEnum.FALSE) {
+ return false;
+ }
+ }
+
+ // extension
+ if (containsExt(name, imageExtensions)) {
+ return true;
+ }
+ // our own signature checks for important types
+ else if (ImageUtils.isJpegFileHeader(file)) {
+ return true;
+ }
+ else if (ImageUtils.isPngFileHeader(file)) {
+ return true;
+ }
+
+ //for gstreamer formats, check if initialized first, then
+ //support audio formats, and video formats
+ return false;
+ }
+
@Override
public boolean isSupported(Node node) {
if (node == null) {
@@ -211,23 +244,15 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
if (file.getSize() == 0) {
return false;
}
- String name = file.getName().toLowerCase();
+
if (imagePanelInited) {
- if (containsExt(name, imageExtensions)) {
+ if (isImageSupported(file))
return true;
- }
- else if (ImageUtils.isJpegFileHeader(file)) {
- return true;
- }
- //for gstreamer formats, check if initialized first, then
- //support audio formats, and video formats
}
if (videoPanelInited && videoPanel.isInited()) {
- if ((containsExt(name, AUDIO_EXTENSIONS)
- || containsExt(name, videoExtensions))&& containsMimeType(node,supportedMimes)) {
+ if (isVideoSupported(file))
return true;
- }
}
return false;
@@ -252,34 +277,12 @@ public class DataContentViewerMedia extends javax.swing.JPanel implements DataCo
}
- private static boolean containsExt(String name, String[] exts) {
+ private static boolean containsExt(String name, Set exts) {
int extStart = name.lastIndexOf(".");
String ext = "";
if (extStart != -1) {
ext = name.substring(extStart, name.length()).toLowerCase();
}
- return Arrays.asList(exts).contains(ext);
- }
- private static boolean containsMimeType(Node node, List mimeTypes) {
- if (mimeTypes.isEmpty()) {
- //this should return true for 32 bit autopsy with the gstreamer MimeTypes list being empty,
- //since mimetype detection is for java fx only. For 64 bit java fx, the code continues for Mimetype detection.
- return true;
- }
- AbstractFile file = node.getLookup().lookup(AbstractFile.class);
- try {
- ArrayList genInfoAttributes = file.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG);
- if (genInfoAttributes.isEmpty() == false) {
- for (BlackboardAttribute batt : genInfoAttributes) {
- if (mimeTypes.contains(batt.getValueString())) {
- return true;
- }
- }
- return false;
- }
- } catch (TskCoreException ex) {
- return false;
- }
- return false;
+ return exts.contains(ext);
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java
index 4f8b8e299e..0d9cc34d45 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/FXVideoPanel.java
@@ -80,9 +80,7 @@ import org.sleuthkit.datamodel.TskData;
public class FXVideoPanel extends MediaViewVideoPanel {
private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".mpg", ".mpeg"}; //NON-NLS
-
- static private final List supportedMimes = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
-
+ private static final List MIMETYPES = Arrays.asList("audio/x-aiff", "video/x-javafx", "video/x-flv", "application/vnd.apple.mpegurl", " audio/mpegurl", "audio/mpeg", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
private static final Logger logger = Logger.getLogger(MediaViewVideoPanel.class.getName());
private boolean fxInited = false;
@@ -825,6 +823,6 @@ public class FXVideoPanel extends MediaViewVideoPanel {
@Override
public List getMimeTypes() {
- return supportedMimes;
+ return MIMETYPES;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java
index 6201734fb6..db5f97b40f 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/GstVideoPanel.java
@@ -52,8 +52,6 @@ import org.gstreamer.elements.RGBDataSink;
import org.gstreamer.swing.VideoComponent;
import org.netbeans.api.progress.ProgressHandle;
import org.netbeans.api.progress.ProgressHandleFactory;
-import org.openide.DialogDisplayer;
-import org.openide.NotifyDescriptor;
import org.openide.util.Cancellable;
import org.openide.util.NbBundle;
import org.openide.util.lookup.ServiceProvider;
@@ -71,8 +69,8 @@ import org.sleuthkit.datamodel.TskData;
@ServiceProvider(service = FrameCapture.class)
})
public class GstVideoPanel extends MediaViewVideoPanel {
-
- private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
+ private static final String[] EXTENSIONS = new String[]{".mov", ".m4v", ".flv", ".mp4", ".3gp", ".avi", ".mpg", ".mpeg", ".wmv"}; //NON-NLS
+ private static final List MIMETYPES = Arrays.asList("video/quicktime", "audio/mpeg", "audio/x-mpeg", "video/mpeg", "video/x-mpeg", "audio/mpeg3", "audio/x-mpeg-3", "video/x-flv", "video/mp4", "audio/x-m4a", "video/x-m4v", "audio/x-wav"); //NON-NLS
private static final Logger logger = Logger.getLogger(GstVideoPanel.class.getName());
private boolean gstInited;
@@ -88,8 +86,8 @@ public class GstVideoPanel extends MediaViewVideoPanel {
private boolean autoTracking = false; // true if the slider is moving automatically
private final Object playbinLock = new Object(); // lock for synchronization of gstPlaybin2 player
private AbstractFile currentFile;
- private Set badVideoFiles = Collections.synchronizedSet(new HashSet());
- static private final List supportedMimes = Arrays.asList();
+ private final Set badVideoFiles = Collections.synchronizedSet(new HashSet());
+
/**
* Creates new form MediaViewVideoPanel
*/
@@ -806,6 +804,6 @@ public class GstVideoPanel extends MediaViewVideoPanel {
@Override
public List getMimeTypes() {
- return supportedMimes;
+ return MIMETYPES;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java
index 50d4461939..b1dfe667d5 100644
--- a/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java
+++ b/Core/src/org/sleuthkit/autopsy/corecomponents/MediaViewImagePanel.java
@@ -23,6 +23,9 @@ import java.awt.EventQueue;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
import java.util.logging.Level;
import javafx.application.Platform;
import javafx.embed.swing.JFXPanel;
@@ -33,7 +36,6 @@ import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javax.imageio.ImageIO;
import javax.swing.SwingUtilities;
-
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.corelibs.ScalrWrapper;
@@ -47,26 +49,30 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
* used with JavaFx image viewer only.
*/
public class MediaViewImagePanel extends javax.swing.JPanel {
-
private JFXPanel fxPanel;
private ImageView fxImageView;
private static final Logger logger = Logger.getLogger(MediaViewImagePanel.class.getName());
private boolean fxInited = false;
+
+ private final List supportedExtensions;
+ static private final List supportedMimes = Arrays.asList("image/jpeg", "image/png", "image/gif", "image/bmp");
/**
* Creates new form MediaViewImagePanel
*/
public MediaViewImagePanel() {
initComponents();
-
-
-
fxInited = org.sleuthkit.autopsy.core.Installer.isJavaFxInited();
-
-
if (fxInited) {
setupFx();
}
+
+ supportedExtensions = new ArrayList<>();
+ //logger.log(Level.INFO, "Supported image formats by javafx image viewer: ");
+ for (String suffix : ImageIO.getReaderFileSuffixes()) {
+ //logger.log(Level.INFO, "suffix: " + suffix);
+ supportedExtensions.add("." + suffix);
+ }
}
public boolean isInited() {
@@ -99,13 +105,8 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
// setVisible(true);
}
});
-
-
-
}
});
-
-
}
public void reset() {
@@ -207,6 +208,22 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
});
}
+
+ /**
+ * returns supported mime types
+ * @return
+ */
+ public List getMimeTypes() {
+ return supportedMimes;
+ }
+
+ /**
+ * returns supported extensions (each starting with .)
+ * @return
+ */
+ public List getExtensions() {
+ return supportedExtensions;
+ }
/**
* This method is called from within the constructor to initialize the form.
@@ -222,4 +239,4 @@ import org.sleuthkit.datamodel.ReadContentInputStream;
}// //GEN-END:initComponents
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
-}
+}
\ No newline at end of file
diff --git a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
index 69f357bc20..b5944cd2b4 100755
--- a/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
+++ b/Core/src/org/sleuthkit/autopsy/coreutils/ImageUtils.java
@@ -196,6 +196,32 @@ public class ImageUtils {
return (((fileHeaderBuffer[0] & 0xff) == 0xff) && ((fileHeaderBuffer[1] & 0xff) == 0xd8));
}
+ public static boolean isPngFileHeader(AbstractFile file) {
+ if (file.getSize() < 10) {
+ return false;
+ }
+
+ byte[] fileHeaderBuffer = new byte[8];
+ int bytesRead;
+ try {
+ bytesRead = file.read(fileHeaderBuffer, 0, 8);
+ } catch (TskCoreException ex) {
+ //ignore if can't read the first few bytes, not an image
+ return false;
+ }
+ if (bytesRead != 8) {
+ return false;
+ }
+ /*
+ * Check for the header. Since Java bytes are signed, we cast them
+ * to an int first.
+ */
+ return (((fileHeaderBuffer[1] & 0xff) == 0x50) && ((fileHeaderBuffer[2] & 0xff) == 0x4E) &&
+ ((fileHeaderBuffer[3] & 0xff) == 0x47) && ((fileHeaderBuffer[4] & 0xff) == 0x0D) &&
+ ((fileHeaderBuffer[5] & 0xff) == 0x0A) && ((fileHeaderBuffer[6] & 0xff) == 0x1A) &&
+ ((fileHeaderBuffer[7] & 0xff) == 0x0A));
+ }
+
private static Image generateAndSaveIcon(Content content, int iconSize) {
Image icon = null;
diff --git a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java
index 88c954af83..7a14d02610 100755
--- a/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/examples/SampleDataSourceIngestModule.java
@@ -99,8 +99,8 @@ class SampleDataSourceIngestModule implements DataSourceIngestModule {
// Get files by creation time.
long currentTime = System.currentTimeMillis() / 1000;
long minTime = currentTime - (14 * 24 * 60 * 60); // Go back two weeks.
- List otherFiles = sleuthkitCase.findFilesWhere("crtime > " + minTime);
- for (FsContent otherFile : otherFiles) {
+ List otherFiles = fileManager.findFiles(dataSource, "crtime > " + minTime);
+ for (AbstractFile otherFile : otherFiles) {
if (!skipKnownFiles || otherFile.getKnown() != TskData.FileKnown.KNOWN) {
++fileCount;
}
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java
index 34ddf4c603..71fb38b13a 100755
--- a/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/DataSourceIngestPipeline.java
@@ -39,31 +39,13 @@ final class DataSourceIngestPipeline {
this.job = job;
// Create an ingest module instance from each data source ingest module
- // template. Put the modules in a map of module class names to module
- // instances to facilitate loading the modules into the pipeline in the
- // sequence indicated by the ordered list of module class names that
- // will be obtained from the data source ingest pipeline configuration.
- Map modulesByClass = new HashMap<>();
+ // template.
for (IngestModuleTemplate template : moduleTemplates) {
if (template.isDataSourceIngestModuleTemplate()) {
DataSourceIngestModuleDecorator module = new DataSourceIngestModuleDecorator(template.createDataSourceIngestModule(), template.getModuleName());
- modulesByClass.put(module.getClassName(), module);
+ modules.add(module);
}
}
-
- // Add the ingest modules to the pipeline in the order indicated by the
- // data source ingest pipeline configuration, adding any additional
- // modules found in the global lookup, but not mentioned in the
- // configuration, to the end of the pipeline in arbitrary order.
- List pipelineConfig = IngestPipelinesConfiguration.getInstance().getDataSourceIngestPipelineConfig();
- for (String moduleClassName : pipelineConfig) {
- if (modulesByClass.containsKey(moduleClassName)) {
- modules.add(modulesByClass.remove(moduleClassName));
- }
- }
- for (DataSourceIngestModuleDecorator module : modulesByClass.values()) {
- modules.add(module);
- }
}
boolean isEmpty() {
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java b/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java
index 1a02e29e55..795ff0b1b9 100755
--- a/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/FileIngestPipeline.java
@@ -37,34 +37,12 @@ final class FileIngestPipeline {
// Create an ingest module instance from each file ingest module
// template.
- // current code uses the order pased in.
- // Commented out code relied on the XML configuration file for ordering.
- //Map modulesByClass = new HashMap<>();
for (IngestModuleTemplate template : moduleTemplates) {
if (template.isFileIngestModuleTemplate()) {
FileIngestModuleDecorator module = new FileIngestModuleDecorator(template.createFileIngestModule(), template.getModuleName());
modules.add(module);
- //modulesByClass.put(module.getClassName(), module);
}
}
-
- // Add the ingest modules to the pipeline in the order indicated by the
- // data source ingest pipeline configuration, adding any additional
- // modules found in the global lookup, but not mentioned in the
- // configuration, to the end of the pipeline in arbitrary order.
- /*List pipelineConfig = IngestPipelinesConfiguration.getInstance().getFileIngestPipelineConfig();
- for (String moduleClassName : pipelineConfig) {
- if (modulesByClass.containsKey(moduleClassName)) {
- modules.add(modulesByClass.remove(moduleClassName));
- }
- else {
- // @@@ add error message to flag renamed / removed modules
- }
- }
- for (FileIngestModuleDecorator module : modulesByClass.values()) {
- modules.add(module);
- }
- */
}
boolean isEmpty() {
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java
index b27afc2178..f9aaf17aef 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestJobConfigurator.java
@@ -125,9 +125,9 @@ public final class IngestJobConfigurator {
ModuleSettings.setConfigSetting(launcherContext, DISABLED_INGEST_MODULES_KEY, makeCommaSeparatedList(disabledModuleNames));
// Get the process unallocated space flag setting. If the setting does
- // not exist yet, default it to false.
+ // not exist yet, default it to true.
if (ModuleSettings.settingExists(launcherContext, PARSE_UNALLOC_SPACE_KEY) == false) {
- ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "false"); //NON-NLS
+ ModuleSettings.setConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY, "true"); //NON-NLS
}
boolean processUnallocatedSpace = Boolean.parseBoolean(ModuleSettings.getConfigSetting(launcherContext, PARSE_UNALLOC_SPACE_KEY));
diff --git a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
index 3ae631dd87..e7ce367a64 100644
--- a/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
+++ b/Core/src/org/sleuthkit/autopsy/ingest/IngestModuleFactoryLoader.java
@@ -22,6 +22,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.TreeMap;
import java.util.logging.Level;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
@@ -41,7 +42,7 @@ import org.sleuthkit.autopsy.modules.sevenzip.ArchiveFileExtractorModuleFactory;
import org.sleuthkit.autopsy.python.JythonModuleLoader;
/**
- * Discovers ingest module factories implemented in Java or Jython.
+ * Discovers and instantiates ingest module factories.
*/
final class IngestModuleFactoryLoader {
@@ -111,9 +112,13 @@ final class IngestModuleFactoryLoader {
}
}
- // Add any remaining non-core factories discovered. Order is not
- // guaranteed!
- factories.addAll(javaFactoriesByClass.values());
+ // Add any remaining non-core factories discovered. Order with an
+ // alphabetical sort by module display name.
+ TreeMap javaFactoriesSortedByName = new TreeMap<>();
+ for (IngestModuleFactory factory : javaFactoriesByClass.values()) {
+ javaFactoriesSortedByName.put(factory.getModuleDisplayName(), factory);
+ }
+ factories.addAll(javaFactoriesSortedByName.values());
// Add any ingest module factories implemented using Jython. Order is
// not guaranteed!
diff --git a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java
index 35c5685ca0..1d02f1c5d8 100644
--- a/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java
+++ b/Core/src/org/sleuthkit/autopsy/modules/sevenzip/SevenZipIngestModule.java
@@ -60,6 +60,8 @@ import org.sleuthkit.autopsy.ingest.IngestJobContext;
import org.sleuthkit.autopsy.casemodule.Case;
import org.sleuthkit.autopsy.ingest.ModuleDataEvent;
import org.sleuthkit.autopsy.ingest.IngestModuleReferenceCounter;
+import net.sf.sevenzipjbinding.ArchiveFormat;
+import static net.sf.sevenzipjbinding.ArchiveFormat.RAR;
/**
* 7Zip ingest module extracts supported archives, adds extracted DerivedFiles,
@@ -260,6 +262,53 @@ public final class SevenZipIngestModule implements FileIngestModule {
return false;
}
}
+
+ /**
+ * Check file extension and return appropriate input options for SevenZip.openInArchive()
+ *
+ * @param archiveFile file to check file extension
+ * @return input parameter for SevenZip.openInArchive()
+ */
+ private ArchiveFormat get7ZipOptions(AbstractFile archiveFile)
+ {
+ // try to get the file type from the BB
+ String detectedFormat = null;
+ try {
+ ArrayList attributes = archiveFile.getGenInfoAttributes(BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FILE_TYPE_SIG);
+ for (BlackboardAttribute attribute : attributes) {
+ detectedFormat = attribute.getValueString();
+ break;
+ }
+ } catch (TskCoreException ex) {
+ logger.log(Level.WARNING, "Couldn't obtain file attributes for file: " + archiveFile.toString(), ex);
+ }
+
+ if (detectedFormat == null) {
+ logger.log(Level.WARNING, "Could not detect format for file: " + archiveFile); //NON-NLS
+
+ // if we don't have attribute info then use file extension
+ String extension = archiveFile.getNameExtension();
+ if ("rar".equals(extension))
+ {
+ // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
+ // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
+ return RAR;
+ }
+
+ // Otherwise open the archive using 7zip's built-in auto-detect functionality
+ return null;
+ }
+ else if (detectedFormat.contains("application/x-rar-compressed"))
+ {
+ // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
+ // it will be opened incorrectly when using 7zip's built-in auto-detect functionality
+ return RAR;
+ }
+
+ // Otherwise open the archive using 7zip's built-in auto-detect functionality
+ return null;
+ }
+
/**
* Unpack the file to local folder and return a list of derived files
@@ -301,8 +350,12 @@ public final class SevenZipIngestModule implements FileIngestModule {
boolean progressStarted = false;
try {
stream = new SevenZipContentReadStream(new ReadContentInputStream(archiveFile));
- inArchive = SevenZip.openInArchive(null, // autodetect archive type
- stream);
+
+ // for RAR files we need to open them explicitly as RAR. Otherwise, if there is a ZIP archive inside RAR archive
+ // it will be opened incorrectly when using 7zip's built-in auto-detect functionality.
+ // All other archive formats are still opened using 7zip built-in auto-detect functionality.
+ ArchiveFormat options = get7ZipOptions(archiveFile);
+ inArchive = SevenZip.openInArchive(options, stream);
int numItems = inArchive.getNumberOfItems();
logger.log(Level.INFO, "Count of items in archive: {0}: {1}", new Object[]{archiveFile.getName(), numItems}); //NON-NLS
diff --git a/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png b/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png
index 5bac99e039..d73d6314c2 100644
Binary files a/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png and b/Core/src/org/sleuthkit/autopsy/report/images/default_generator_logo.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico b/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico
index efad7a3de4..696550c9ff 100644
Binary files a/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico and b/Core/src/org/sleuthkit/autopsy/report/images/favicon.ico differ
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
index 6780af841a..cd5cb85bee 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/TimeLineController.java
@@ -23,8 +23,10 @@ import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.sql.ResultSet;
import java.sql.SQLException;
+import java.text.NumberFormat;
import java.time.ZoneId;
import java.util.Collection;
+import java.util.Map;
import java.util.MissingResourceException;
import java.util.TimeZone;
import java.util.concurrent.ExecutionException;
@@ -92,7 +94,7 @@ import org.sleuthkit.datamodel.TskCoreException;
*
*
*/
-public class TimeLineController {
+public class TimeLineController {
private static final Logger LOGGER = Logger.getLogger(TimeLineController.class.getName());
@@ -221,9 +223,9 @@ public class TimeLineController {
filteredEvents = eventsRepository.getEventsModel();
InitialZoomState = new ZoomParams(filteredEvents.getSpanningInterval(),
- EventTypeZoomLevel.BASE_TYPE,
- Filter.getDefaultFilter(),
- DescriptionLOD.SHORT);
+ EventTypeZoomLevel.BASE_TYPE,
+ Filter.getDefaultFilter(),
+ DescriptionLOD.SHORT);
historyManager.advance(InitialZoomState);
//persistent listener instances
@@ -466,13 +468,30 @@ public class TimeLineController {
}
}
- synchronized public void pushDescrLOD(DescriptionLOD newLOD) {
- ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
- if (currentZoom == null) {
- advance(InitialZoomState.withDescrLOD(newLOD));
- } else if (currentZoom.hasDescrLOD(newLOD) == false) {
- advance(currentZoom.withDescrLOD(newLOD));
+ synchronized public boolean pushDescrLOD(DescriptionLOD newLOD) {
+ Map eventCounts = filteredEvents.getEventCounts(filteredEvents.getRequestedZoomParamters().get().getTimeRange());
+ final Long count = eventCounts.values().stream().reduce(0l, Long::sum);
+
+ boolean shouldContinue = true;
+ if (newLOD == DescriptionLOD.FULL && count > 10_000) {
+
+ int showConfirmDialog = JOptionPane.showConfirmDialog(mainFrame,
+ "You are about to show details for " + NumberFormat.getInstance().format(count) + " events. This might be very slow or even crash Autopsy.\n\nDo you want to continue?",
+ "",
+ JOptionPane.YES_NO_OPTION);
+
+ shouldContinue = (showConfirmDialog == JOptionPane.YES_OPTION);
}
+
+ if (shouldContinue) {
+ ZoomParams currentZoom = filteredEvents.getRequestedZoomParamters().get();
+ if (currentZoom == null) {
+ advance(InitialZoomState.withDescrLOD(newLOD));
+ } else if (currentZoom.hasDescrLOD(newLOD) == false) {
+ advance(currentZoom.withDescrLOD(newLOD));
+ }
+ }
+ return shouldContinue;
}
synchronized public void pushTimeAndType(Interval timeRange, EventTypeZoomLevel typeZoom) {
@@ -599,35 +618,35 @@ public class TimeLineController {
*/
synchronized public boolean outOfDatePromptAndRebuild() {
return showOutOfDateConfirmation() == JOptionPane.YES_OPTION
- ? rebuildRepo()
- : false;
+ ? rebuildRepo()
+ : false;
}
synchronized int showLastPopulatedWhileIngestingConfirmation() {
return JOptionPane.showConfirmDialog(mainFrame,
- DO_REPOPULATE_MESSAGE,
- "re populate events?",
- JOptionPane.YES_NO_OPTION,
- JOptionPane.QUESTION_MESSAGE);
+ DO_REPOPULATE_MESSAGE,
+ "re populate events?",
+ JOptionPane.YES_NO_OPTION,
+ JOptionPane.QUESTION_MESSAGE);
}
synchronized int showOutOfDateConfirmation() throws MissingResourceException, HeadlessException {
return JOptionPane.showConfirmDialog(mainFrame,
- NbBundle.getMessage(TimeLineController.class,
- "Timeline.propChg.confDlg.timelineOOD.msg"),
- NbBundle.getMessage(TimeLineController.class,
- "Timeline.propChg.confDlg.timelineOOD.details"),
- JOptionPane.YES_NO_OPTION);
+ NbBundle.getMessage(TimeLineController.class,
+ "Timeline.propChg.confDlg.timelineOOD.msg"),
+ NbBundle.getMessage(TimeLineController.class,
+ "Timeline.propChg.confDlg.timelineOOD.details"),
+ JOptionPane.YES_NO_OPTION);
}
synchronized int showIngestConfirmation() throws MissingResourceException, HeadlessException {
return JOptionPane.showConfirmDialog(mainFrame,
- NbBundle.getMessage(TimeLineController.class,
- "Timeline.initTimeline.confDlg.genBeforeIngest.msg"),
- NbBundle.getMessage(TimeLineController.class,
- "Timeline.initTimeline.confDlg.genBeforeIngest.details"),
- JOptionPane.YES_NO_OPTION);
+ NbBundle.getMessage(TimeLineController.class,
+ "Timeline.initTimeline.confDlg.genBeforeIngest.msg"),
+ NbBundle.getMessage(TimeLineController.class,
+ "Timeline.initTimeline.confDlg.genBeforeIngest.details"),
+ JOptionPane.YES_NO_OPTION);
}
private class AutopsyIngestModuleListener implements PropertyChangeListener {
@@ -670,7 +689,7 @@ public class TimeLineController {
}
@Immutable
- class AutopsyCaseListener implements PropertyChangeListener {
+ private class AutopsyCaseListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java b/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java
index c97008328d..454b90975c 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/events/AggregateEvent.java
@@ -108,7 +108,7 @@ public class AggregateEvent {
return new AggregateEvent(IntervalUtils.span(ag1.span, ag2.span), ag1.getType(), ids, ag1.getDescription(), ag1.lod);
}
- DescriptionLOD getLOD() {
+ public DescriptionLOD getLOD() {
return lod;
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java b/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java
index 3b453930c1..0d19d1e28d 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/events/FilteredEventsModel.java
@@ -259,4 +259,7 @@ public class FilteredEventsModel {
// requestedLOD.set(zCrumb.getDescrLOD());
// }
// }
+ public DescriptionLOD getDescriptionLOD() {
+ return requestedLOD.get();
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java b/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java
index 79716db2f7..dd6955fe37 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/events/type/RootEventType.java
@@ -59,6 +59,9 @@ public class RootEventType implements EventType {
private static class RootEventTypeHolder {
private static final RootEventType INSTANCE = new RootEventType();
+
+ private RootEventTypeHolder() {
+ }
}
@Override
@@ -76,8 +79,6 @@ public class RootEventType implements EventType {
return Arrays.asList(BaseTypes.values());
}
-
-
@Override
public String getIconBase() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java
index 4e4f122f24..f9c945a997 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/AbstractFilter.java
@@ -13,7 +13,6 @@ import javafx.beans.property.SimpleBooleanProperty;
public abstract class AbstractFilter implements Filter {
private final SimpleBooleanProperty active = new SimpleBooleanProperty(true);
-
private final SimpleBooleanProperty disabled = new SimpleBooleanProperty(false);
@Override
@@ -42,7 +41,7 @@ public abstract class AbstractFilter implements Filter {
}
@Override
- public boolean isdisabled() {
+ public boolean isDisabled() {
return disabled.get();
}
@@ -50,4 +49,7 @@ public abstract class AbstractFilter implements Filter {
public String getStringCheckBox() {
return "[" + (isActive() ? "x" : " ") + "]";
}
+
+
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java
index df07d23ffa..4392fd68cd 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/Filter.java
@@ -76,6 +76,5 @@ public interface Filter {
SimpleBooleanProperty getDisabledProperty();
- boolean isdisabled();
-
+ boolean isDisabled();
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java
index 86c36ce5fe..0742a91cbe 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/HideKnownFilter.java
@@ -37,7 +37,7 @@ public class HideKnownFilter extends AbstractFilter {
public HideKnownFilter copyOf() {
HideKnownFilter hideKnownFilter = new HideKnownFilter();
hideKnownFilter.setActive(isActive());
- hideKnownFilter.setDisabled(isdisabled());
+ hideKnownFilter.setDisabled(isDisabled());
return hideKnownFilter;
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java
index b11b0f53b7..5af67c3483 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/IntersectionFilter.java
@@ -10,15 +10,15 @@ import javafx.collections.ObservableList;
/** Intersection(And) filter */
public class IntersectionFilter extends CompoundFilter {
-
+
public IntersectionFilter(ObservableList subFilters) {
super(subFilters);
}
-
+
public IntersectionFilter() {
super(FXCollections.observableArrayList());
}
-
+
@Override
public IntersectionFilter copyOf() {
IntersectionFilter filter = new IntersectionFilter(FXCollections.observableArrayList(
@@ -26,20 +26,22 @@ public class IntersectionFilter extends CompoundFilter {
.map(Filter::copyOf)
.collect(Collectors.toList())));
filter.setActive(isActive());
- filter.setDisabled(isdisabled());
+ filter.setDisabled(isDisabled());
return filter;
}
-
+
@Override
public String getDisplayName() {
- return "Intersection";
+ return "Intersection" + getSubFilters().stream()
+ .map(Filter::getDisplayName)
+ .collect(Collectors.joining(",", "[", "]"));
}
-
+
@Override
public String getHTMLReportString() {
return getSubFilters().stream().filter(Filter::isActive).map(Filter::getHTMLReportString).collect(Collectors.joining("- ", ""));
}
-
+
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -49,11 +51,11 @@ public class IntersectionFilter extends CompoundFilter {
return false;
}
final IntersectionFilter other = (IntersectionFilter) obj;
-
+
if (isActive() != other.isActive()) {
return false;
}
-
+
for (int i = 0; i < getSubFilters().size(); i++) {
if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) {
return false;
@@ -61,7 +63,7 @@ public class IntersectionFilter extends CompoundFilter {
}
return true;
}
-
+
@Override
public int hashCode() {
int hash = 7;
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java
index 44a38c30e9..6d7db2ca0e 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/TextFilter.java
@@ -56,7 +56,7 @@ public class TextFilter extends AbstractFilter {
synchronized public TextFilter copyOf() {
TextFilter textFilter = new TextFilter(getText());
textFilter.setActive(isActive());
- textFilter.setDisabled(isdisabled());
+ textFilter.setDisabled(isDisabled());
return textFilter;
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java b/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java
index 0306e463d9..80f5d1edcb 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/filters/TypeFilter.java
@@ -42,7 +42,7 @@ public class TypeFilter extends UnionFilter {
private TypeFilter(EventType et, boolean recursive) {
super(FXCollections.observableArrayList());
this.eventType = et;
-
+
if (recursive) { // add subfilters for each subtype
for (EventType subType : et.getSubTypes()) {
this.getSubFilters().add(new TypeFilter(subType));
@@ -57,11 +57,11 @@ public class TypeFilter extends UnionFilter {
public TypeFilter(EventType et) {
this(et, true);
}
-
+
public EventType getEventType() {
return eventType;
}
-
+
@Override
public String getDisplayName() {
return eventType == RootEventType.getInstance() ? "Event Type Filter" : eventType.getDisplayName();
@@ -76,21 +76,21 @@ public class TypeFilter extends UnionFilter {
public Image getFXImage() {
return eventType.getFXImage();
}
-
+
@Override
public TypeFilter copyOf() {
//make a nonrecursive copy of this filter
final TypeFilter typeFilter = new TypeFilter(eventType, false);
typeFilter.setActive(isActive());
- typeFilter.setDisabled(isdisabled());
+ typeFilter.setDisabled(isDisabled());
//add a copy of each subfilter
this.getSubFilters().forEach((Filter t) -> {
typeFilter.getSubFilters().add(t.copyOf());
});
-
+
return typeFilter;
}
-
+
@Override
public String getHTMLReportString() {
String string = getEventType().getDisplayName() + getStringCheckBox();
@@ -99,7 +99,7 @@ public class TypeFilter extends UnionFilter {
}
return string;
}
-
+
@Override
public boolean equals(Object obj) {
if (obj == null) {
@@ -109,15 +109,15 @@ public class TypeFilter extends UnionFilter {
return false;
}
final TypeFilter other = (TypeFilter) obj;
-
+
if (isActive() != other.isActive()) {
return false;
}
-
+
if (this.eventType != other.eventType) {
return false;
}
-
+
for (int i = 0; i < getSubFilters().size(); i++) {
if (getSubFilters().get(i).equals(other.getSubFilters().get(i)) == false) {
return false;
@@ -125,12 +125,12 @@ public class TypeFilter extends UnionFilter {
}
return true;
}
-
+
@Override
public int hashCode() {
int hash = 7;
hash = 67 * hash + Objects.hashCode(this.eventType);
return hash;
}
-
+
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png b/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png
new file mode 100644
index 0000000000..108778b80e
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/timeline/images/minus-button.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png b/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png
new file mode 100644
index 0000000000..f6cced51a6
Binary files /dev/null and b/Core/src/org/sleuthkit/autopsy/timeline/images/plus-button.png differ
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java
index aabac6e1bb..fad4f78949 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/AbstractVisualization.java
@@ -18,6 +18,7 @@
*/
package org.sleuthkit.autopsy.timeline.ui;
+import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javafx.beans.InvalidationListener;
@@ -102,7 +103,7 @@ public abstract class AbstractVisualization getSettingsNodes() {
- return settingsNodes;
+ return Collections.unmodifiableList(settingsNodes);
}
/** @param value a value along this visualization's x axis
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml
index 7aa00b91b4..becc6fc797 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/VisualizationPanel.fxml
@@ -1,5 +1,6 @@
+
@@ -14,10 +15,13 @@
-
-
+
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java
index c0c10dd49a..5e74fcf979 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/detailview/EventDetailChart.java
@@ -27,7 +27,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
-import java.util.concurrent.ExecutionException;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javafx.animation.KeyFrame;
@@ -54,41 +53,27 @@ import javafx.scene.Node;
import javafx.scene.chart.Axis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
-import javafx.scene.control.ContentDisplay;
import javafx.scene.control.ContextMenu;
-import javafx.scene.control.CustomMenuItem;
-import javafx.scene.control.Label;
-import javafx.scene.control.MenuItem;
-import javafx.scene.control.Slider;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.shape.Line;
import javafx.scene.shape.StrokeLineCap;
-import javafx.scene.text.Text;
import javafx.util.Duration;
-import javafx.util.StringConverter;
import javax.annotation.concurrent.GuardedBy;
import org.controlsfx.control.action.AbstractAction;
import org.controlsfx.control.action.ActionGroup;
import org.controlsfx.control.action.ActionUtils;
import org.joda.time.DateTime;
import org.joda.time.Interval;
-import org.openide.util.Exceptions;
-import org.sleuthkit.autopsy.coreutils.LoggedTask;
import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.actions.Back;
import org.sleuthkit.autopsy.timeline.actions.Forward;
import org.sleuthkit.autopsy.timeline.events.AggregateEvent;
import org.sleuthkit.autopsy.timeline.events.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.events.type.EventType;
-import org.sleuthkit.autopsy.timeline.filters.Filter;
-import org.sleuthkit.autopsy.timeline.filters.TextFilter;
-import org.sleuthkit.autopsy.timeline.filters.TypeFilter;
import org.sleuthkit.autopsy.timeline.ui.TimeLineChart;
-import org.sleuthkit.autopsy.timeline.zooming.DescriptionLOD;
-import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
/**
* Custom implementation of {@link XYChart} to graph events on a horizontal
@@ -103,7 +88,7 @@ import org.sleuthkit.autopsy.timeline.zooming.ZoomParams;
* node to contain each band if we need a place for per band controls.
*
* //TODO: refactor the projected lines to a separate class. -jm */
-public class EventDetailChart extends XYChart implements TimeLineChart {
+public final class EventDetailChart extends XYChart implements TimeLineChart {
private static final int PROJECTED_LINE_Y_OFFSET = 5;
@@ -113,14 +98,16 @@ public class EventDetailChart extends XYChart implemen
* events together during layout */
private final SimpleBooleanProperty bandByType = new SimpleBooleanProperty(false);
+ // I don't like having these package visible, but it was the easiest way to
private ContextMenu chartContextMenu;
private TimeLineController controller;
+ private FilteredEventsModel filteredEvents;
+
/** how much detail of the description to show in the ui */
private final SimpleObjectProperty descrVisibility = new SimpleObjectProperty<>(DescriptionVisibility.SHOWN);
- private FilteredEventsModel filteredEvents;
/** a user position-able vertical line to help the compare events */
private Line guideLine;
@@ -171,7 +158,7 @@ public class EventDetailChart extends XYChart implemen
@GuardedBy(value = "this")
private boolean requiresLayout = true;
- private final ObservableList selectedNodes;
+ final ObservableList selectedNodes;
/**
* list of series of data added to this chart TODO: replace this with a map
@@ -262,7 +249,7 @@ public class EventDetailChart extends XYChart implemen
}
}
}, new ActionGroup("Zoom History", new Back(controller),
- new Forward(controller))));
+ new Forward(controller))));
chartContextMenu.setAutoHide(true);
chartContextMenu.show(EventDetailChart.this, clickEvent.getScreenX(), clickEvent.getScreenY());
clickEvent.consume();
@@ -297,7 +284,7 @@ public class EventDetailChart extends XYChart implemen
});
c.getAddedSubList().forEach((AggregateEventNode t) -> {
Line line = new Line(dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEvent().getSpan().getStartMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET,
- dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEvent().getSpan().getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
+ dateAxis.localToParent(dateAxis.getDisplayPosition(new DateTime(t.getEvent().getSpan().getEndMillis(), TimeLineController.getJodaTimeZone())), 0).getX(), dateAxis.getLayoutY() + PROJECTED_LINE_Y_OFFSET
);
line.setStroke(t.getEvent().getType().getColor().deriveColor(0, 1, 1, .5));
line.setStrokeWidth(PROJECTED_LINE_STROKE_WIDTH);
@@ -326,7 +313,7 @@ public class EventDetailChart extends XYChart implemen
}
@Override
- public final synchronized void setController(TimeLineController controller) {
+ public synchronized void setController(TimeLineController controller) {
this.controller = controller;
setModel(this.controller.getEventsModel());
}
@@ -361,7 +348,7 @@ public class EventDetailChart extends XYChart implemen
* @return the DateTime along the x-axis corresponding to the given x value
* (in the space of this {@link EventDetailChart}
*/
- public final DateTime getDateTimeForPosition(double x) {
+ public DateTime getDateTimeForPosition(double x) {
return getXAxis().getValueForDisplay(getXAxis().parentToLocal(x, 0).getX());
}
@@ -398,8 +385,7 @@ public class EventDetailChart extends XYChart implemen
final AggregateEvent aggEvent = data.getYValue();
AggregateEventNode eventNode = nodeMap.get(aggEvent);
if (eventNode == null) {
- eventNode = new AggregateEventNode(aggEvent, null);
- eventNode.setOnMouseClicked(new EventMouseHandler(eventNode));
+ eventNode = new AggregateEventNode(aggEvent, null, this);
eventNode.setLayoutX(getXAxis().getDisplayPosition(new DateTime(aggEvent.getSpan().getStartMillis())));
data.setNode(eventNode);
@@ -566,10 +552,9 @@ public class EventDetailChart extends XYChart implemen
//size timespan border
tlNode.setSpanWidth(span);
if (truncateAll.get()) { //if truncate option is selected limit width of description label
- tlNode.setDescriptionLabelMaxWidth(Math.max(span, truncateWidth.get()));
+ tlNode.setDescriptionWidth(Math.max(span, truncateWidth.get()));
} else { //else set it unbounded
-
- tlNode.setDescriptionLabelMaxWidth(20 + new Text(tlNode.getDisplayedDescription()).getLayoutBounds().getWidth());
+ tlNode.setDescriptionWidth(USE_PREF_SIZE);//20 + new Text(tlNode.getDisplayedDescription()).getLayoutBounds().getWidth());
}
tlNode.autosize(); //compute size of tlNode based on constraints and event data
@@ -577,7 +562,7 @@ public class EventDetailChart extends XYChart implemen
double xRight = xPos + tlNode.getWidth();
//get the height of the node
- final double h = layoutNodesResultHeight == 0 ? tlNode.getHeight() : layoutNodesResultHeight;
+ final double h = layoutNodesResultHeight == 0 ? tlNode.getHeight() : layoutNodesResultHeight + DEFAULT_ROW_HEIGHT;
//initial test position
double yPos = minY;
@@ -616,8 +601,8 @@ public class EventDetailChart extends XYChart implemen
localMax = Math.max(yPos2, localMax);
Timeline tm = new Timeline(new KeyFrame(Duration.seconds(1.0),
- new KeyValue(tlNode.layoutXProperty(), xPos),
- new KeyValue(tlNode.layoutYProperty(), yPos)));
+ new KeyValue(tlNode.layoutXProperty(), xPos),
+ new KeyValue(tlNode.layoutYProperty(), yPos)));
tm.play();
// tlNode.relocate(xPos, yPos);
@@ -625,6 +610,7 @@ public class EventDetailChart extends XYChart implemen
maxY.set(Math.max(maxY.get(), localMax));
return localMax - minY;
}
+ private static final int DEFAULT_ROW_HEIGHT = 24;
private void layoutProjectionMap() {
for (final Map.Entry entry : projectionMap.entrySet()) {
@@ -642,21 +628,29 @@ public class EventDetailChart extends XYChart implemen
return getXAxis().localToParent(getXAxis().getDisplayPosition(dt), 0).getX();
}
- private static final class DescriptionLODConverter extends StringConverter {
-
- @Override
- public String toString(Double value) {
- return value == -1 ? "None"
- : DescriptionLOD.values()[value.intValue()].getDisplayName();
- }
-
- @Override
- public Double fromString(String string) {
- //we never convert from string to double (slider position)
- throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
- }
+ /**
+ * @return the controller
+ */
+ public TimeLineController getController() {
+ return controller;
}
+ /**
+ * @return the filteredEvents
+ */
+ public FilteredEventsModel getFilteredEvents() {
+ return filteredEvents;
+ }
+
+ /**
+ * @return the chartContextMenu
+ */
+ public ContextMenu getChartContextMenu() {
+ return chartContextMenu;
+ }
+
+
+
private static class StartTimeComparator implements Comparator {
@Override
@@ -669,7 +663,7 @@ public class EventDetailChart extends XYChart implemen
} else {
return Long.compare(((AggregateEventNode) n1).getEvent().getSpan().getStartMillis(),
- (((AggregateEventNode) n2).getEvent().getSpan().getStartMillis()));
+ (((AggregateEventNode) n2).getEvent().getSpan().getStartMillis()));
}
}
@@ -698,144 +692,13 @@ public class EventDetailChart extends XYChart implemen
}
- /** event handler used for mouse events on {@link AggregateEventNode}s
- * //TODO: refactor this to put more of the state(slider)in the node */
- private class EventMouseHandler implements EventHandler {
- private final AggregateEventNode aggNode;
+ synchronized void setRequiresLayout(boolean b) {
+ requiresLayout = true;
+ }
- private final AggregateEvent aggEvent;
-
- private final Slider slider;
-
- public EventMouseHandler(AggregateEventNode aggNode) {
- this.aggNode = aggNode;
- this.aggEvent = aggNode.getEvent();
-
- //configure slider
- this.slider = new Slider(-1, 2, -1);
- slider.setShowTickMarks(true);
- slider.setShowTickLabels(true);
- slider.setSnapToTicks(true);
- slider.setMajorTickUnit(1);
- slider.setMinorTickCount(0);
- slider.setBlockIncrement(1);
- slider.setLabelFormatter(new DescriptionLODConverter());
-
- //on slider change, reload subnodes
- InvalidationListener invalidationListener = o -> {
- if (slider.isValueChanging() == false) {
- reloadSubNodes();
- }
- };
- slider.valueProperty().addListener(invalidationListener);
- slider.valueChangingProperty().addListener(invalidationListener);
- }
-
- private void reloadSubNodes() {
- final int value = Math.round(slider.valueProperty().floatValue());
- aggNode.getSubNodePane().getChildren().clear();
- aggNode.setEventDetailsVisible(true);
- if (value == -1) {
- aggNode.getSubNodePane().getChildren().clear();
- aggNode.setEventDetailsVisible(true);
- synchronized (EventDetailChart.this) {
- requiresLayout = true;
- }
- requestChartLayout();
- } else {
- final DescriptionLOD newLOD = DescriptionLOD.values()[value];
-
- final Filter combinedFilter = Filter.intersect(new Filter[]{new TextFilter(aggEvent.getDescription()),
- new TypeFilter(aggEvent.getType()),
- filteredEvents.filter().get()});
- final Interval span = aggEvent.getSpan().withEndMillis(aggEvent.getSpan().getEndMillis() + 1000);
- LoggedTask
> loggedTask = new LoggedTask>("Load sub events", true) {
-
- @Override
- protected List call() throws Exception {
-
- List aggregatedEvents = filteredEvents.getAggregatedEvents(new ZoomParams(span,
- filteredEvents.eventTypeZoom().get(),
- combinedFilter,
- newLOD));
- return aggregatedEvents.stream().map((AggregateEvent t) -> {
- AggregateEventNode subNode = new AggregateEventNode(t, aggNode);
- subNode.setOnMouseClicked(new EventMouseHandler(subNode));
- subNode.setLayoutX(getXAxis().getDisplayPosition(new DateTime(t.getSpan().getStartMillis())) - aggNode.getLayoutXCompensation());
- return subNode;
- }).collect(Collectors.toList());
- }
-
- @Override
- protected void succeeded() {
- try {
- if (get().size() > 1) {
- setCursor(Cursor.WAIT);
- aggNode.setEventDetailsVisible(false);
- aggNode.getSubNodePane().getChildren().setAll(get());
- synchronized (EventDetailChart.this) {
- requiresLayout = true;
- }
- requestChartLayout();
- setCursor(null);
- }
- } catch (InterruptedException | ExecutionException ex) {
- Exceptions.printStackTrace(ex);
- }
- }
- };
- controller.monitorTask(loggedTask);
- }
- }
-
- @Override
- public void handle(MouseEvent t) {
- t.consume();
- if (t.getButton() == MouseButton.PRIMARY) {
- if (t.isShiftDown()) {
- if (selectedNodes.contains(aggNode) == false) {
- selectedNodes.add(aggNode);
- }
- } else if (t.isShortcutDown()) {
- selectedNodes.removeAll(aggNode);
- } else if (t.getClickCount() > 1) {
- slider.increment();
- } else {
- selectedNodes.setAll(aggNode);
- }
- } else if (t.getButton() == MouseButton.SECONDARY) {
-
- if (chartContextMenu != null) {
- chartContextMenu.hide();
- }
- //we use a per node menu to remember the slider position
- ContextMenu nodeContextMenu = aggNode.getContextMenu();
- if (nodeContextMenu == null) {
- nodeContextMenu = builContextMenu();
- aggNode.setContextMenu(nodeContextMenu);
- }
- nodeContextMenu.show(aggNode, t.getScreenX(), t.getScreenY());
- }
- }
-
- private ContextMenu builContextMenu() {
- //should we include a label to remind uer of what group this is for
- //final MenuItem headingItem = new CustomMenuItem(new Label(aggEvent.getDescription()), false);
- //headingItem.getStyleClass().remove("menu-item");
- final Label sliderLabel = new Label("Nested Detail:", slider);
- sliderLabel.setContentDisplay(ContentDisplay.RIGHT);
- final MenuItem detailSliderItem = new CustomMenuItem(sliderLabel, false);
- detailSliderItem.getStyleClass().remove("menu-item");
- ContextMenu contextMenu = new ContextMenu(detailSliderItem);
- //we don't reuse item from chartContextMenu because 'place marker' is location specific.
- //TODO: refactor this so we can reuse chartContextMenu items
- contextMenu.getItems().addAll(ActionUtils.createContextMenu(
- Arrays.asList(new ActionGroup("Zoom History", new Back(controller),
- new Forward(controller)))).getItems());
- //TODO: add tagging actions here
- contextMenu.setAutoHide(true);
- return contextMenu;
- }
+ @Override
+ protected void requestChartLayout() {
+ super.requestChartLayout(); //To change body of generated methods, choose Tools | Templates.
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java
index ee1c507ed5..22ba4ca113 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterSetPanel.java
@@ -20,6 +20,9 @@ package org.sleuthkit.autopsy.timeline.ui.filtering;
import javafx.application.Platform;
import javafx.beans.Observable;
+import javafx.beans.property.SimpleBooleanProperty;
+import javafx.collections.FXCollections;
+import javafx.collections.ObservableMap;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.control.CheckBox;
@@ -67,6 +70,8 @@ public class FilterSetPanel extends BorderPane implements TimeLineView {
private TimeLineController controller;
+ private final ObservableMap expansionMap = FXCollections.observableHashMap();
+
@FXML
void initialize() {
assert applyButton != null : "fx:id=\"applyButton\" was not injected: check your FXML file 'FilterSetPanel.fxml'.";
@@ -138,6 +143,7 @@ public class FilterSetPanel extends BorderPane implements TimeLineView {
public FilterSetPanel() {
FXMLConstructor.construct(this, "FilterSetPanel.fxml");
+ expansionMap.put("Event Type Filter", Boolean.TRUE);
}
@Override
@@ -152,16 +158,14 @@ public class FilterSetPanel extends BorderPane implements TimeLineView {
@Override
public void setModel(FilteredEventsModel filteredEvents) {
this.filteredEvents = filteredEvents;
-
refresh();
-
this.filteredEvents.filter().addListener((Observable o) -> {
refresh();
});
}
private void refresh() {
- filterTreeTable.setRoot(new FilterTreeItem(this.filteredEvents.filter().get().copyOf()));
+ filterTreeTable.setRoot(new FilterTreeItem(this.filteredEvents.filter().get().copyOf(), expansionMap));
}
/**
@@ -171,23 +175,29 @@ public class FilterSetPanel extends BorderPane implements TimeLineView {
private static class FilterCheckBoxCell extends TreeTableCell {
private final CheckBox checkBox = new CheckBox();
+ private SimpleBooleanProperty activeProperty;
@Override
protected void updateItem(AbstractFilter item, boolean empty) {
super.updateItem(item, empty);
Platform.runLater(() -> {
+ if (activeProperty != null) {
+ checkBox.selectedProperty().unbindBidirectional(activeProperty);
+ }
+ checkBox.disableProperty().unbind();
if (item == null) {
setText(null);
setGraphic(null);
- checkBox.selectedProperty().unbind();
- checkBox.disableProperty().unbind();
+
} else {
setText(item.getDisplayName());
- checkBox.selectedProperty().bindBidirectional(item.getActiveProperty());
+ activeProperty = item.getActiveProperty();
+ checkBox.selectedProperty().bindBidirectional(activeProperty);
checkBox.disableProperty().bind(item.getDisabledProperty());
setGraphic(checkBox);
}
});
}
+
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java
index 2302099939..fa294f7c1d 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/ui/filtering/FilterTreeItem.java
@@ -1,5 +1,8 @@
package org.sleuthkit.autopsy.timeline.ui.filtering;
+import javafx.beans.Observable;
+import javafx.collections.MapChangeListener;
+import javafx.collections.ObservableMap;
import javafx.scene.control.TreeItem;
import org.sleuthkit.autopsy.timeline.filters.CompoundFilter;
import org.sleuthkit.autopsy.timeline.filters.Filter;
@@ -16,15 +19,28 @@ public class FilterTreeItem extends TreeItem {
* be made for them added added to the children of this
* FilterTreeItem
*/
- public FilterTreeItem(Filter f) {
+ public FilterTreeItem(Filter f, ObservableMap expansionMap) {
super(f);
- setExpanded(true);
+
+ expansionMap.addListener((MapChangeListener.Change extends String, ? extends Boolean> change) -> {
+ if (change.getKey() == f.getDisplayName()) {
+ setExpanded(expansionMap.get(change.getKey()));
+ }
+ });
+
+ if (expansionMap.get(f.getDisplayName()) != null) {
+ setExpanded(expansionMap.get(f.getDisplayName()));
+ }
+
+ expandedProperty().addListener((Observable observable) -> {
+ expansionMap.put(f.getDisplayName(), isExpanded());
+ });
if (f instanceof CompoundFilter) {
CompoundFilter cf = (CompoundFilter) f;
for (Filter af : cf.getSubFilters()) {
- getChildren().add(new FilterTreeItem(af));
+ getChildren().add(new FilterTreeItem(af, expansionMap));
}
}
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLOD.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLOD.java
index be12be9825..45b7d08423 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLOD.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/DescriptionLOD.java
@@ -34,4 +34,20 @@ public enum DescriptionLOD {
private DescriptionLOD(String displayName) {
this.displayName = displayName;
}
+
+ public DescriptionLOD next() {
+ try {
+ return values()[ordinal() + 1];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
+
+ public DescriptionLOD previous() {
+ try {
+ return values()[ordinal() - 1];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return null;
+ }
+ }
}
diff --git a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java
index f07e113461..114df4b35d 100644
--- a/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java
+++ b/Core/src/org/sleuthkit/autopsy/timeline/zooming/ZoomSettingsPane.java
@@ -111,40 +111,42 @@ public class ZoomSettingsPane extends TitledPane implements TimeLineView {
this.filteredEvents = filteredEvents;
initializeSlider(timeUnitSlider,
- () -> {
- TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()];
- if (requestedUnit == TimeUnits.FOREVER) {
- controller.showFullRange();
- } else {
- controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRange().get()), requestedUnit.getPeriod()));
- }
- },
- this.filteredEvents.timeRange(),
- () -> {
- RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(this.filteredEvents.timeRange().get());
- ChronoUnit chronoUnit = rangeInfo.getPeriodSize().getChronoUnit();
+ () -> {
+ TimeUnits requestedUnit = TimeUnits.values()[new Double(timeUnitSlider.getValue()).intValue()];
+ if (requestedUnit == TimeUnits.FOREVER) {
+ controller.showFullRange();
+ } else {
+ controller.pushTimeRange(IntervalUtils.getIntervalAround(IntervalUtils.middleOf(ZoomSettingsPane.this.filteredEvents.timeRange().get()), requestedUnit.getPeriod()));
+ }
+ },
+ this.filteredEvents.timeRange(),
+ () -> {
+ RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(this.filteredEvents.timeRange().get());
+ ChronoUnit chronoUnit = rangeInfo.getPeriodSize().getChronoUnit();
- timeUnitSlider.setValue(TimeUnits.fromChronoUnit(chronoUnit).ordinal() - 1);
- });
+ timeUnitSlider.setValue(TimeUnits.fromChronoUnit(chronoUnit).ordinal() - 1);
+ });
initializeSlider(descrLODSlider,
- () -> {
- DescriptionLOD newLOD = DescriptionLOD.values()[Math.round(descrLODSlider.valueProperty().floatValue())];
- controller.pushDescrLOD(newLOD);
- }, this.filteredEvents.descriptionLOD(),
- () -> {
- descrLODSlider.setValue(this.filteredEvents.descriptionLOD().get().ordinal());
- });
+ () -> {
+ DescriptionLOD newLOD = DescriptionLOD.values()[Math.round(descrLODSlider.valueProperty().floatValue())];
+ if (controller.pushDescrLOD(newLOD) == false) {
+ descrLODSlider.setValue(new DescrLODConverter().fromString(filteredEvents.getDescriptionLOD().toString()));
+ }
+ }, this.filteredEvents.descriptionLOD(),
+ () -> {
+ descrLODSlider.setValue(this.filteredEvents.descriptionLOD().get().ordinal());
+ });
initializeSlider(typeZoomSlider,
- () -> {
- EventTypeZoomLevel newZoomLevel = EventTypeZoomLevel.values()[Math.round(typeZoomSlider.valueProperty().floatValue())];
- controller.pushEventTypeZoom(newZoomLevel);
- },
- this.filteredEvents.eventTypeZoom(),
- () -> {
- typeZoomSlider.setValue(this.filteredEvents.eventTypeZoom().get().ordinal());
- });
+ () -> {
+ EventTypeZoomLevel newZoomLevel = EventTypeZoomLevel.values()[Math.round(typeZoomSlider.valueProperty().floatValue())];
+ controller.pushEventTypeZoom(newZoomLevel);
+ },
+ this.filteredEvents.eventTypeZoom(),
+ () -> {
+ typeZoomSlider.setValue(this.filteredEvents.eventTypeZoom().get().ordinal());
+ });
}
/**
@@ -178,7 +180,7 @@ public class ZoomSettingsPane extends TitledPane implements TimeLineView {
slider.valueProperty().removeListener(sliderListener);
slider.valueChangingProperty().removeListener(sliderListener);
- driverChangHandler.run();
+ Platform.runLater(driverChangHandler);
slider.valueProperty().addListener(sliderListener);
slider.valueChangingProperty().addListener(sliderListener);
diff --git a/README.txt b/README.txt
index 6183136aaa..1bdce6e2b5 100644
--- a/README.txt
+++ b/README.txt
@@ -115,3 +115,22 @@ ImgScalr 4.2 for image resizing in image viewers
- Web page: http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/
- License: http://www.thebuzzmedia.com/software/imgscalr-java-image-scaling-library/#license
+
+EMBEDED RESOURCES
+
+This section lists other resources, such as icons, that are used by Autopsy.
+
+FAMFAMFAM Silk Icons v1.3
+- Web page: http://www.famfamfam.com/lab/icons/silk/
+- License: http://creativecommons.org/licenses/by/3.0/
+
+Fugue Icons v3.5.6
+- Web page: http://p.yusukekamiyamane.com/
+- License: http://creativecommons.org/licenses/by/3.0/
+
+WebHostingHub Glyphs
+- Web page: http://www.webhostinghub.com/glyphs/
+- License: http://creativecommons.org/licenses/by/3.0/
+
+Splashy Icons (free as in free)
+- Web page: http://splashyfish.com/icons/
\ No newline at end of file
diff --git a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties
index 9a48d8441f..a16d8e667b 100644
--- a/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties
+++ b/branding/core/core.jar/org/netbeans/core/startup/Bundle.properties
@@ -1,11 +1,11 @@
#Updated by build script
-#Wed, 03 Sep 2014 09:52:11 -0400
+#Wed, 22 Oct 2014 12:03:43 -0400
LBL_splash_window_title=Starting Autopsy
-SPLASH_HEIGHT=288
-SPLASH_WIDTH=538
-SplashProgressBarBounds=3,282,533,6
-SplashRunningTextBounds=5,266,530,17
+SPLASH_HEIGHT=560
+SPLASH_WIDTH=960
+SplashProgressBarBounds=0,546,960,14
+SplashRunningTextBounds=0,517,957,25
SplashRunningTextColor=0x0
-SplashRunningTextFontSize=18
+SplashRunningTextFontSize=27
currentVersion=Autopsy 3.1.0
diff --git a/branding/core/core.jar/org/netbeans/core/startup/frame.gif b/branding/core/core.jar/org/netbeans/core/startup/frame.gif
index 08dc748a00..c1fc81ff1b 100644
Binary files a/branding/core/core.jar/org/netbeans/core/startup/frame.gif and b/branding/core/core.jar/org/netbeans/core/startup/frame.gif differ
diff --git a/branding/core/core.jar/org/netbeans/core/startup/frame32.gif b/branding/core/core.jar/org/netbeans/core/startup/frame32.gif
index 0cc097241a..b77aa72faa 100644
Binary files a/branding/core/core.jar/org/netbeans/core/startup/frame32.gif and b/branding/core/core.jar/org/netbeans/core/startup/frame32.gif differ
diff --git a/branding/core/core.jar/org/netbeans/core/startup/frame48.gif b/branding/core/core.jar/org/netbeans/core/startup/frame48.gif
index 3c6cdfd587..15fb04d164 100644
Binary files a/branding/core/core.jar/org/netbeans/core/startup/frame48.gif and b/branding/core/core.jar/org/netbeans/core/startup/frame48.gif differ
diff --git a/branding/core/core.jar/org/netbeans/core/startup/splash.gif b/branding/core/core.jar/org/netbeans/core/startup/splash.gif
index 34727c1a97..2a00a2053c 100644
Binary files a/branding/core/core.jar/org/netbeans/core/startup/splash.gif and b/branding/core/core.jar/org/netbeans/core/startup/splash.gif differ
diff --git a/build-windows-installer.xml b/build-windows-installer.xml
new file mode 100644
index 0000000000..b8efca0718
--- /dev/null
+++ b/build-windows-installer.xml
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Product Version: ${app.version}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build-windows.xml b/build-windows.xml
index d1b34d586d..768173d662 100644
--- a/build-windows.xml
+++ b/build-windows.xml
@@ -9,6 +9,7 @@
+
@@ -138,229 +139,5 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Product Version: ${app.version}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/build.xml b/build.xml
index 6cf86dae81..174bce4bf5 100644
--- a/build.xml
+++ b/build.xml
@@ -12,6 +12,7 @@
+
@@ -19,10 +20,10 @@
-
-
-
+
+
+
@@ -31,12 +32,11 @@
+
+
+
+
-
-
-
-
-
@@ -48,10 +48,8 @@
TSK_HOME: ${env.TSK_HOME}
-
-
-
-
+
+
+
+
+
+
@@ -229,6 +232,7 @@
+
diff --git a/docs/doxygen/modDevPython.dox b/docs/doxygen/modDevPython.dox
index 1dadb5955b..c78ebecded 100644
--- a/docs/doxygen/modDevPython.dox
+++ b/docs/doxygen/modDevPython.dox
@@ -50,6 +50,8 @@ from neededLib.mylib import neededClass
Jython will look in the module's folder to resolve these libraries.
+NOTE: Your code or third party modules may depend on core python modules that are not distributed with Jython. You will need to copy those libraries into your folder as though they were external libraries.
+
\section mod_dev_py_distribute Distribution
To distribute and share your Python module, ZIP up the folder and send it around. Other users of the module should expand the ZIP file and drop the folder into their Autopsy Python folder.
diff --git a/icons/icon.icns b/icons/icon.icns
index 2d55fafc0d..5cc5190d93 100644
Binary files a/icons/icon.icns and b/icons/icon.icns differ
diff --git a/icons/icon.ico b/icons/icon.ico
index efad7a3de4..696550c9ff 100644
Binary files a/icons/icon.ico and b/icons/icon.ico differ