Merge remote-tracking branch 'upstream/TL-list-view' into 799-tags-and-hashhit-columns-in-list-view

This commit is contained in:
jmillman 2016-05-26 14:32:00 -04:00
commit f044c824a9
26 changed files with 1258 additions and 437 deletions

View File

@ -22,6 +22,7 @@ import java.awt.Dimension;
import java.awt.Toolkit;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.logging.Level;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
@ -31,6 +32,7 @@ import org.apache.commons.io.FilenameUtils;
import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.coreutils.ModuleSettings;
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDb.KnownFilesType;
import org.sleuthkit.autopsy.modules.hashdatabase.HashDbManager.HashDbManagerException;
@ -249,9 +251,16 @@ final class HashDbImportDatabaseDialog extends javax.swing.JDialog {
}// </editor-fold>//GEN-END:initComponents
private void openButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openButtonActionPerformed
String lastBaseDirectory = Paths.get(PlatformUtil.getUserConfigDirectory(), "HashDatabases").toString();
if (ModuleSettings.settingExists(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)) {
fileChooser.setCurrentDirectory(new File(ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY)));
lastBaseDirectory = ModuleSettings.getConfigSetting(ModuleSettings.MAIN_SETTINGS, LAST_FILE_PATH_KEY);
}
File hashDbFolder = new File(lastBaseDirectory);
// create the folder if it doesn't exist
if (!hashDbFolder.exists()) {
hashDbFolder.mkdir();
}
fileChooser.setCurrentDirectory(hashDbFolder);
if (fileChooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
File databaseFile = fileChooser.getSelectedFile();
try {

View File

@ -225,7 +225,8 @@ ReportHTML.writeSum.noCaseNum=<i>No case number</i>
ReportBodyFile.generateReport.srcModuleName.text=TSK Body File
ReportExcel.endReport.srcModuleName.text=Excel Report
ReportHTML.writeIndex.srcModuleName.text=HTML Report
ReportKML.genReport.srcModuleName.text=KML Report
ReportKML.genReport.srcModuleName.text=Geospatial Data
ReportKML.genReport.reportName=KML Report
ReportGenerator.artTableColHdr.extension.text=Extension
ReportGenerator.artTableColHdr.mimeType.text=MIME Type
ReportGenerator.artTableColHdr.processorArchitecture.text=Processor Architecture

View File

@ -188,7 +188,7 @@ ReportExcel.endReport.srcModuleName.text=Excel\u30ec\u30dd\u30fc\u30c8
ReportGenerator.artTableColHdr.extension.text=\u62e1\u5f35\u5b50
ReportGenerator.artTableColHdr.mimeType.text=MIME\u30bf\u30a4\u30d7
ReportHTML.writeIndex.srcModuleName.text=HTML\u30ec\u30dd\u30fc\u30c8
ReportKML.genReport.srcModuleName.text=KML\u30ec\u30dd\u30fc\u30c8
ReportKML.genReport.reportName=KML\u30ec\u30dd\u30fc\u30c8
ReportGenerator.artTableColHdr.associatedArtifact=\u95a2\u4fc2\u3059\u308b\u30a2\u30fc\u30c6\u30a3\u30d5\u30a1\u30af\u30c8
ReportGenerator.artTableColHdr.count=\u30ab\u30a6\u30f3\u30c8
ReportGenerator.artTableColHdr.devMake=\u6a5f\u5668\u578b\u540d

View File

@ -2,7 +2,7 @@
*
* Autopsy Forensic Browser
*
* Copyright 2014 Basis Technology Corp.
* Copyright 2014-2016 Basis Technology Corp.
* contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -27,33 +27,56 @@ import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.datamodel.*;
import org.sleuthkit.autopsy.ingest.IngestManager;
import org.sleuthkit.datamodel.BlackboardArtifact;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.logging.Level;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.Namespace;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.apache.commons.lang.StringEscapeUtils;
import org.jdom2.CDATA;
import org.openide.filesystems.FileUtil;
/**
* Generates a KML file based on geo coordinates store in blackboard.
* Generates a KML file based on geospatial information from the BlackBoard.
*/
class ReportKML implements GeneralReportModule {
private static final Logger logger = Logger.getLogger(ReportKML.class.getName());
private static final String KML_STYLE_FILE = "style.kml";
private static final String REPORT_KML = "ReportKML.kml";
private static final String STYLESHEETS_PATH = "/org/sleuthkit/autopsy/report/stylesheets/";
private static ReportKML instance = null;
private Case currentCase;
private SleuthkitCase skCase;
private String reportPath;
private final SimpleDateFormat kmlDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssX");
private Namespace ns;
private final String SEP = "<br>";
private enum FeatureColor {
RED("style.kml#redFeature"),
GREEN("style.kml#greenFeature"),
BLUE("style.kml#blueFeature"),
PURPLE("style.kml#purpleFeature"),
WHITE("style.kml#whiteFeature"),
YELLOW("style.kml#yellowFeature");
private String color;
FeatureColor(String color) {
this.color = color;
}
String getColor() {
return this.color;
}
}
// Hidden constructor for the report
private ReportKML() {
@ -80,279 +103,684 @@ class ReportKML implements GeneralReportModule {
progressPanel.setIndeterminate(false);
progressPanel.start();
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.querying"));
reportPath = baseReportDir + "ReportKML.kml"; //NON-NLS
String reportPath2 = baseReportDir + "ReportKML.txt"; //NON-NLS
String kmlFileFullPath = baseReportDir + REPORT_KML; //NON-NLS
currentCase = Case.getCurrentCase();
skCase = currentCase.getSleuthkitCase();
progressPanel.updateStatusLabel(NbBundle.getMessage(this.getClass(), "ReportKML.progress.loading"));
// Check if ingest has finished
String ingestwarning = "";
if (IngestManager.getInstance().isIngestRunning()) {
ingestwarning = NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text");
}
progressPanel.setMaximumProgress(5);
progressPanel.increment();
// @@@ BC: I don't get why we do this in two passes.
// Why not just print the coordinates as we find them and make some utility methods to do the printing?
// Should pull out time values for all of these points and store in TimeSpan element
try {
try (BufferedWriter out = new BufferedWriter(new FileWriter(reportPath2))) {
ns = Namespace.getNamespace("", "http://www.opengis.net/kml/2.2"); //NON-NLS
double lat = 0; // temp latitude
double lon = 0; //temp longitude
AbstractFile aFile;
String geoPath = ""; // will hold values of images to put in kml
String imageName = "";
File f;
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) {
lat = 0;
lon = 0;
geoPath = "";
String extractedToPath;
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID()) //latitude
{
lat = attribute.getValueDouble();
}
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()) //longitude
{
lon = attribute.getValueDouble();
}
}
if (lon != 0 && lat != 0) {
aFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
if (aFile != null) {
extractedToPath = reportPath + aFile.getName();
geoPath = extractedToPath;
f = new File(extractedToPath);
f.createNewFile();
copyFileUsingStream(aFile, f);
imageName = aFile.getName();
}
out.write(String.valueOf(lat));
out.write(";");
out.write(String.valueOf(lon));
out.write(";");
out.write(String.valueOf(geoPath));
out.write(";");
out.write(String.valueOf(imageName));
out.write("\n");
// lat lon path name
}
}
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) {
lat = 0;
lon = 0;
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE.getTypeID()) //latitude
{
lat = attribute.getValueDouble();
}
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE.getTypeID()) //longitude
{
lon = attribute.getValueDouble();
}
}
if (lon != 0 && lat != 0) {
out.write(lat + ";" + lon + "\n");
}
}
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) {
lat = 0;
lon = 0;
double destlat = 0;
double destlon = 0;
String name = "";
String location = "";
for (BlackboardAttribute attribute : artifact.getAttributes()) {
if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START.getTypeID()) //latitude
{
lat = attribute.getValueDouble();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END.getTypeID()) //longitude
{
destlat = attribute.getValueDouble();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START.getTypeID()) //longitude
{
lon = attribute.getValueDouble();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END.getTypeID()) //longitude
{
destlon = attribute.getValueDouble();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME.getTypeID()) //longitude
{
name = attribute.getValueString();
} else if (attribute.getAttributeType().getTypeID() == BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION.getTypeID()) //longitude
{
location = attribute.getValueString();
}
}
// @@@ Should do something more fancy with these in KML and store them as a single point.
String display = name;
if (display.isEmpty()) {
display = location;
}
if (lon != 0 && lat != 0) {
out.write(NbBundle.getMessage(this.getClass(), "ReportKML.latLongStartPoint", lat, lon, display));
}
if (destlat != 0 && destlon != 0) {
out.write(NbBundle.getMessage(this.getClass(), "ReportKML.latLongEndPoint", destlat, destlon,
display));
}
}
out.flush();
progressPanel.increment();
/*
* Step 1: generate XML stub
*/
Namespace ns = Namespace.getNamespace("", "http://earth.google.com/kml/2.2"); //NON-NLS
// kml
Element kml = new Element("kml", ns); //NON-NLS
kml.addNamespaceDeclaration(Namespace.getNamespace("gx", "http://www.google.com/kml/ext/2.2")); //NON-NLS
kml.addNamespaceDeclaration(Namespace.getNamespace("kml", "http://www.opengis.net/kml/2.2")); //NON-NLS
kml.addNamespaceDeclaration(Namespace.getNamespace("atom", "http://www.w3.org/2005/Atom")); //NON-NLS
Document kmlDocument = new Document(kml);
// Document
Element document = new Element("Document", ns); //NON-NLS
kml.addContent(document);
// name
Element name = new Element("name", ns); //NON-NLS
name.setText("Java Generated KML Document"); //NON-NLS
ReportBranding rb = new ReportBranding();
name.setText(rb.getReportTitle() + " KML"); //NON-NLS
document.addContent(name);
/*
* Step 2: add in Style elements
// Check if ingest has finished
if (IngestManager.getInstance().isIngestRunning()) {
Element ingestwarning = new Element("snippet", ns); //NON-NLS
ingestwarning.addContent(NbBundle.getMessage(this.getClass(), "ReportBodyFile.ingestWarning.text")); //NON-NLS
document.addContent(ingestwarning);
}
// Create folder structure
Element gpsExifMetadataFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataExifMetadataFolder = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/camera-icon-16.png"); //NON-NLS
Element hrefExifMetadata = new Element("href", ns).addContent(cdataExifMetadataFolder); //NON-NLS
gpsExifMetadataFolder.addContent(new Element("Icon", ns).addContent(hrefExifMetadata)); //NON-NLS
Element gpsBookmarksFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataBookmarks = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gpsfav.png"); //NON-NLS
Element hrefBookmarks = new Element("href", ns).addContent(cdataBookmarks); //NON-NLS
gpsBookmarksFolder.addContent(new Element("Icon", ns).addContent(hrefBookmarks)); //NON-NLS
Element gpsLastKnownLocationFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataLastKnownLocation = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-lastlocation.png"); //NON-NLS
Element hrefLastKnownLocation = new Element("href", ns).addContent(cdataLastKnownLocation); //NON-NLS
gpsLastKnownLocationFolder.addContent(new Element("Icon", ns).addContent(hrefLastKnownLocation)); //NON-NLS
Element gpsRouteFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataRoute = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
Element hrefRoute = new Element("href", ns).addContent(cdataRoute); //NON-NLS
gpsRouteFolder.addContent(new Element("Icon", ns).addContent(hrefRoute)); //NON-NLS
Element gpsSearchesFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataSearches = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-search.png"); //NON-NLS
Element hrefSearches = new Element("href", ns).addContent(cdataSearches); //NON-NLS
gpsSearchesFolder.addContent(new Element("Icon", ns).addContent(hrefSearches)); //NON-NLS
Element gpsTrackpointsFolder = new Element("Folder", ns); //NON-NLS
CDATA cdataTrackpoints = new CDATA("https://raw.githubusercontent.com/sleuthkit/autopsy/develop/Core/src/org/sleuthkit/autopsy/images/gps-trackpoint.png"); //NON-NLS
Element hrefTrackpoints = new Element("href", ns).addContent(cdataTrackpoints); //NON-NLS
gpsTrackpointsFolder.addContent(new Element("Icon", ns).addContent(hrefTrackpoints)); //NON-NLS
gpsExifMetadataFolder.addContent(new Element("name", ns).addContent("EXIF Metadata")); //NON-NLS
gpsBookmarksFolder.addContent(new Element("name", ns).addContent("GPS Bookmarks")); //NON-NLS
gpsLastKnownLocationFolder.addContent(new Element("name", ns).addContent("GPS Last Known Location")); //NON-NLS
gpsRouteFolder.addContent(new Element("name", ns).addContent("GPS Routes")); //NON-NLS
gpsSearchesFolder.addContent(new Element("name", ns).addContent("GPS Searches")); //NON-NLS
gpsTrackpointsFolder.addContent(new Element("name", ns).addContent("GPS Trackpoints")); //NON-NLS
document.addContent(gpsExifMetadataFolder);
document.addContent(gpsBookmarksFolder);
document.addContent(gpsLastKnownLocationFolder);
document.addContent(gpsRouteFolder);
document.addContent(gpsSearchesFolder);
document.addContent(gpsTrackpointsFolder);
/**
* In the following code, nulls are okay, and are handled when we go to
* write out the KML feature. Nulls are expected to be returned from any
* method where the artifact is not found and is handled in the
* individual feature creation methods. This is done because we don't
* know beforehand which attributes will be included for which artifact,
* as anyone could write a module that adds additional attributes to an
* artifact.
*
*/
// Style
Element style = new Element("Style", ns); //NON-NLS
style.setAttribute("id", "redIcon"); //NON-NLS
document.addContent(style);
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_METADATA_EXIF)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
String desc = getDescriptionFromArtifact(artifact, "EXIF Metadata With Locations"); //NON-NLS
Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE));
Path destination = null;
if (lat != null && lat != 0.0 && lon != null && lon != 0.0) {
AbstractFile abstractFile = artifact.getSleuthkitCase().getAbstractFileById(artifact.getObjectID());
if (abstractFile != null) {
destination = Paths.get(baseReportDir, abstractFile.getName());
copyFileUsingStream(abstractFile, destination.toFile());
}
String formattedCoordinates = String.format("%.2f, %.2f", lat, lon);
gpsExifMetadataFolder.addContent(makePlacemarkWithPicture(abstractFile.getName(), FeatureColor.RED, desc, timestamp, point, destination, formattedCoordinates));
}
// IconStyle
Element iconStyle = new Element("IconStyle", ns); //NON-NLS
style.addContent(iconStyle);
}
} catch (TskCoreException | IOException ex) {
logger.log(Level.SEVERE, "Could not extract photos with EXIF metadata.", ex); //NON-NLS
}
// color
Element color = new Element("color", ns); //NON-NLS
color.setText("990000ff"); //NON-NLS
iconStyle.addContent(color);
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_BOOKMARK)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
String desc = getDescriptionFromArtifact(artifact, "GPS Bookmark"); //NON-NLS
Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
Element point = makePoint(lat, lon, getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE));
String bookmarkName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
String formattedCoordinates = String.format("%.2f, %.2f", lat, lon);
gpsBookmarksFolder.addContent(makePlacemark(bookmarkName, FeatureColor.BLUE, desc, timestamp, point, formattedCoordinates));
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get GPS Bookmarks from database.", ex); //NON-NLS
}
// Icon
Element icon = new Element("Icon", ns); //NON-NLS
iconStyle.addContent(icon);
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_LAST_KNOWN_LOCATION)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
String desc = getDescriptionFromArtifact(artifact, "GPS Last Known Location"); //NON-NLS
Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
Element point = makePoint(lat, lon, alt);
String formattedCoordinates = String.format("%.2f, %.2f", lat, lon);
gpsLastKnownLocationFolder.addContent(makePlacemark("Last Known Location", FeatureColor.PURPLE, desc, timestamp, point, formattedCoordinates)); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get GPS Last Known Location from database.", ex); //NON-NLS
}
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_ROUTE)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
String desc = getDescriptionFromArtifact(artifact, "GPS Route");
Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
Element route = makeLineString(latitudeStart, longitudeStart, altitude, latitudeEnd, longitudeEnd, altitude);
Element startingPoint = makePoint(latitudeStart, longitudeStart, altitude);
Element endingPoint = makePoint(latitudeEnd, longitudeEnd, altitude);
String formattedCoordinates = String.format("%.2f, %.2f to %.2f, %.2f", latitudeStart, longitudeStart, latitudeEnd, longitudeEnd);
gpsRouteFolder.addContent(makePlacemark("As-the-crow-flies Route", FeatureColor.GREEN, desc, timestamp, route, formattedCoordinates)); //NON-NLS
formattedCoordinates = String.format("%.2f, %.2f", latitudeStart, longitudeStart);
gpsRouteFolder.addContent(makePlacemark("Start", FeatureColor.GREEN, desc, timestamp, startingPoint, formattedCoordinates)); //NON-NLS
formattedCoordinates = String.format("%.2f, %.2f", latitudeEnd, longitudeEnd);
gpsRouteFolder.addContent(makePlacemark("End", FeatureColor.GREEN, desc, timestamp, endingPoint, formattedCoordinates)); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get GPS Routes from database.", ex); //NON-NLS
}
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_SEARCH)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
String desc = getDescriptionFromArtifact(artifact, "GPS Search"); //NON-NLS
Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
Element point = makePoint(lat, lon, alt);
String formattedCoordinates = String.format("%.2f, %.2f", lat, lon);
String searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
if (searchName == null || searchName.isEmpty()) {
searchName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
}
if (searchName == null || searchName.isEmpty()) {
searchName = "GPS Search";
}
gpsSearchesFolder.addContent(makePlacemark(searchName, FeatureColor.WHITE, desc, timestamp, point, formattedCoordinates)); //NON-NLS
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get GPS Searches from database.", ex); //NON-NLS
}
try {
for (BlackboardArtifact artifact : skCase.getBlackboardArtifacts(BlackboardArtifact.ARTIFACT_TYPE.TSK_GPS_TRACKPOINT)) {
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
String desc = getDescriptionFromArtifact(artifact, "GPS Trackpoint"); //NON-NLS
Double lat = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
Double lon = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
Double alt = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
Element point = makePoint(lat, lon, alt);
String formattedCoordinates = String.format("%.2f, %.2f, %.2f", lat, lon, alt);
String trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
if (trackName == null || trackName.isEmpty()) {
trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
}
if (trackName == null || trackName.isEmpty()) {
trackName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG);
}
if (trackName == null || trackName.isEmpty()) {
trackName = "GPS Trackpoint";
}
gpsTrackpointsFolder.addContent(makePlacemark(trackName, FeatureColor.YELLOW, desc, timestamp, point, formattedCoordinates));
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Could not get GPS Trackpoints from database.", ex); //NON-NLS
}
// href
Element href = new Element("href", ns); //NON-NLS
href.setText("http://www.cs.mun.ca/~hoeber/teaching/cs4767/notes/02.1-kml/circle.png"); //NON-NLS
icon.addContent(href);
progressPanel.increment();
/*
* Step 3: read data from source location and add in a Placemark
* for each data element
*/
File file = new File(reportPath2);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line = reader.readLine();
while (line != null) {
String[] lineParts = line.split(";");
if (lineParts.length > 1) {
String coordinates = lineParts[1].trim() + "," + lineParts[0].trim(); //lat,lon
// Placemark
Element placemark = new Element("Placemark", ns); //NON-NLS
document.addContent(placemark);
if (lineParts.length == 4) {
// name
Element pmName = new Element("name", ns); //NON-NLS
pmName.setText(lineParts[3].trim());
placemark.addContent(pmName);
String savedPath = lineParts[2].trim();
if (savedPath.isEmpty() == false) {
// Path
Element pmPath = new Element("Path", ns); //NON-NLS
pmPath.setText(savedPath);
placemark.addContent(pmPath);
// description
Element pmDescription = new Element("description", ns); //NON-NLS
String xml = "<![CDATA[ \n" + " <img src='file:///" + savedPath + "' width='400' /><br/&gt; \n"; //NON-NLS
StringEscapeUtils.unescapeXml(xml);
pmDescription.setText(xml);
placemark.addContent(pmDescription);
}
}
// styleUrl
Element pmStyleUrl = new Element("styleUrl", ns); //NON-NLS
pmStyleUrl.setText("#redIcon"); //NON-NLS
placemark.addContent(pmStyleUrl);
// Point
Element pmPoint = new Element("Point", ns); //NON-NLS
placemark.addContent(pmPoint);
// coordinates
Element pmCoordinates = new Element("coordinates", ns); //NON-NLS
pmCoordinates.setText(coordinates);
pmPoint.addContent(pmCoordinates);
}
// read the next line
line = reader.readLine();
}
}
progressPanel.increment();
/*
* Step 4: write the XML file
*/
try (FileOutputStream writer = new FileOutputStream(reportPath)) {
try (FileOutputStream writer = new FileOutputStream(kmlFileFullPath)) {
XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
outputter.output(kmlDocument, writer);
Case.getCurrentCase().addReport(reportPath, NbBundle.getMessage(this.getClass(),
"ReportKML.genReport.srcModuleName.text"), "");
Case.getCurrentCase().addReport(kmlFileFullPath,
NbBundle.getMessage(this.getClass(), "ReportKML.genReport.srcModuleName.text"),
NbBundle.getMessage(this.getClass(), "ReportKML.genReport.reportName"));
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not write the KML file.", ex); //NON-NLS
} catch (TskCoreException ex) {
String errorMessage = String.format("Error adding %s to case as a report", reportPath); //NON-NLS
logger.log(Level.SEVERE, errorMessage, ex);
}
} catch (IOException ex) {
logger.log(Level.WARNING, "Could not write the KML report.", ex); //NON-NLS
}
logger.log(Level.SEVERE, "Could not write the KML file.", ex); //NON-NLS
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
} catch (TskCoreException ex) {
logger.log(Level.WARNING, "Failed to get the unique path.", ex); //NON-NLS
String errorMessage = String.format("Error adding %s to case as a report", kmlFileFullPath); //NON-NLS
logger.log(Level.SEVERE, errorMessage, ex);
progressPanel.complete(ReportProgressPanel.ReportStatus.ERROR);
}
// Copy the style sheet
try {
InputStream input = getClass().getResourceAsStream(STYLESHEETS_PATH + KML_STYLE_FILE); // Preserve slash direction
OutputStream output = new FileOutputStream(baseReportDir + KML_STYLE_FILE); // Preserve slash direction
FileUtil.copy(input, output);
} catch (IOException ex) {
logger.log(Level.SEVERE, "Error placing KML stylesheet. The .KML file will not function properly.", ex); //NON-NLS
}
progressPanel.increment();
progressPanel.complete(ReportProgressPanel.ReportStatus.COMPLETE);
}
public static void copyFileUsingStream(AbstractFile file, File jFile) throws IOException {
InputStream is = new ReadContentInputStream(file);
OutputStream os = new FileOutputStream(jFile);
byte[] buffer = new byte[8192];
int length;
/**
* Get a Double from an artifact if it exists, return null otherwise.
*
* @param artifact The artifact to query
* @param type The attribute type we're looking for
*
* @return The Double if it exists, or null if not
*/
private Double getDouble(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
Double returnValue = null;
try {
BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type));
if (bba != null) {
Double value = bba.getValueDouble();
if (value != null) {
returnValue = value;
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting Double value: " + type.toString(), ex); //NON-NLS
}
return returnValue;
}
/**
* Get a Long from an artifact if it exists, return null otherwise.
*
* @param artifact The artifact to query
* @param type The attribute type we're looking for
*
* @return The Long if it exists, or null if not
*/
private Long getLong(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
Long returnValue = null;
try {
BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type));
if (bba != null) {
Long value = bba.getValueLong();
if (value != null) {
returnValue = value;
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting Long value: " + type.toString(), ex); //NON-NLS
}
return returnValue;
}
/**
* Get an Integer from an artifact if it exists, return null otherwise.
*
* @param artifact The artifact to query
* @param type The attribute type we're looking for
*
* @return The Integer if it exists, or null if not
*/
private Integer getInteger(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
Integer returnValue = null;
try {
BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type));
if (bba != null) {
Integer value = bba.getValueInt();
if (value != null) {
returnValue = value;
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting Integer value: " + type.toString(), ex); //NON-NLS
}
return returnValue;
}
/**
* Get a String from an artifact if it exists, return null otherwise.
*
* @param artifact The artifact to query
* @param type The attribute type we're looking for
*
* @return The String if it exists, or null if not
*/
private String getString(BlackboardArtifact artifact, BlackboardAttribute.ATTRIBUTE_TYPE type) {
String returnValue = null;
try {
BlackboardAttribute bba = artifact.getAttribute(new BlackboardAttribute.Type(type));
if (bba != null) {
String value = bba.getValueString();
if (value != null && !value.isEmpty()) {
returnValue = value;
}
}
} catch (TskCoreException ex) {
logger.log(Level.SEVERE, "Error getting String value: " + type.toString(), ex); //NON-NLS
}
return returnValue;
}
/**
* This method creates a text description for a map feature using all the
* geospatial and time data we can for the Artifact. It queries the
* following attributes:
*
* TSK_GEO_LATITUDE 54; TSK_GEO_LONGITUDE 55; TSK_GEO_LATITUDE_START 98;
* TSK_GEO_LATITUDE_END 99; TSK_GEO_LONGITUDE_START 100;
* TSK_GEO_LONGITUDE_END 101; TSK_GEO_VELOCITY 56; TSK_GEO_ALTITUDE 57;
* TSK_GEO_BEARING 58; TSK_GEO_HPRECISION 59; TSK_GEO_VPRECISION 60;
* TSK_GEO_MAPDATUM 61; TSK_DATETIME_START 83; TSK_DATETIME_END 84;
* TSK_LOCATION 86; TSK_PATH_SOURCE 94;
*
* @param artifact the artifact to query.
* @param featureType the type of Artifact we're working on.
*
* @return a String with the information we have available
*/
private String getDescriptionFromArtifact(BlackboardArtifact artifact, String featureType) {
StringBuilder result = new StringBuilder("<h3>" + featureType + "</h3>"); //NON-NLS
String name = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_NAME);
if (name != null && !name.isEmpty()) {
result.append("<b>Name:</b> ").append(name).append(SEP); //NON-NLS
}
String location = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_LOCATION);
if (location != null && !location.isEmpty()) {
result.append("<b>Location:</b> ").append(location).append(SEP); //NON-NLS
}
Long timestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME);
if (timestamp != null) {
result.append("<b>Timestamp:</b> ").append(getTimeStamp(timestamp)).append(SEP); //NON-NLS
result.append("<b>Unix timestamp:</b> ").append(timestamp).append(SEP); //NON-NLS
}
Long startingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_START);
if (startingTimestamp != null) {
result.append("<b>Starting Timestamp:</b> ").append(getTimeStamp(startingTimestamp)).append(SEP); //NON-NLS
result.append("<b>Starting Unix timestamp:</b> ").append(startingTimestamp).append(SEP); //NON-NLS
}
Long endingTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_END);
if (endingTimestamp != null) {
result.append("<b>Ending Timestamp:</b> ").append(getTimeStamp(endingTimestamp)).append(SEP); //NON-NLS
result.append("<b>Ending Unix timestamp:</b> ").append(endingTimestamp).append(SEP); //NON-NLS
}
Long createdTimestamp = getLong(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DATETIME_CREATED);
if (createdTimestamp != null) {
result.append("<b>Created Timestamp:</b> ").append(getTimeStamp(createdTimestamp)).append(SEP); //NON-NLS
result.append("<b>Created Unix timestamp:</b> ").append(createdTimestamp).append(SEP); //NON-NLS
}
Double latitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE);
if (latitude != null) {
result.append("<b>Latitude:</b> ").append(latitude).append(SEP); //NON-NLS
}
Double longitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE);
if (longitude != null) {
result.append("<b>Longitude:</b> ").append(longitude).append(SEP); //NON-NLS
}
Double latitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_START);
if (latitudeStart != null) {
result.append("<b>Latitude Start:</b> ").append(latitudeStart).append(SEP); //NON-NLS
}
Double longitudeStart = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_START);
if (longitudeStart != null) {
result.append("<b>Longitude Start:</b> ").append(longitudeStart).append(SEP); //NON-NLS
}
Double latitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LATITUDE_END);
if (latitudeEnd != null) {
result.append("<b>Latitude End:</b> ").append(latitudeEnd).append(SEP); //NON-NLS
}
Double longitudeEnd = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_LONGITUDE_END);
if (longitudeEnd != null) {
result.append("<b>Longitude End:</b> ").append(longitudeEnd).append(SEP); //NON-NLS
}
Double velocity = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VELOCITY);
if (velocity != null) {
result.append("<b>Velocity:</b> ").append(velocity).append(SEP); //NON-NLS
}
Double altitude = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_ALTITUDE);
if (altitude != null) {
result.append("<b>Altitude:</b> ").append(altitude).append(SEP); //NON-NLS
}
Double bearing = getDouble(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_BEARING);
if (bearing != null) {
result.append("<b>Bearing:</b> ").append(bearing).append(SEP); //NON-NLS
}
Integer hPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_HPRECISION);
if (hPrecision != null) {
result.append("<b>Horizontal Precision Figure of Merit:</b> ").append(hPrecision).append(SEP); //NON-NLS
}
Integer vPrecision = getInteger(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_VPRECISION);
if (vPrecision != null) {
result.append("<b>Vertical Precision Figure of Merit:</b> ").append(vPrecision).append(SEP); //NON-NLS
}
String mapDatum = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_GEO_MAPDATUM);
if (mapDatum != null && !mapDatum.isEmpty()) {
result.append("<b>Map Datum:</b> ").append(mapDatum).append(SEP); //NON-NLS
}
String programName = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PROG_NAME);
if (programName != null && !programName.isEmpty()) {
result.append("<b>Reported by:</b> ").append(programName).append(SEP); //NON-NLS
}
String flag = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_FLAG);
if (flag != null && !flag.isEmpty()) {
result.append("<b>Flag:</b> ").append(flag).append(SEP); //NON-NLS
}
String pathSource = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PATH_SOURCE);
if (pathSource != null && !pathSource.isEmpty()) {
result.append("<b>Source:</b> ").append(pathSource).append(SEP); //NON-NLS
}
String deviceMake = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MAKE);
if (deviceMake != null && !deviceMake.isEmpty()) {
result.append("<b>Device Make:</b> ").append(deviceMake).append(SEP); //NON-NLS
}
String deviceModel = getString(artifact, BlackboardAttribute.ATTRIBUTE_TYPE.TSK_DEVICE_MODEL);
if (deviceModel != null && !deviceModel.isEmpty()) {
result.append("<b>Device Model:</b> ").append(deviceModel).append(SEP); //NON-NLS
}
return result.toString();
}
private String getTimeStamp(long timeStamp) {
return kmlDateFormat.format(new java.util.Date(timeStamp * 1000));
}
/**
* Create a Point for use in a Placemark. Note in this method altitude is
* ignored, as Google Earth apparently has trouble using altitudes for
* LineStrings, though the parameters are still in the call. Also note that
* any null value passed in will be set to 0.0, under the idea that it is
* better to show some data with gaps, than to show nothing at all.
*
* @param latitude point latitude
* @param longitude point longitude
* @param altitude point altitude. Currently ignored.
*
* @return the Point as an Element
*/
private Element makePoint(Double latitude, Double longitude, Double altitude) {
if (latitude == null) {
latitude = 0.0;
}
if (longitude == null) {
longitude = 0.0;
}
if (altitude == null) {
altitude = 0.0;
}
Element point = new Element("Point", ns); //NON-NLS
// KML uses lon, lat. Deliberately reversed.
Element coordinates = new Element("coordinates", ns).addContent(longitude + "," + latitude + "," + altitude); //NON-NLS
if (altitude != 0) {
/*
Though we are including a non-zero altitude, clamp it to the
ground because inaccuracies from the GPS data can cause the terrain
to occlude points when zoomed in otherwise. Show the altitude, but
keep the point clamped to the ground. We may change this later for
flying GPS sensors.
*/
Element altitudeMode = new Element("altitudeMode", ns).addContent("clampToGround"); //NON-NLS
point.addContent(altitudeMode);
}
point.addContent(coordinates);
return point;
}
/**
* Create a LineString for use in a Placemark. Note in this method, start
* and stop altitudes get ignored, as Google Earth apparently has trouble
* using altitudes for LineStrings, though the parameters are still in the
* call. Also note that any null value passed in will be set to 0.0, under
* the idea that it is better to show some data with gaps, than to show
* nothing at all.
*
* @param startLatitude Starting latitude
* @param startLongitude Starting longitude
* @param startAltitude Starting altitude. Currently ignored.
* @param stopLatitude Ending latitude
* @param stopLongitude Ending longitude
* @param stopAltitude Ending altitude. Currently ignored.
*
* @return the Line as an Element
*/
private Element makeLineString(Double startLatitude, Double startLongitude, Double startAltitude, Double stopLatitude, Double stopLongitude, Double stopAltitude) {
if (startLatitude == null) {
startLatitude = 0.0;
}
if (startLongitude == null) {
startLongitude = 0.0;
}
if (startAltitude == null) {
startAltitude = 0.0;
}
if (stopLatitude == null) {
stopLatitude = 0.0;
}
if (stopLongitude == null) {
stopLongitude = 0.0;
}
if (stopAltitude == null) {
stopAltitude = 0.0;
}
Element lineString = new Element("LineString", ns); //NON-NLS
lineString.addContent(new Element("extrude", ns).addContent("1")); //NON-NLS
lineString.addContent(new Element("tessellate", ns).addContent("1")); //NON-NLS
lineString.addContent(new Element("altitudeMode", ns).addContent("clampToGround")); //NON-NLS
// KML uses lon, lat. Deliberately reversed.
lineString.addContent(new Element("coordinates", ns).addContent(
startLongitude + "," + startLatitude + ",0.0,"
+ stopLongitude + "," + stopLatitude + ",0.0")); //NON-NLS
return lineString;
}
/**
* Make a Placemark for use in displaying features. Takes a
* coordinate-bearing feature (Point, LineString, etc) and places it in the
* Placemark element.
*
* @param name Placemark name
* @param color Placemark color
* @param description Description for the info bubble on the map
* @param timestamp Placemark timestamp
* @param feature The feature to show. Could be Point, LineString, etc.
* @param coordinates The coordinates to display in the list view snippet
*
* @return the entire KML placemark
*/
private Element makePlacemark(String name, FeatureColor color, String description, Long timestamp, Element feature, String coordinates) {
Element placemark = new Element("Placemark", ns); //NON-NLS
if (name != null && !name.isEmpty()) {
placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
} else if (timestamp != null) {
placemark.addContent(new Element("name", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
} else {
placemark.addContent(new Element("name", ns).addContent("")); //NON-NLS
}
placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
placemark.addContent(new Element("description", ns).addContent(description)); //NON-NLS
if (timestamp != null) {
Element time = new Element("TimeStamp", ns); //NON-NLS
time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
placemark.addContent(time);
}
placemark.addContent(feature);
if (coordinates != null && !coordinates.isEmpty()) {
placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
}
return placemark;
}
/**
* Make a Placemark for use in displaying features. Takes a
* coordinate-bearing feature (Point, LineString, etc) and places it in the
* Placemark element.
*
* @param name Placemark name
* @param color Placemark color
* @param description Description for the info bubble on the map
* @param timestamp Placemark timestamp
* @param feature The feature to show. Could be Point, LineString, etc.
* @param path The path to the file on disk
* @param coordinates The coordinates to display in the list view snippet
*
* @return the entire KML Placemark, including a picture.
*/
private Element makePlacemarkWithPicture(String name, FeatureColor color, String description, Long timestamp, Element feature, Path path, String coordinates) {
Element placemark = new Element("Placemark", ns); //NON-NLS
if (name != null && !name.isEmpty()) {
placemark.addContent(new Element("name", ns).addContent(name)); //NON-NLS
}
placemark.addContent(new Element("styleUrl", ns).addContent(color.getColor())); //NON-NLS
Element desc = new Element("description", ns); //NON-NLS
if (path != null) {
String pathAsString = path.toString();
if (pathAsString != null && !pathAsString.isEmpty()) {
String image = "<img src='file:///" + pathAsString + "' width='400'/>"; //NON-NLS
desc.addContent(image);
}
}
desc.addContent(description + "<b>File Path:</b> " + path.toString());
placemark.addContent(desc);
if (timestamp != null) {
Element time = new Element("TimeStamp", ns); //NON-NLS
time.addContent(new Element("when", ns).addContent(getTimeStamp(timestamp))); //NON-NLS
placemark.addContent(time);
}
placemark.addContent(feature);
if (coordinates != null && !coordinates.isEmpty()) {
placemark.addContent(new Element("snippet", ns).addContent(coordinates)); //NON-NLS
}
return placemark;
}
/**
* Extracts the file to the output folder.
*
* @param inputFile The input AbstractFile to copy
* @param outputFile the output file
*
* @throws IOException
*/
private void copyFileUsingStream(AbstractFile inputFile, File outputFile) throws IOException {
byte[] buffer = new byte[65536];
int length;
outputFile.createNewFile();
try (InputStream is = new ReadContentInputStream(inputFile);
OutputStream os = new FileOutputStream(outputFile)) {
while ((length = is.read(buffer)) != -1) {
os.write(buffer, 0, length);
}
} finally {
is.close();
os.close();
}
}

View File

@ -0,0 +1,299 @@
<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:gx="http://www.google.com/kml/ext/2.2" xmlns:kml="http://www.opengis.net/kml/2.2">
<Document>
<StyleMap id="yellowFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_YellowPushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_YellowPushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_YellowPushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>0</scale>
</LabelStyle>
<LineStyle>
<color>FF00FFFF</color>
<width>5.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<Style id="h_YellowPushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/ylw-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FF00FFFF</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<StyleMap id="blueFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_bluePushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_bluePushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_bluePushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>0</scale>
</LabelStyle>
<LineStyle>
<color>FFE63900</color>
<width>5.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<Style id="h_bluePushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/blue-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FFE63900</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<StyleMap id="redFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_redPushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_redPushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_redPushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/red-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>0</scale>
</LabelStyle>
<LineStyle>
<color>FF0000FF</color>
<width>5.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<Style id="h_redPushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/red-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FF0000FF</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<StyleMap id="greenFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_greenPushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_greenPushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_greenPushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FF00CC00</color>
<width>5.0</width>
</LineStyle>
<BalloonStyle>
<text>
A route was planned between these two points. The green line connecting the points is not the actual route, but it indicates which Start and End points are associated with each other.
$[description]
</text>
</BalloonStyle>
</Style>
<Style id="h_greenPushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/grn-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FF00CC00</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>
A route was planned between these two points. The green line connecting the points is not the actual route, but it indicates which Start and End points are associated with each other.
$[description]
</text>
</BalloonStyle>
</Style>
<StyleMap id="purpleFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_purplePushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_purplePushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_purplePushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/purple-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FFCC0066</color>
<width>5.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<Style id="h_purplePushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/purple-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FFCC0066</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
<StyleMap id="whiteFeature">
<Pair>
<key>normal</key>
<styleUrl>#n_whitePushpin</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#h_whitePushpin</styleUrl>
</Pair>
</StyleMap>
<Style id="n_whitePushpin">
<IconStyle>
<scale>1.0</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/wht-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>0</scale>
</LabelStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
<LineStyle>
<color>FFFFFFFF</color>
<width>5.0</width>
</LineStyle>
</Style>
<Style id="h_whitePushpin">
<IconStyle>
<scale>1.3</scale>
<Icon>
<href>http://maps.google.com/mapfiles/kml/pushpin/wht-pushpin.png</href>
</Icon>
<hotSpot x="20" y="2" xunits="pixels" yunits="pixels" />
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<LineStyle>
<color>FFFFFFFF</color>
<width>10.0</width>
</LineStyle>
<BalloonStyle>
<text>$[description]</text>
</BalloonStyle>
</Style>
</Document>
</kml>

View File

@ -54,7 +54,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
<SubComponents>
<Container class="javafx.embed.swing.JFXPanel" name="jFXVizPanel">
<Container class="javafx.embed.swing.JFXPanel" name="jFXViewPanel">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
<JSplitPaneConstraints position="left"/>

View File

@ -61,7 +61,7 @@ import org.sleuthkit.autopsy.timeline.explorernodes.EventRootNode;
import org.sleuthkit.autopsy.timeline.ui.HistoryToolBar;
import org.sleuthkit.autopsy.timeline.ui.StatusBar;
import org.sleuthkit.autopsy.timeline.ui.TimeZonePanel;
import org.sleuthkit.autopsy.timeline.ui.VisualizationPanel;
import org.sleuthkit.autopsy.timeline.ui.ViewFrame;
import org.sleuthkit.autopsy.timeline.ui.detailview.tree.EventsTree;
import org.sleuthkit.autopsy.timeline.ui.filtering.FilterSetPanel;
import org.sleuthkit.autopsy.timeline.zooming.ZoomSettingsPane;
@ -137,7 +137,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
LOGGER.log(Level.SEVERE, "Failed to lookup Sleuthkit object backing a SingleEvent.", ex); // NON-NLS
Platform.runLater(() -> {
Notifications.create()
.owner(jFXVizPanel.getScene().getWindow())
.owner(jFXViewPanel.getScene().getWindow())
.text(Bundle.TimelineTopComponent_selectedEventListener_errorMsg())
.showError();
});
@ -260,8 +260,8 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
final VBox leftVBox = new VBox(5, timeZonePanel, historyToolBar, zoomSettingsPane, leftTabPane);
SplitPane.setResizableWithParent(leftVBox, Boolean.FALSE);
final VisualizationPanel visualizationPanel = new VisualizationPanel(controller, eventsTree);
final SplitPane mainSplitPane = new SplitPane(leftVBox, visualizationPanel);
final ViewFrame viewFrame = new ViewFrame(controller, eventsTree);
final SplitPane mainSplitPane = new SplitPane(leftVBox, viewFrame);
mainSplitPane.setDividerPositions(0);
final Scene scene = new Scene(mainSplitPane);
@ -278,7 +278,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
});
//add ui componenets to JFXPanels
jFXVizPanel.setScene(scene);
jFXViewPanel.setScene(scene);
jFXstatusPanel.setScene(new Scene(new StatusBar(controller)));
}
@ -297,7 +297,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
jFXstatusPanel = new javafx.embed.swing.JFXPanel();
splitYPane = new javax.swing.JSplitPane();
jFXVizPanel = new javafx.embed.swing.JFXPanel();
jFXViewPanel = new javafx.embed.swing.JFXPanel();
horizontalSplitPane = new javax.swing.JSplitPane();
leftFillerPanel = new javax.swing.JPanel();
rightfillerPanel = new javax.swing.JPanel();
@ -308,7 +308,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
splitYPane.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
splitYPane.setResizeWeight(0.9);
splitYPane.setPreferredSize(new java.awt.Dimension(1024, 400));
splitYPane.setLeftComponent(jFXVizPanel);
splitYPane.setLeftComponent(jFXViewPanel);
horizontalSplitPane.setDividerLocation(600);
horizontalSplitPane.setResizeWeight(0.5);
@ -361,7 +361,7 @@ public final class TimeLineTopComponent extends TopComponent implements Explorer
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSplitPane horizontalSplitPane;
private javafx.embed.swing.JFXPanel jFXVizPanel;
private javafx.embed.swing.JFXPanel jFXViewPanel;
private javafx.embed.swing.JFXPanel jFXstatusPanel;
private javax.swing.JPanel leftFillerPanel;
private javax.swing.JPanel rightfillerPanel;

View File

@ -71,7 +71,7 @@ public class SaveSnapshotAsReport extends Action {
"Timeline.ModuleName=Timeline",
"SaveSnapShotAsReport.action.dialogs.title=Timeline",
"SaveSnapShotAsReport.action.name.text=Snapshot Report",
"SaveSnapShotAsReport.action.longText=Save a screen capture of the visualization as a report.",
"SaveSnapShotAsReport.action.longText=Save a screen capture of the current view of the timeline as a report.",
"# {0} - report file path",
"SaveSnapShotAsReport.ReportSavedAt=Report saved at [{0}]",
"SaveSnapShotAsReport.Success=Success",

View File

@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.timeline.datamodel;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
@ -43,8 +44,7 @@ public class CombinedEvent {
* epoch.
* @param description The full description shared by all the combined events
* @param fileID The ID of the file all the combined events are for.
* @param eventMap A map from EventType to the ID of the event for the
* given file ID with that type.
* @param eventMap A map from EventType to event ID.
*/
public CombinedEvent(long epochMillis, String description, long fileID, Map<EventType, Long> eventMap) {
this.epochMillis = epochMillis;
@ -105,7 +105,45 @@ public class CombinedEvent {
*
* @return An arbitrary representative event ID for the combined events.
*/
public Long getRepresentitiveEventID() {
public Long getRepresentativeEventID() {
return eventTypeMap.values().stream().findFirst().get();
}
@Override
public int hashCode() {
int hash = 3;
hash = 53 * hash + (int) (this.fileID ^ (this.fileID >>> 32));
hash = 53 * hash + (int) (this.epochMillis ^ (this.epochMillis >>> 32));
hash = 53 * hash + Objects.hashCode(this.description);
hash = 53 * hash + Objects.hashCode(this.eventTypeMap);
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final CombinedEvent other = (CombinedEvent) obj;
if (this.fileID != other.fileID) {
return false;
}
if (this.epochMillis != other.epochMillis) {
return false;
}
if (!Objects.equals(this.description, other.description)) {
return false;
}
if (!Objects.equals(this.eventTypeMap, other.eventTypeMap)) {
return false;
}
return true;
}
}

View File

@ -20,7 +20,7 @@ package org.sleuthkit.autopsy.timeline.events;
/**
* A "local" event published by filteredEventsModel to indicate that the user
* requested that the current visualization be refreshed with out changing any
* requested that the current view be refreshed with out changing any
* of the parameters ( to include more up to date tag data for example.)
*
* This event is not intended for use out side of the Timeline module.

View File

@ -70,7 +70,7 @@ public class SnapShotReportWriter {
* @param zoomParams The ZoomParams in effect when the snapshot was
* taken.
* @param generationDate The generation Date of the report.
* @param snapshot A snapshot of the visualization to include in the
* @param snapshot A snapshot of the view to include in the
* report.
*/
public SnapShotReportWriter(Case currentCase, Path reportFolderPath, String reportName, ZoomParams zoomParams, Date generationDate, BufferedImage snapshot) {

View File

@ -136,7 +136,7 @@ public abstract class AbstractTimeLineView extends BorderPane {
/**
* Refresh this view based on current state of zoom / filters. Primarily
* this invokes the background VisualizationUpdateTask returned by
* this invokes the background ViewRefreshTask returned by
* getUpdateTask(), which derived classes must implement.
*
* TODO: replace this logic with a javafx Service ? -jm
@ -291,11 +291,11 @@ public abstract class AbstractTimeLineView extends BorderPane {
* @throws Exception If there is an unhandled exception during the
* background operation
*/
@NbBundle.Messages(value = {"VisualizationUpdateTask.preparing=Analyzing zoom and filter settings"})
@NbBundle.Messages(value = {"ViewRefreshTask.preparing=Analyzing zoom and filter settings"})
@Override
protected Boolean call() throws Exception {
updateProgress(-1, 1);
updateMessage(Bundle.VisualizationUpdateTask_preparing());
updateMessage(Bundle.ViewRefreshTask_preparing());
Platform.runLater(() -> {
MaskerPane maskerPane = new MaskerPane();
maskerPane.textProperty().bind(messageProperty());

View File

@ -57,7 +57,7 @@ import org.sleuthkit.autopsy.timeline.TimeLineController;
import org.sleuthkit.autopsy.timeline.datamodel.eventtype.EventType;
/**
* Abstract base class for TimeLineChart based visualizations.
* Abstract base class for TimeLineChart based views.
*
* @param <X> The type of data plotted along the x axis
* @param <Y> The type of data plotted along the y axis
@ -74,12 +74,12 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
private static final Logger LOGGER = Logger.getLogger(AbstractTimelineChart.class.getName());
@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());
@NbBundle.Messages("AbstractTimelineChart.defaultTooltip.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.AbstractTimelineChart_defaultTooltip_text());
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
* Get the tool tip to use for this view when no more specific
* Tooltip is needed.
*
* @return The default Tooltip.
@ -89,10 +89,10 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
}
/**
* The visualization nodes that are selected.
* The nodes that are selected.
*
* @return An ObservableList<NodeType> of the nodes that are selected in
* this visualization.
* this view.
*/
protected ObservableList<NodeType> getSelectedNodes() {
return selectedNodes;
@ -127,18 +127,18 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
}
/**
* Get the CharType that implements this visualization.
* Get the CharType that implements this view.
*
* @return The CharType that implements this visualization.
* @return The CharType that implements this view.
*/
protected ChartType getChart() {
return chart;
}
/**
* Set the ChartType that implements this visualization.
* Set the ChartType that implements this view.
*
* @param chart The ChartType that implements this visualization.
* @param chart The ChartType that implements this view.
*/
@ThreadConfined(type = ThreadConfined.ThreadType.JFX)
protected void setChart(ChartType chart) {
@ -147,7 +147,7 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
}
/**
* Apply this visualization's 'selection effect' to the given node.
* Apply this view's 'selection effect' to the given node.
*
* @param node The node to apply the 'effect' to.
*/
@ -156,7 +156,7 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
}
/**
* Remove this visualization's 'selection effect' from the given node.
* Remove this view's 'selection effect' from the given node.
*
* @param node The node to remvoe the 'effect' from.
*/
@ -168,7 +168,7 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
* 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
* @param value A value along this view's x axis
*
* @return True if the tick label for the given value should be bold ( has
* relevant data), false otherwise
@ -176,7 +176,7 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
abstract protected Boolean isTickBold(X value);
/**
* Apply this visualization's 'selection effect' to the given node, if
* Apply this view'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
@ -203,16 +203,16 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
abstract protected double getTickSpacing();
/**
* Get the X-Axis of this Visualization's chart
* Get the X-Axis of this view's chart
*
* @return The horizontal axis used by this Visualization's chart
* @return The horizontal axis used by this view's chart
*/
abstract protected Axis<X> getXAxis();
/**
* Get the Y-Axis of this Visualization's chart
* Get the Y-Axis of this view's chart
*
* @return The vertical axis used by this Visualization's chart
* @return The vertical axis used by this view's chart
*/
abstract protected Axis<Y> getYAxis();
@ -252,7 +252,7 @@ public abstract class AbstractTimelineChart<X, Y, NodeType extends Node, ChartTy
/**
* Constructor
*
* @param controller The TimelineController for this visualization.
* @param controller The TimelineController for this view.
*/
protected AbstractTimelineChart(TimeLineController controller) {
super(controller);

View File

@ -35,13 +35,13 @@ Timeline.ui.ZoomRanges.threeyears.text=Three Years
Timeline.ui.ZoomRanges.fiveyears.text=Five Years
Timeline.ui.ZoomRanges.tenyears.text=Ten Years
Timeline.ui.ZoomRanges.all.text=All
VisualizationPanel.histogramTask.title=Rebuild Histogram
VisualizationPanel.histogramTask.preparing=preparing
VisualizationPanel.histogramTask.resetUI=resetting ui
VisualizationPanel.histogramTask.queryDb=querying db
VisualizationPanel.histogramTask.updateUI2=updating ui
VisualizationPanel.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.
VisualizationPanel.zoomButton.text=Zoom to events
ViewFrame.histogramTask.title=Rebuild Histogram
ViewFrame.histogramTask.preparing=preparing
ViewFrame.histogramTask.resetUI=resetting ui
ViewFrame.histogramTask.queryDb=querying db
ViewFrame.histogramTask.updateUI2=updating ui
ViewFrame.noEventsDialogLabel.text=There are no events visible with the current zoom / filter settings.
ViewFrame.zoomButton.text=Zoom to events
TimeZonePanel.localRadio.text=Local Time Zone
TimeZonePanel.otherRadio.text=GMT / UTC
VisualizationPanel.resetFiltersButton.text=Reset all filters
ViewFrame.resetFiltersButton.text=Reset all filters

View File

@ -2,23 +2,23 @@ Timeline.node.root=\u30eb\u30fc\u30c8
Timeline.ui.TimeLineChart.tooltip.text=\u30c0\u30d6\u30eb\u30af\u30ea\u30c3\u30af\u3067\u4e0b\u8a18\u306e\u7bc4\u56f2\u3078\u30ba\u30fc\u30e0\uff1a\n{0}\u301c{1}\n\u53f3\u30af\u30ea\u30c3\u30af\u3067\u5143\u306b\u623b\u308a\u307e\u3059\u3002
Timeline.ui.ZoomRanges.all.text=\u5168\u3066
VisualizationPanel.histogramTask.preparing=\u6e96\u5099\u4e2d
VisualizationPanel.histogramTask.queryDb=DB\u3092\u30af\u30a8\u30ea\u4e2d
VisualizationPanel.histogramTask.resetUI=ui\u3092\u518d\u8a2d\u5b9a\u4e2d
VisualizationPanel.histogramTask.title=\u30d2\u30b9\u30c8\u30b0\u30e9\u30e0\u3092\u518d\u30d3\u30eb\u30c9
VisualizationPanel.histogramTask.updateUI2=ui\u3092\u66f4\u65b0\u4e2d
ViewFrame.histogramTask.preparing=\u6e96\u5099\u4e2d
ViewFrame.histogramTask.queryDb=DB\u3092\u30af\u30a8\u30ea\u4e2d
ViewFrame.histogramTask.resetUI=ui\u3092\u518d\u8a2d\u5b9a\u4e2d
ViewFrame.histogramTask.title=\u30d2\u30b9\u30c8\u30b0\u30e9\u30e0\u3092\u518d\u30d3\u30eb\u30c9
ViewFrame.histogramTask.updateUI2=ui\u3092\u66f4\u65b0\u4e2d
TimeZonePanel.localRadio.text=\u30ed\u30fc\u30ab\u30eb\u30bf\u30a4\u30e0\u30be\u30fc\u30f3
VisualizationPanel.countsToggle.text=\u30ab\u30a6\u30f3\u30c8
VisualizationPanel.detailsToggle.text=\u8a73\u7d30
VisualizationPanel.endLabel.text=\u30a8\u30f3\u30c9\uff1a
VisualizationPanel.noEventsDialogLabel.text=\u73fe\u5728\u306e\u30ba\u30fc\u30e0\uff0f\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3067\u306f\u898b\u3048\u308b\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
VisualizationPanel.resetFiltersButton.text=\u5168\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30ea\u30bb\u30c3\u30c8
VisualizationPanel.startLabel.text=\u30b9\u30bf\u30fc\u30c8\uff1a
VisualizationPanel.visualizationModeLabel.text=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u30e2\u30fc\u30c9\uff1a
VisualizationPanel.zoomButton.text=\u30a4\u30d9\u30f3\u30c8\u3078\u30ba\u30fc\u30e0
VisualizationPanel.zoomMenuButton.text=\u4e0b\u8a18\u3078\u30ba\u30fc\u30e0\u30a4\u30f3\uff0f\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8
ViewFrame.countsToggle.text=\u30ab\u30a6\u30f3\u30c8
ViewFrame.detailsToggle.text=\u8a73\u7d30
ViewFrame.endLabel.text=\u30a8\u30f3\u30c9\uff1a
ViewFrame.noEventsDialogLabel.text=\u73fe\u5728\u306e\u30ba\u30fc\u30e0\uff0f\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3067\u306f\u898b\u3048\u308b\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093\u3002
ViewFrame.resetFiltersButton.text=\u5168\u3066\u306e\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30ea\u30bb\u30c3\u30c8
ViewFrame.startLabel.text=\u30b9\u30bf\u30fc\u30c8\uff1a
ViewFrame.viewModeLabel.text=\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u30e2\u30fc\u30c9\uff1a
ViewFrame.zoomButton.text=\u30a4\u30d9\u30f3\u30c8\u3078\u30ba\u30fc\u30e0
ViewFrame.zoomMenuButton.text=\u4e0b\u8a18\u3078\u30ba\u30fc\u30e0\u30a4\u30f3\uff0f\u30ba\u30fc\u30e0\u30a2\u30a6\u30c8
*=Autopsy\u30d5\u30a9\u30ec\u30f3\u30b8\u30c3\u30af\u30d6\u30e9\u30a6\u30b6
AbstractVisualization.Default_Tooltip.text=\u30de\u30a6\u30b9\u3092\u30c9\u30e9\u30c3\u30b0\u3057\u3066\u30ba\u30fc\u30e0\u3059\u308b\u30bf\u30a4\u30e0\u9593\u9694\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\u305d\u306e\u4ed6\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044
AbstractTimelineChart.defaultTooltip.text=\u30de\u30a6\u30b9\u3092\u30c9\u30e9\u30c3\u30b0\u3057\u3066\u30ba\u30fc\u30e0\u3059\u308b\u30bf\u30a4\u30e0\u9593\u9694\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002\n\u305d\u306e\u4ed6\u306e\u30a2\u30af\u30b7\u30e7\u30f3\u306f\u53f3\u30af\u30ea\u30c3\u30af\u3057\u3066\u304f\u3060\u3055\u3044
IntervalSelector.ClearSelectedIntervalAction.tooltTipText=\u9078\u629e\u3057\u305f\u9593\u9694\u3092\u30af\u30ea\u30a2\u3059\u308b
IntervalSelector.ZoomAction.name=\u30ba\u30fc\u30e0
NoEventsDialog.titledPane.text=\u898b\u308c\u308b\u30a4\u30d9\u30f3\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
@ -40,6 +40,6 @@ Timeline.ui.ZoomRanges.fiveyears.text=5\u5e74
Timeline.ui.ZoomRanges.tenyears.text=10\u5e74
TimeLineChart.zoomHistoryActionGroup.name=\u30ba\u30fc\u30e0\u5c65\u6b74
TimeZonePanel.title=\u6642\u9593\u3092\u6b21\u3067\u8868\u793a\uff1a
VisualizationPanel.refresh=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5
VisualizationPanel.tagsAddedOrDeleted=\u30bf\u30b0\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u304a\u3088\u3073\u307e\u305f\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
VisualizationUpdateTask.preparing=\u30ba\u30fc\u30e0\u304a\u3088\u3073\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3092\u89e3\u6790\u4e2d
ViewFrame.refresh=\u30ea\u30d5\u30ec\u30c3\u30b7\u30e5
ViewFrame.tagsAddedOrDeleted=\u30bf\u30b0\u304c\u4f5c\u6210\u3055\u308c\u307e\u3057\u305f\u304a\u3088\u3073\u307e\u305f\u306f\u524a\u9664\u3055\u308c\u307e\u3057\u305f\u3002\u30d3\u30b8\u30e5\u30a2\u30e9\u30a4\u30bc\u30fc\u30b7\u30e7\u30f3\u304c\u6700\u65b0\u3067\u306f\u306a\u3044\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002
ViewRefreshTask.preparing=\u30ba\u30fc\u30e0\u304a\u3088\u3073\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u3092\u89e3\u6790\u4e2d

View File

@ -23,7 +23,7 @@
<items>
<HBox alignment="CENTER_LEFT" BorderPane.alignment="CENTER" HBox.hgrow="NEVER">
<children>
<Label fx:id="visualizationModeLabel" text="Visualisation Mode:" textAlignment="CENTER" wrapText="true" HBox.hgrow="NEVER">
<Label fx:id="viewModeLabel" text="View Mode:" textAlignment="CENTER" wrapText="true" HBox.hgrow="NEVER">
<HBox.margin>
<Insets right="5.0" />
</HBox.margin>
@ -93,7 +93,7 @@
</graphic>
</Button>
<Separator maxWidth="1.7976931348623157E308" orientation="VERTICAL" />
<Button fx:id="refreshButton" alignment="CENTER_RIGHT" mnemonicParsing="false" text="Refresh Vis.">
<Button fx:id="refreshButton" alignment="CENTER_RIGHT" mnemonicParsing="false" text="Refresh View">
<graphic>
<ImageView fitHeight="16.0" fitWidth="16.0" pickOnBounds="true" preserveRatio="true">
<image>

View File

@ -97,9 +97,9 @@ import org.sleuthkit.autopsy.timeline.utils.RangeDivisionInfo;
*
* TODO: Refactor common code out of histogram and CountsView? -jm
*/
final public class VisualizationPanel extends BorderPane {
final public class ViewFrame extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(VisualizationPanel.class.getName());
private static final Logger LOGGER = Logger.getLogger(ViewFrame.class.getName());
private static final Image INFORMATION = new Image("org/sleuthkit/autopsy/timeline/images/information.png", 16, 16, true, true); // NON-NLS
private static final Image WARNING = new Image("org/sleuthkit/autopsy/timeline/images/warning_triangle.png", 16, 16, true, true); // NON-NLS
@ -108,7 +108,7 @@ final public class VisualizationPanel extends BorderPane {
/**
* Region that will be stacked in between the no-events "dialog" and the
* hosted AbstractVisualizationPane in order to gray out the visualization.
* hosted AbstractTimelineView in order to gray out the AbstractTimelineView.
*/
private final static Region NO_EVENTS_BACKGROUND = new Region() {
{
@ -159,7 +159,7 @@ final public class VisualizationPanel extends BorderPane {
@FXML
private ToolBar toolBar;
@FXML
private Label visualizationModeLabel;
private Label viewModeLabel;
@FXML
private SegmentedButton modeSegButton;
@FXML
@ -176,7 +176,7 @@ final public class VisualizationPanel extends BorderPane {
private Button updateDBButton;
/*
* Wraps contained visualization so that we can show notifications over it.
* Wraps contained AbstractTimelineView so that we can show notifications over it.
*/
private final NotificationPane notificationPane = new NotificationPane();
@ -245,26 +245,26 @@ final public class VisualizationPanel extends BorderPane {
/**
* Constructor
*
* @param controller The TimeLineController for this VisualizationPanel
* @param eventsTree The EventsTree this VisualizationPanel hosts.
* @param controller The TimeLineController for this ViewFrame
* @param eventsTree The EventsTree this ViewFrame hosts.
*/
public VisualizationPanel(@Nonnull TimeLineController controller, @Nonnull EventsTree eventsTree) {
public ViewFrame(@Nonnull TimeLineController controller, @Nonnull EventsTree eventsTree) {
this.controller = controller;
this.filteredEvents = controller.getEventsModel();
this.eventsTree = eventsTree;
FXMLConstructor.construct(this, "VisualizationPanel.fxml"); // NON-NLS
FXMLConstructor.construct(this, "ViewFrame.fxml"); // NON-NLS
}
@FXML
@NbBundle.Messages({
"VisualizationPanel.visualizationModeLabel.text=Visualization Mode:",
"VisualizationPanel.startLabel.text=Start:",
"VisualizationPanel.endLabel.text=End:",
"VisualizationPanel.countsToggle.text=Counts",
"VisualizationPanel.detailsToggle.text=Details",
"VisualizationPanel.listToggle.text=List",
"VisualizationPanel.zoomMenuButton.text=Zoom in/out to",
"VisualizationPanel.tagsAddedOrDeleted=Tags have been created and/or deleted. The visualization may not be up to date."
"ViewFrame.viewModeLabel.text=View Mode:",
"ViewFrame.startLabel.text=Start:",
"ViewFrame.endLabel.text=End:",
"ViewFrame.countsToggle.text=Counts",
"ViewFrame.detailsToggle.text=Details",
"ViewFrame.listToggle.text=List",
"ViewFrame.zoomMenuButton.text=Zoom in/out to",
"ViewFrame.tagsAddedOrDeleted=Tags have been created and/or deleted. The view may not be up to date."
})
void initialize() {
assert endPicker != null : "fx:id=\"endPicker\" was not injected: check your FXML file 'ViewWrapper.fxml'."; // NON-NLS
@ -278,11 +278,11 @@ final public class VisualizationPanel extends BorderPane {
notificationPane.getStyleClass().add(NotificationPane.STYLE_CLASS_DARK);
setCenter(notificationPane);
//configure visualization mode toggle
visualizationModeLabel.setText(Bundle.VisualizationPanel_visualizationModeLabel_text());
countsToggle.setText(Bundle.VisualizationPanel_countsToggle_text());
detailsToggle.setText(Bundle.VisualizationPanel_detailsToggle_text());
listToggle.setText(Bundle.VisualizationPanel_listToggle_text());
//configure view mode toggle
viewModeLabel.setText(Bundle.ViewFrame_viewModeLabel_text());
countsToggle.setText(Bundle.ViewFrame_countsToggle_text());
detailsToggle.setText(Bundle.ViewFrame_detailsToggle_text());
listToggle.setText(Bundle.ViewFrame_listToggle_text());
ToggleGroupValue<ViewMode> visModeToggleGroup = new ToggleGroupValue<>();
visModeToggleGroup.add(listToggle, ViewMode.LIST);
@ -302,8 +302,8 @@ final public class VisualizationPanel extends BorderPane {
ActionUtils.configureButton(new UpdateDB(controller), updateDBButton);
/////configure start and end pickers
startLabel.setText(Bundle.VisualizationPanel_startLabel_text());
endLabel.setText(Bundle.VisualizationPanel_endLabel_text());
startLabel.setText(Bundle.ViewFrame_startLabel_text());
endLabel.setText(Bundle.ViewFrame_endLabel_text());
//suppress stacktraces on malformed input
//TODO: should we do anything else? show a warning?
@ -344,7 +344,7 @@ final public class VisualizationPanel extends BorderPane {
}
})));
}
zoomMenuButton.setText(Bundle.VisualizationPanel_zoomMenuButton_text());
zoomMenuButton.setText(Bundle.ViewFrame_zoomMenuButton_text());
ActionUtils.configureButton(new ZoomOut(controller), zoomOutButton);
ActionUtils.configureButton(new ZoomIn(controller), zoomInButton);
@ -355,17 +355,17 @@ final public class VisualizationPanel extends BorderPane {
TimeLineController.getTimeZone().addListener(timeZoneProp -> refreshTimeUI());
filteredEvents.timeRangeProperty().addListener(timeRangeProp -> refreshTimeUI());
filteredEvents.zoomParametersProperty().addListener(zoomListener);
refreshTimeUI(); //populate the viz
refreshTimeUI(); //populate the view
refreshHistorgram();
}
/**
* Handle TagsUpdatedEvents by marking that the visualization needs to be
* Handle TagsUpdatedEvents by marking that the view needs to be
* refreshed.
*
* NOTE: This VisualizationPanel must be registered with the
* NOTE: This ViewFrame must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The TagsUpdatedEvent to handle.
@ -376,7 +376,7 @@ final public class VisualizationPanel extends BorderPane {
Platform.runLater(() -> {
if (notificationPane.isShowing() == false) {
notificationPane.getActions().setAll(new Refresh());
notificationPane.show(Bundle.VisualizationPanel_tagsAddedOrDeleted(), new ImageView(INFORMATION));
notificationPane.show(Bundle.ViewFrame_tagsAddedOrDeleted(), new ImageView(INFORMATION));
}
});
}
@ -385,7 +385,7 @@ final public class VisualizationPanel extends BorderPane {
* Handle a RefreshRequestedEvent from the events model by clearing the
* refresh notification.
*
* NOTE: This VisualizationPanel must be registered with the
* NOTE: This ViewFrame must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The RefreshRequestedEvent to handle.
@ -393,7 +393,7 @@ final public class VisualizationPanel extends BorderPane {
@Subscribe
public void handleRefreshRequested(RefreshRequestedEvent event) {
Platform.runLater(() -> {
if (Bundle.VisualizationPanel_tagsAddedOrDeleted().equals(notificationPane.getText())) {
if (Bundle.ViewFrame_tagsAddedOrDeleted().equals(notificationPane.getText())) {
notificationPane.hide();
}
});
@ -401,9 +401,9 @@ final public class VisualizationPanel extends BorderPane {
/**
* Handle a DBUpdatedEvent from the events model by refreshing the
* visualization.
* view.
*
* NOTE: This VisualizationPanel must be registered with the
* NOTE: This ViewFrame must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The DBUpdatedEvent to handle.
@ -419,7 +419,7 @@ final public class VisualizationPanel extends BorderPane {
* Handle a DataSourceAddedEvent from the events model by showing a
* notification.
*
* NOTE: This VisualizationPanel must be registered with the
* NOTE: This ViewFrame must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The DataSourceAddedEvent to handle.
@ -427,11 +427,11 @@ final public class VisualizationPanel extends BorderPane {
@Subscribe
@NbBundle.Messages({
"# {0} - datasource name",
"VisualizationPanel.notification.newDataSource={0} has been added as a new datasource. The Timeline DB may be out of date."})
"ViewFrame.notification.newDataSource={0} has been added as a new datasource. The Timeline DB may be out of date."})
public void handlDataSourceAdded(DataSourceAddedEvent event) {
Platform.runLater(() -> {
notificationPane.getActions().setAll(new UpdateDB(controller));
notificationPane.show(Bundle.VisualizationPanel_notification_newDataSource(event.getDataSource().getName()), new ImageView(WARNING));
notificationPane.show(Bundle.ViewFrame_notification_newDataSource(event.getDataSource().getName()), new ImageView(WARNING));
});
}
@ -439,7 +439,7 @@ final public class VisualizationPanel extends BorderPane {
* Handle a DataSourceAnalysisCompletedEvent from the events modelby showing
* a notification.
*
* NOTE: This VisualizationPanel must be registered with the
* NOTE: This ViewFrame must be registered with the
* filteredEventsModel's EventBus in order for this handler to be invoked.
*
* @param event The DataSourceAnalysisCompletedEvent to handle.
@ -447,11 +447,11 @@ final public class VisualizationPanel extends BorderPane {
@Subscribe
@NbBundle.Messages({
"# {0} - datasource name",
"VisualizationPanel.notification.analysisComplete=Analysis has finished for {0}. The Timeline DB may be out of date."})
"ViewFrame.notification.analysisComplete=Analysis has finished for {0}. The Timeline DB may be out of date."})
public void handleAnalysisCompleted(DataSourceAnalysisCompletedEvent event) {
Platform.runLater(() -> {
notificationPane.getActions().setAll(new UpdateDB(controller));
notificationPane.show(Bundle.VisualizationPanel_notification_analysisComplete(event.getDataSource().getName()), new ImageView(WARNING));
notificationPane.show(Bundle.ViewFrame_notification_analysisComplete(event.getDataSource().getName()), new ImageView(WARNING));
});
}
@ -464,13 +464,13 @@ final public class VisualizationPanel extends BorderPane {
}
histogramTask = new LoggedTask<Void>(
NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.title"), true) { // NON-NLS
NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.title"), true) { // NON-NLS
private final Lighting lighting = new Lighting();
@Override
protected Void call() throws Exception {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.preparing")); // NON-NLS
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.preparing")); // NON-NLS
long max = 0;
final RangeDivisionInfo rangeInfo = RangeDivisionInfo.getRangeDivisionInfo(filteredEvents.getSpanningInterval());
@ -483,7 +483,7 @@ final public class VisualizationPanel extends BorderPane {
//clear old data, and reset ranges and series
Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.resetUI")); // NON-NLS
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.resetUI")); // NON-NLS
});
@ -500,7 +500,7 @@ final public class VisualizationPanel extends BorderPane {
start = end;
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.queryDb")); // NON-NLS
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.queryDb")); // NON-NLS
//query for current range
long count = filteredEvents.getEventCounts(interval).values().stream().mapToLong(Long::valueOf).sum();
bins.add(count);
@ -510,7 +510,7 @@ final public class VisualizationPanel extends BorderPane {
final double fMax = Math.log(max);
final ArrayList<Long> fbins = new ArrayList<>(bins);
Platform.runLater(() -> {
updateMessage(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.histogramTask.updateUI2")); // NON-NLS
updateMessage(NbBundle.getMessage(ViewFrame.class, "ViewFrame.histogramTask.updateUI2")); // NON-NLS
histogramBox.getChildren().clear();
@ -583,7 +583,7 @@ final public class VisualizationPanel extends BorderPane {
AbstractTimeLineView view;
ViewMode viewMode = controller.viewModeProperty().get();
//make new visualization.
//make new view.
switch (viewMode) {
case LIST:
view = new ListViewPane(controller);
@ -609,20 +609,20 @@ final public class VisualizationPanel extends BorderPane {
view = detailViewPane;
break;
default:
throw new IllegalArgumentException("Unknown VisualizationMode: " + viewMode.toString());
throw new IllegalArgumentException("Unknown ViewMode: " + viewMode.toString());
}
//Set the new AbstractVisualizationPane as the one hosted by this VisualizationPanel.
//Set the new AbstractTimeLineView as the one hosted by this ViewFrame.
Platform.runLater(() -> {
//clear out old vis.
//clear out old view.
if (hostedView != null) {
toolBar.getItems().removeAll(hostedView.getSettingsNodes());
hostedView.dispose();
}
hostedView = view;
//setup new vis.
ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new visualization
//setup new view.
ActionUtils.configureButton(new Refresh(), refreshButton);//configure new refresh action for new view
hostedView.refresh();
toolBar.getItems().addAll(2, view.getSettingsNodes());
notificationPane.setContent(hostedView);
@ -670,7 +670,7 @@ final public class VisualizationPanel extends BorderPane {
assert zoomButton != null : "fx:id=\"zoomButton\" was not injected: check your FXML file 'NoEventsDialog.fxml'."; // NON-NLS
titledPane.setText(Bundle.NoEventsDialog_titledPane_text());
noEventsDialogLabel.setText(NbBundle.getMessage(NoEventsDialog.class, "VisualizationPanel.noEventsDialogLabel.text")); // NON-NLS
noEventsDialogLabel.setText(NbBundle.getMessage(NoEventsDialog.class, "ViewFrame.noEventsDialogLabel.text")); // NON-NLS
dismissButton.setOnAction(actionEvent -> closeCallback.run());
@ -699,7 +699,7 @@ final public class VisualizationPanel extends BorderPane {
LocalDateTime pickerTime = pickerSupplier.get().getLocalDateTime();
if (pickerTime != null) {
controller.pushTimeRange(intervalMapper.apply(filteredEvents.timeRangeProperty().get(), localDateTimeToEpochMilli(pickerTime)));
Platform.runLater(VisualizationPanel.this::refreshTimeUI);
Platform.runLater(ViewFrame.this::refreshTimeUI);
}
}
}
@ -767,16 +767,16 @@ final public class VisualizationPanel extends BorderPane {
}
/**
* Action that refreshes the Visualization.
* Action that refreshes the View.
*/
private class Refresh extends Action {
@NbBundle.Messages({
"VisualizationPanel.refresh.text=Refresh Vis.",
"VisualizationPanel.refresh.longText=Refresh the visualization to include information that is in the DB but not visualized, such as newly updated tags."})
"ViewFrame.refresh.text=Refresh View",
"ViewFrame.refresh.longText=Refresh the view to include information that is in the DB but not displayed, such as newly updated tags."})
Refresh() {
super(Bundle.VisualizationPanel_refresh_text());
setLongText(Bundle.VisualizationPanel_refresh_longText());
super(Bundle.ViewFrame_refresh_text());
setLongText(Bundle.ViewFrame_refresh_longText());
setGraphic(new ImageView(REFRESH));
setEventHandler(actionEvent -> filteredEvents.postRefreshRequest());
disabledProperty().bind(hostedView.outOfDateProperty().not());

View File

@ -105,7 +105,7 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
/**
* Constructor
*
* @param controller The TimelineController for this visualization.
* @param controller The TimelineController for this view.
*/
@NbBundle.Messages({
"# {0} - scale name",
@ -249,7 +249,7 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
"CountsViewPane.scaleLabel.text=Scale:",
"CountsViewPane.scaleHelp.label.text=Scales: ",
"CountsViewPane.linearRadio.text=Linear",
"CountsViewPane.scaleHelpLinear=The 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, time periods with low counts may have a bar that is too small to see. To help the user detect this, 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 larger bars are excluded, or adjust the scale setting to logarithmic.",
"CountsViewPane.scaleHelpLinear=The 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, time periods with low counts may have a bar that is too small to see. To help the user detect this, 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 timeline has more vertical space, adjust the time range shown so that time periods with larger bars are excluded, or adjust the scale setting to logarithmic.",
"CountsViewPane.scaleHelpLog=The logarithmic scale represents the number of events in a non-linear way that compresses the difference between large and 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 filter events to reduce the difference in counts. NOTE: Because the logarithmic scale is applied to each event type separately, the meaning of the height of the combined bar is not intuitive, and to emphasize this, no labels are shown on the y-axis with the logarithmic scale. The logarithmic scale should be used to quickly compare the counts ",
"CountsViewPane.scaleHelpLog2=across time within a type, or across types for one time period, but not both.",
"CountsViewPane.scaleHelpLog3= The actual counts (available in tooltips or the result viewer) should be used for absolute comparisons. Use the logarithmic scale with care."})
@ -336,7 +336,7 @@ public class CountsViewPane extends AbstractTimelineChart<String, Number, Node,
*/
@NbBundle.Messages({
"CountsViewPane.loggedTask.name=Updating Counts View",
"CountsViewPane.loggedTask.updatingCounts=Populating visualization"})
"CountsViewPane.loggedTask.updatingCounts=Populating view"})
private class CountsUpdateTask extends ViewRefreshTask<List<String>> {
CountsUpdateTask() {

View File

@ -405,25 +405,6 @@ final class EventCountsChart extends StackedBarChart<String, Number> implements
if (showConfirmDialog == JOptionPane.YES_OPTION) {
controller.setViewMode(ViewMode.DETAIL);
}
/*
* //I would like to use the JAvafx dialog, but it doesn't
* block the ui (because it is embeded in a TopComponent)
* -jm
*
* final Dialogs.CommandLink yes = new
* Dialogs.CommandLink("Yes", "switch to Details view");
* final Dialogs.CommandLink no = new
* Dialogs.CommandLink("No", "return to Counts view with a
* resolution of Seconds"); Action choice = Dialogs.create()
* .title("Switch to Details View?") .masthead("There is no
* temporal resolution smaller than Seconds.")
* .message("Would you like to switch to the Details view
* instead?") .showCommandLinks(Arrays.asList(yes, no));
*
* if (choice == yes) {
* controller.setViewMode(VisualizationMode.DETAIL); }
*/
}
}
}

View File

@ -91,7 +91,7 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
/**
* Local copy of the zoomParams. Used to backout of a zoomParam change
* without needing to requery/redraw the vis.
* without needing to requery/redraw the view.
*/
private ZoomParams currentZoomParams;
@ -347,7 +347,7 @@ public class DetailViewPane extends AbstractTimelineChart<DateTime, EventStripe,
@NbBundle.Messages({
"DetailViewPane.loggedTask.queryDb=Retreiving event data",
"DetailViewPane.loggedTask.name=Updating Details View",
"DetailViewPane.loggedTask.updateUI=Populating visualization",
"DetailViewPane.loggedTask.updateUI=Populating view",
"DetailViewPane.loggedTask.continueButton=Continue",
"DetailViewPane.loggedTask.backButton=Back (Cancel)",
"# {0} - number of events",

View File

@ -62,7 +62,7 @@ import org.sleuthkit.autopsy.timeline.ui.AbstractTimelineChart;
import org.sleuthkit.autopsy.timeline.ui.ContextMenuProvider;
/**
* One "lane" of a the details visualization, contains all the core logic and
* One "lane" of a the details view, contains all the core logic and
* layout code.
*
* NOTE: It was too hard to control the threading of this chart via the

View File

@ -75,7 +75,7 @@ class ListTimeline extends BorderPane {
private static final Logger LOGGER = Logger.getLogger(ListTimeline.class.getName());
/**
* call-back used to wrap the event ID inn a ObservableValue<Long>
* call-back used to wrap the CombinedEvent in a ObservableValue
*/
private static final Callback<TableColumn.CellDataFeatures<CombinedEvent, CombinedEvent>, ObservableValue<CombinedEvent>> CELL_VALUE_FACTORY = param -> new SimpleObjectProperty<>(param.getValue());
@ -101,7 +101,7 @@ class ListTimeline extends BorderPane {
private final TimeLineController controller;
/**
* observable list used to track selected events.
* Observable list used to track selected events.
*/
private final ObservableList<Long> selectedEventIDs = FXCollections.observableArrayList();
@ -128,7 +128,7 @@ class ListTimeline extends BorderPane {
assert typeColumn != null : "fx:id=\"typeColumn\" was not injected: check your FXML file 'ListViewPane.fxml'.";
assert knownColumn != null : "fx:id=\"knownColumn\" was not injected: check your FXML file 'ListViewPane.fxml'.";
//override default row with one that provides context menu.S
//override default row with one that provides context menus
table.setRowFactory(tableView -> new EventRow());
//remove idColumn (can be restored for debugging).
@ -173,7 +173,7 @@ class ListTimeline extends BorderPane {
//keep the selectedEventsIDs in sync with the table's selection model, via getRepresentitiveEventID().
selectedEventIDs.setAll(FluentIterable.from(table.getSelectionModel().getSelectedItems())
.filter(Objects::nonNull)
.transform(CombinedEvent::getRepresentitiveEventID)
.transform(CombinedEvent::getRepresentativeEventID)
.toSet());
});
}
@ -205,10 +205,10 @@ class ListTimeline extends BorderPane {
ObservableList<Long> getSelectedEventIDs() {
return selectedEventIDs;
}
private static final Image HASH_HIT = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); //NOI18N NON-NLS
private static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS //NOI18N
private static final Image PIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--plus.png"); // NON-NLS //NOI18N
private static final Image UNPIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--minus.png"); // NON-NLS //NOI18N
private static final Image HASH_HIT = new Image("/org/sleuthkit/autopsy/images/hashset_hits.png"); // NON-NLS
private static final Image TAG = new Image("/org/sleuthkit/autopsy/images/green-tag-icon-16.png"); // NON-NLS
private static final Image PIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--plus.png"); // NON-NLS
private static final Image UNPIN = new Image("/org/sleuthkit/autopsy/timeline/images/marker--minus.png"); // NON-NLS
private class TaggedCell extends EventTableCell {
@ -223,6 +223,7 @@ class ListTimeline extends BorderPane {
}
}
}
private class HashHitCell extends EventTableCell {
@Override
@ -232,7 +233,7 @@ class ListTimeline extends BorderPane {
if (empty || item == null) {
setGraphic(null);
} else {
setGraphic(getEvent().isHashHit()? new ImageView(HASH_HIT) : null);
setGraphic(getEvent().isHashHit() ? new ImageView(HASH_HIT) : null);
}
}
}
@ -248,12 +249,12 @@ class ListTimeline extends BorderPane {
}
/**
* Set the ID of the event that is selected.
* Set the combineded events that are selected in this view.
*
* @param selectedEventID The ID of the event that should be selected.
* @param selectedEvents The events that should be selected.
*/
void selectEvents(Collection<CombinedEvent> selectedEvents) {
CombinedEvent firstSelected = selectedEvents.stream().min(Comparator.comparing(CombinedEvent::getStartMillis)).orElseGet(null);
CombinedEvent firstSelected = selectedEvents.stream().min(Comparator.comparing(CombinedEvent::getStartMillis)).orElse(null);
table.getSelectionModel().clearSelection();
table.scrollTo(firstSelected);
selectedEvents.forEach(table.getSelectionModel()::select);
@ -353,7 +354,7 @@ class ListTimeline extends BorderPane {
event = null;
} else {
//stash the event in the cell for derived classed to use.
event = controller.getEventsModel().getEventById(item.getRepresentitiveEventID());
event = controller.getEventsModel().getEventById(item.getRepresentativeEventID());
}
}
}
@ -383,7 +384,7 @@ class ListTimeline extends BorderPane {
if (empty || item == null) {
event = null;
} else {
event = controller.getEventsModel().getEventById(item.getRepresentitiveEventID());
event = controller.getEventsModel().getEventById(item.getRepresentativeEventID());
//make context menu
try {
EventNode node = EventNode.createEventNode(event.getEventID(), controller.getEventsModel());

View File

@ -31,7 +31,7 @@ import org.sleuthkit.autopsy.timeline.datamodel.FilteredEventsModel;
import org.sleuthkit.autopsy.timeline.ui.AbstractTimeLineView;
/**
* An AbstractTimeLineView that uses a TableView to represent the events.
* An AbstractTimeLineView that uses a TableView to display events.
*/
public class ListViewPane extends AbstractTimeLineView {

View File

@ -22,6 +22,7 @@ import java.util.function.Consumer;
import java.util.function.Function;
import javafx.application.Platform;
import javafx.beans.InvalidationListener;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
@ -92,7 +93,6 @@ public class ZoomSettingsPane extends TitledPane {
EventTypeZoomLevel::ordinal,
Function.identity());
typeZoomLabel.setText(Bundle.ZoomSettingsPane_typeZoomLabel_text());
typeZoomSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.LIST));
descrLODSlider.setMax(DescriptionLoD.values().length - 1);
configureSliderListeners(descrLODSlider,
@ -103,15 +103,14 @@ public class ZoomSettingsPane extends TitledPane {
Function.identity());
descrLODLabel.setText(Bundle.ZoomSettingsPane_descrLODLabel_text());
//the description slider is only usefull in the detail view
descrLODSlider.disableProperty().bind(controller.viewModeProperty().isNotEqualTo(ViewMode.DETAIL));
descrLODSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.COUNTS));
/**
* In order for the selected value in the time unit slider to correspond
* to the amount of time used as units along the x-axis of the
* visualization, and since we don't want to show "forever" as a time
* unit, the range of the slider is restricted, and there is an offset
* of 1 between the "real" value, and what is shown in the slider
* labels.
* to the amount of time used as units along the x-axis of the view, and
* since we don't want to show "forever" as a time unit, the range of
* the slider is restricted, and there is an offset of 1 between the
* "real" value, and what is shown in the slider labels.
*/
timeUnitSlider.setMax(TimeUnits.values().length - 2);
configureSliderListeners(timeUnitSlider,
@ -122,7 +121,12 @@ public class ZoomSettingsPane extends TitledPane {
modelTimeRange -> RangeDivisionInfo.getRangeDivisionInfo(modelTimeRange).getPeriodSize().ordinal() - 1,
index -> index + 1); //compensate for the -1 above when mapping to the Enum whose displayName will be shown at index
timeUnitLabel.setText(Bundle.ZoomSettingsPane_timeUnitLabel_text());
timeUnitSlider.disableProperty().bind(controller.viewModeProperty().isEqualTo(ViewMode.LIST));
//hide the whole panel in list mode
BooleanBinding notListMode = controller.viewModeProperty().isNotEqualTo(ViewMode.LIST);
visibleProperty().bind(notListMode);
managedProperty().bind(notListMode);
}
/**
@ -178,7 +182,7 @@ public class ZoomSettingsPane extends TitledPane {
//set the tick labels to the enum displayNames
slider.setLabelFormatter(new EnumSliderLabelFormatter<>(enumClass, labelIndexMapper));
//make a listener to responds to slider value changes (by updating the visualization)
//make a listener to responds to slider value changes (by updating the view)
final InvalidationListener sliderListener = observable -> {
//only process event if the slider value is not changing (user has released slider thumb)
if (slider.isValueChanging() == false) {

View File

@ -1,7 +1,7 @@
/*
* Autopsy Forensic Browser
*
* Copyright 2011-2015 Basis Technology Corp.
* Copyright 2011-2016 Basis Technology Corp.
* Contact: carrier <at> sleuthkit <dot> org
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -45,6 +45,13 @@ import org.openide.util.NbBundle;
import org.sleuthkit.autopsy.corecomponents.OptionsPanel;
import org.sleuthkit.autopsy.coreutils.Logger;
import org.sleuthkit.autopsy.ingest.IngestManager;
import java.awt.Component;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import static javax.swing.SwingConstants.CENTER;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
import org.openide.util.NbBundle.Messages;
/**
* GlobalEditListPanel widget to manage keywords in lists
@ -52,7 +59,8 @@ import org.sleuthkit.autopsy.ingest.IngestManager;
class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionListener, OptionsPanel {
private static final Logger logger = Logger.getLogger(GlobalEditListPanel.class.getName());
private KeywordTableModel tableModel;
private static final long serialVersionUID = 1L;
private final KeywordTableModel tableModel;
private KeywordList currentKeywordList;
private final PropertyChangeSupport pcs = new PropertyChangeSupport(this);
@ -85,7 +93,8 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
column.setPreferredWidth(((int) (width * 0.90)));
} else {
column.setPreferredWidth(((int) (width * 0.10)));
//column.setCellRenderer(new CheckBoxRenderer());
column.setCellRenderer(new CheckBoxRenderer());
column.setHeaderRenderer(new HeaderRenderer(keywordTable));
}
}
keywordTable.setCellSelectionEnabled(false);
@ -686,4 +695,55 @@ class GlobalEditListPanel extends javax.swing.JPanel implements ListSelectionLis
resync();
}
}
/**
* A cell renderer for boolean cells that shows a center-aligned green check
* mark if true, nothing if false.
*/
private class CheckBoxRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 1L;
final ImageIcon theCheck = new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/keywordsearch/checkmark.png")); // NON-NLS
CheckBoxRenderer() {
setHorizontalAlignment(CENTER);
}
@Override
@Messages("IsRegularExpression=Keyword is a regular expression")
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
if ((value instanceof Boolean)) {
if ((Boolean) value) {
setIcon(theCheck);
setToolTipText(Bundle.IsRegularExpression());
} else {
setIcon(null);
setToolTipText(null);
}
}
return this;
}
}
/**
* A cell renderer for header cells that center-aligns the header text.
*/
private static class HeaderRenderer implements TableCellRenderer {
private DefaultTableCellRenderer renderer;
public HeaderRenderer(JTable table) {
renderer = (DefaultTableCellRenderer) table.getTableHeader().getDefaultRenderer();
renderer.setHorizontalAlignment(JLabel.CENTER);
}
@Override
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int col) {
return renderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, col);
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 279 B