mirror of
https://github.com/overcuriousity/autopsy-flatpak.git
synced 2025-07-19 19:14:55 +00:00
This commit is contained in:
commit
c8870c1bb7
@ -1,21 +1,43 @@
|
|||||||
/*
|
/*
|
||||||
* To change this template, choose Tools | Templates
|
* Autopsy Forensic Browser
|
||||||
* and open the template in the editor.
|
*
|
||||||
|
* Copyright 2013 Basis Technology Corp.
|
||||||
|
* Contact: carrier <at> sleuthkit <dot> org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.openide.util.Exceptions;
|
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.HashSearchAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.TagFileAction;
|
||||||
import org.sleuthkit.datamodel.DerivedFile;
|
import org.sleuthkit.datamodel.DerivedFile;
|
||||||
import org.sleuthkit.datamodel.LayoutFile;
|
import org.sleuthkit.datamodel.LayoutFile;
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A Node for a DerivedFile content object.
|
* A Node for a DerivedFile content object.
|
||||||
*
|
*
|
||||||
* TODO should be able to extend FileNode after FileNode extends AbstractFsContentNode<AbstractFile>
|
* TODO should be able to extend FileNode after FileNode extends
|
||||||
|
* AbstractFsContentNode<AbstractFile>
|
||||||
*/
|
*/
|
||||||
public class DerivedFileNode extends AbstractAbstractFileNode<DerivedFile> {
|
public class DerivedFileNode extends AbstractAbstractFileNode<DerivedFile> {
|
||||||
|
|
||||||
@ -65,6 +87,21 @@ public class DerivedFileNode extends AbstractAbstractFileNode<DerivedFile> {
|
|||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action[] getActions(boolean context) {
|
||||||
|
List<Action> actionsList = new ArrayList<Action>();
|
||||||
|
|
||||||
|
actionsList.add(new NewWindowViewAction("View in New Window", this));
|
||||||
|
actionsList.add(new ExternalViewerAction("Open in External Viewer", this));
|
||||||
|
actionsList.add(null); // creates a menu separator
|
||||||
|
actionsList.add(new ExtractAction("Extract", content)); //might not need this actions - already local file
|
||||||
|
actionsList.add(new HashSearchAction("Search for files with the same MD5 hash", this));
|
||||||
|
actionsList.add(null); // creates a menu separator
|
||||||
|
actionsList.add(new TagFileAction(content));
|
||||||
|
|
||||||
|
return actionsList.toArray(new Action[0]);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(ContentNodeVisitor<T> v) {
|
public <T> T accept(ContentNodeVisitor<T> v) {
|
||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
@ -79,8 +116,4 @@ public class DerivedFileNode extends AbstractAbstractFileNode<DerivedFile> {
|
|||||||
public boolean isLeafTypeNode() {
|
public boolean isLeafTypeNode() {
|
||||||
return true; //!this.hasContentChildren();
|
return true; //!this.hasContentChildren();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,8 +18,13 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.FileSearchAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -64,9 +69,13 @@ public class ImageNode extends AbstractContentNode<Image> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Action[] getActions(boolean context) {
|
public Action[] getActions(boolean context) {
|
||||||
return new Action[]{ // SystemAction.get( NewAction.class ),
|
List<Action> actionsList = new ArrayList<Action>();
|
||||||
// SystemAction.get( PasteAction.class )
|
|
||||||
};
|
actionsList.add(new NewWindowViewAction("View in New Window", this));
|
||||||
|
actionsList.add(new FileSearchAction("Open File Search by Attributes"));
|
||||||
|
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||||
|
|
||||||
|
return actionsList.toArray(new Action[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -18,9 +18,17 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.ExternalViewerAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.ExtractAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.TagFileAction;
|
||||||
import org.sleuthkit.datamodel.LayoutFile;
|
import org.sleuthkit.datamodel.LayoutFile;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -87,6 +95,20 @@ public class LayoutFileNode extends AbstractAbstractFileNode<LayoutFile> {
|
|||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Action[] getActions(boolean context) {
|
||||||
|
List<Action> actionsList = new ArrayList<Action>();
|
||||||
|
|
||||||
|
actionsList.add(new NewWindowViewAction("View in New Window", this));
|
||||||
|
actionsList.add(new ExternalViewerAction("Open in External Viewer", this));
|
||||||
|
actionsList.add(null); // creates a menu separator
|
||||||
|
actionsList.add(new ExtractAction("Extract File", content));
|
||||||
|
actionsList.add(null); // creates a menu separator
|
||||||
|
actionsList.add(new TagFileAction(content));
|
||||||
|
|
||||||
|
return actionsList.toArray(new Action[0]);
|
||||||
|
}
|
||||||
|
|
||||||
private static void fillPropertyMap(Map<String, Object> map, LayoutFile content) {
|
private static void fillPropertyMap(Map<String, Object> map, LayoutFile content) {
|
||||||
AbstractAbstractFileNode.fillPropertyMap(map, content);
|
AbstractAbstractFileNode.fillPropertyMap(map, content);
|
||||||
map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts());
|
map.put(LayoutContentPropertyType.PARTS.toString(), content.getNumParts());
|
||||||
|
@ -18,9 +18,12 @@
|
|||||||
*/
|
*/
|
||||||
package org.sleuthkit.autopsy.datamodel;
|
package org.sleuthkit.autopsy.datamodel;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import javax.swing.Action;
|
import javax.swing.Action;
|
||||||
import org.openide.nodes.Sheet;
|
import org.openide.nodes.Sheet;
|
||||||
import org.sleuthkit.autopsy.directorytree.ExtractUnallocAction;
|
import org.sleuthkit.autopsy.directorytree.ExplorerNodeActionVisitor;
|
||||||
|
import org.sleuthkit.autopsy.directorytree.NewWindowViewAction;
|
||||||
import org.sleuthkit.datamodel.Volume;
|
import org.sleuthkit.datamodel.Volume;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,10 +68,12 @@ public class VolumeNode extends AbstractContentNode<Volume> {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Action[] getActions(boolean popup) {
|
public Action[] getActions(boolean popup) {
|
||||||
return new Action[]{ //new ShowDetailAction("Volume Details", this.getName(), this),
|
List<Action> actionsList = new ArrayList<Action>();
|
||||||
//new ShowDetailAction("File System Details", this.getName(), this)
|
|
||||||
//new ExtractUnallocAction("Extract Unallocated Files to single Single", this)
|
actionsList.add(new NewWindowViewAction("View in New Window", this));
|
||||||
};
|
actionsList.addAll(ExplorerNodeActionVisitor.getActions(content));
|
||||||
|
|
||||||
|
return actionsList.toArray(new Action[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.directorytree;
|
|||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.beans.PropertyVetoException;
|
import java.beans.PropertyVetoException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.sleuthkit.autopsy.datamodel.VolumeNode;
|
import org.sleuthkit.autopsy.datamodel.VolumeNode;
|
||||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
||||||
@ -147,111 +148,11 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
return propertySets;
|
return propertySets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Uses the default nodes actions per node, adds some custom ones and returns them per visited node type
|
||||||
|
*/
|
||||||
private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default<List<Action>> {
|
private static class GetPopupActionsDisplayableItemNodeVisitor extends DisplayableItemNodeVisitor.Default<List<Action>> {
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(ImageNode img) {
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
//retain actions from the node if any
|
|
||||||
for (Action a : img.getActions(true)) {
|
|
||||||
actions.add(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.add(new NewWindowViewAction("View in New Window", img));
|
|
||||||
actions.add(new FileSearchAction("Open File Search by Attributes"));
|
|
||||||
actions.addAll(ExplorerNodeActionVisitor.getActions(img.getLookup().lookup(Content.class)));
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(VolumeNode vol) {
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
//retain actions from the node if any
|
|
||||||
for (Action a : vol.getActions(true)) {
|
|
||||||
actions.add(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.add(new NewWindowViewAction("View in New Window", vol));
|
|
||||||
actions.addAll(ExplorerNodeActionVisitor.getActions(vol.getLookup().lookup(Content.class)));
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(DirectoryNode dir) {
|
|
||||||
//preserve the default node's actions
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
for (Action action : dir.getActions(true)) {
|
|
||||||
actions.add(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(LayoutFileNode lf) {
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
//retain actions from the node if any
|
|
||||||
for (Action a : lf.getActions(true)) {
|
|
||||||
actions.add(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.add(new NewWindowViewAction("View in New Window", lf));
|
|
||||||
actions.add(new ExternalViewerAction("Open in External Viewer", lf));
|
|
||||||
actions.add(null); // creates a menu separator
|
|
||||||
actions.add(new ExtractAction("Extract File", lf));
|
|
||||||
actions.add(null); // creates a menu separator
|
|
||||||
actions.add(new TagFileAction(lf));
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(VirtualDirectoryNode ld) {
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
//retain actions from the node if any
|
|
||||||
for (Action a : ld.getActions(true)) {
|
|
||||||
actions.add(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.add(new TagFileAction(ld));
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(DerivedFileNode dfn) {
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
//retain actions from the node if any
|
|
||||||
for (Action a : dfn.getActions(true)) {
|
|
||||||
actions.add(a);
|
|
||||||
}
|
|
||||||
|
|
||||||
actions.add(new NewWindowViewAction("View in New Window", dfn));
|
|
||||||
actions.add(new ExternalViewerAction("Open in External Viewer", dfn));
|
|
||||||
actions.add(null); // creates a menu separator
|
|
||||||
actions.add(new ExtractAction("Extract", dfn)); //might not need this actions - already local file
|
|
||||||
actions.add(new HashSearchAction("Search for files with the same MD5 hash", dfn));
|
|
||||||
actions.add(null); // creates a menu separator
|
|
||||||
actions.add(new TagFileAction(dfn));
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(FileNode f) {
|
|
||||||
//preserve the default node's actions
|
|
||||||
List<Action> actions = new ArrayList<Action>();
|
|
||||||
|
|
||||||
for (Action action : f.getActions(true)) {
|
|
||||||
actions.add(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
return actions;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<Action> visit(BlackboardArtifactNode ban) {
|
public List<Action> visit(BlackboardArtifactNode ban) {
|
||||||
@ -322,39 +223,22 @@ public class DataResultFilterNode extends FilterNode {
|
|||||||
actions.add(new TagResultAction(ba));
|
actions.add(new TagResultAction(ba));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//if (artifactTypeID == BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID()) {
|
|
||||||
//actions.add(null); // creates a menu separator
|
|
||||||
//actions.add(new ResultDeleteAction("Delete Result", ba));
|
|
||||||
//}
|
|
||||||
|
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(KeywordHitsRootNode khrn) {
|
|
||||||
//List<Action> actions = new ArrayList<Action>();
|
|
||||||
//actions.add(null); // creates a menu separator
|
|
||||||
|
|
||||||
//actions.add(new ResultDeleteAction("Delete Results", BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT));
|
|
||||||
//return actions;
|
|
||||||
return super.visit(khrn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(KeywordHitsListNode khsn) {
|
|
||||||
//TODO delete by list
|
|
||||||
return super.visit(khsn);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<Action> visit(KeywordHitsKeywordNode khmln) {
|
|
||||||
//TODO delete by keyword hit
|
|
||||||
return super.visit(khmln);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<Action> defaultVisit(DisplayableItemNode ditem) {
|
protected List<Action> defaultVisit(DisplayableItemNode ditem) {
|
||||||
return new ArrayList<Action>();
|
//preserve the default node's actions
|
||||||
|
List<Action> actions = new ArrayList<Action>();
|
||||||
|
|
||||||
|
for (Action action : ditem.getActions(true)) {
|
||||||
|
actions.add(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Content findLinked(BlackboardArtifactNode ba) {
|
private Content findLinked(BlackboardArtifactNode ba) {
|
||||||
|
@ -80,7 +80,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
private transient ExplorerManager em = new ExplorerManager();
|
private transient ExplorerManager em = new ExplorerManager();
|
||||||
private static DirectoryTreeTopComponent instance;
|
private static DirectoryTreeTopComponent instance;
|
||||||
private DataResultTopComponent dataResult = new DataResultTopComponent(true, "Directory Listing");
|
private DataResultTopComponent dataResult = new DataResultTopComponent(true, "Directory Listing");
|
||||||
|
|
||||||
private LinkedList<String[]> backList;
|
private LinkedList<String[]> backList;
|
||||||
private LinkedList<String[]> forwardList;
|
private LinkedList<String[]> forwardList;
|
||||||
/**
|
/**
|
||||||
@ -567,18 +566,57 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
// }
|
// }
|
||||||
// change in node selection
|
// change in node selection
|
||||||
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
else if (changed.equals(ExplorerManager.PROP_SELECTED_NODES)) {
|
||||||
if (getSelectedNode() == null && oldValue != null) {
|
respondSelection((Node[]) oldValue, (Node[]) newValue);
|
||||||
try {
|
} else if (changed.equals(IngestModuleEvent.DATA.toString())) {
|
||||||
em.setSelectedNodes((Node[]) oldValue);
|
final ModuleDataEvent event = (ModuleDataEvent) oldValue;
|
||||||
} catch (PropertyVetoException ex) {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
logger.log(Level.WARNING, "Error resetting node", ex);
|
@Override
|
||||||
|
public void run() {
|
||||||
|
refreshTree(event.getArtifactType());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (changed.equals(IngestModuleEvent.COMPLETED.toString())) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
refreshContentTree();
|
||||||
|
refreshTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (changed.equals(IngestModuleEvent.CONTENT_CHANGED.toString())) {
|
||||||
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
refreshContentTree();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final Node[] oldNodes = (Node[]) oldValue;
|
|
||||||
final Node[] newNodes = (Node[]) newValue;
|
/**
|
||||||
|
* Event handler to run when selection changed
|
||||||
|
*
|
||||||
|
* TODO this needs to be revised
|
||||||
|
*
|
||||||
|
* @param oldNodes
|
||||||
|
* @param newNodes
|
||||||
|
*/
|
||||||
|
private void respondSelection(final Node[] oldNodes, final Node[] newNodes) {
|
||||||
|
|
||||||
|
//this looks redundant?
|
||||||
|
// if (getSelectedNode() == null && oldNodes != null) {
|
||||||
|
// try {
|
||||||
|
// em.setSelectedNodes(oldNodes);
|
||||||
|
// } catch (PropertyVetoException ex) {
|
||||||
|
// logger.log(Level.WARNING, "Error resetting node", ex);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
// Some lock that prevents certain Node operations is set during the
|
// Some lock that prevents certain Node operations is set during the
|
||||||
// ExplorerManager selection-change, so we must handle changes after the
|
// ExplorerManager selection-change, so we must handle changes after the
|
||||||
// selection-change event is processed.
|
// selection-change event is processed.
|
||||||
|
//TODO find a different way to refresh data result viewer, scheduling this
|
||||||
|
//to EDT breaks loading of nodes in the background
|
||||||
EventQueue.invokeLater(new Runnable() {
|
EventQueue.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@ -586,8 +624,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
DirectoryTreeTopComponent.this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// make sure dataResult is open
|
// make sure dataResult is open, redundant?
|
||||||
dataResult.open();
|
//dataResult.open();
|
||||||
|
|
||||||
Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
|
Node treeNode = DirectoryTreeTopComponent.this.getSelectedNode();
|
||||||
if (treeNode != null) {
|
if (treeNode != null) {
|
||||||
@ -661,30 +699,6 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
forwardButton.setEnabled(false); // disable the forward Button
|
forwardButton.setEnabled(false); // disable the forward Button
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (changed.equals(IngestModuleEvent.DATA.toString())) {
|
|
||||||
final ModuleDataEvent event = (ModuleDataEvent) oldValue;
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refreshTree(event.getArtifactType());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (changed.equals(IngestModuleEvent.COMPLETED.toString())) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refreshContentTree();
|
|
||||||
refreshTree();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else if (changed.equals(IngestModuleEvent.CONTENT_CHANGED.toString())) {
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
refreshContentTree();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -739,8 +753,8 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
RootContentChildren contentRootChildren = (RootContentChildren) imagesNode.getChildren();
|
RootContentChildren contentRootChildren = (RootContentChildren) imagesNode.getChildren();
|
||||||
contentRootChildren.refreshContentKeys();
|
contentRootChildren.refreshContentKeys();
|
||||||
|
|
||||||
final TreeView tree = getTree();
|
//final TreeView tree = getTree();
|
||||||
tree.expandNode(imagesNode);
|
//tree.expandNode(imagesNode);
|
||||||
|
|
||||||
setSelectedNode(selectedPath, ImagesNode.NAME);
|
setSelectedNode(selectedPath, ImagesNode.NAME);
|
||||||
|
|
||||||
@ -751,6 +765,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
* be called in the gui thread
|
* be called in the gui thread
|
||||||
*/
|
*/
|
||||||
void refreshTree(final BlackboardArtifact.ARTIFACT_TYPE... types) {
|
void refreshTree(final BlackboardArtifact.ARTIFACT_TYPE... types) {
|
||||||
|
//save current selection
|
||||||
Node selectedNode = getSelectedNode();
|
Node selectedNode = getSelectedNode();
|
||||||
final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
|
final String[] selectedPath = NodeOp.createPath(selectedNode, em.getRootContext());
|
||||||
|
|
||||||
@ -791,6 +806,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
}
|
}
|
||||||
tree.expandNode(childNode);
|
tree.expandNode(childNode);
|
||||||
|
|
||||||
|
//restores selection if it was under the Results node
|
||||||
setSelectedNode(selectedPath, ResultsNode.NAME);
|
setSelectedNode(selectedPath, ResultsNode.NAME);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -822,7 +838,7 @@ public final class DirectoryTreeTopComponent extends TopComponent implements Dat
|
|||||||
backList.pollLast();
|
backList.pollLast();
|
||||||
}
|
}
|
||||||
//select
|
//select
|
||||||
tree.expandNode(newSelection);
|
//tree.expandNode(newSelection);
|
||||||
em.setExploredContextAndSelection(newSelection, new Node[]{newSelection});
|
em.setExploredContextAndSelection(newSelection, new Node[]{newSelection});
|
||||||
}
|
}
|
||||||
// We need to set the selection, which will refresh dataresult and get rid of the oob exception
|
// We need to set the selection, which will refresh dataresult and get rid of the oob exception
|
||||||
|
@ -45,7 +45,7 @@ import org.sleuthkit.datamodel.FileSystem;
|
|||||||
import org.sleuthkit.datamodel.Image;
|
import org.sleuthkit.datamodel.Image;
|
||||||
import org.sleuthkit.datamodel.Volume;
|
import org.sleuthkit.datamodel.Volume;
|
||||||
|
|
||||||
class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? extends Action>> {
|
public class ExplorerNodeActionVisitor extends ContentVisitor.Default<List<? extends Action>> {
|
||||||
|
|
||||||
private static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor();
|
private static ExplorerNodeActionVisitor instance = new ExplorerNodeActionVisitor();
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ import com.drew.metadata.exif.GpsDirectory;
|
|||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Calendar;
|
import java.util.Calendar;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
@ -70,6 +71,10 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
|||||||
|
|
||||||
private String args;
|
private String args;
|
||||||
|
|
||||||
|
private static final int readHeaderSize = 2;
|
||||||
|
private final byte[] fileHeaderBuffer = new byte[readHeaderSize];
|
||||||
|
private static final char JPEG_SIGNATURE_BE = 0xFFD8;
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
|
private static final Logger logger = Logger.getLogger(ExifParserFileIngestModule.class.getName());
|
||||||
private static ExifParserFileIngestModule defaultInstance = null;
|
private static ExifParserFileIngestModule defaultInstance = null;
|
||||||
private static int messageId = 0;
|
private static int messageId = 0;
|
||||||
@ -183,6 +188,12 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
|||||||
return IngestModuleAbstractFile.ProcessResult.ERROR;
|
return IngestModuleAbstractFile.ProcessResult.ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if should try to attempt to extract exif.
|
||||||
|
* Currently checks if JPEG image, first by extension, then by signature (if extension fails)
|
||||||
|
* @param f file to be checked
|
||||||
|
* @return true if to be processed
|
||||||
|
*/
|
||||||
private boolean parsableFormat(AbstractFile f) {
|
private boolean parsableFormat(AbstractFile f) {
|
||||||
// Get the name, extension
|
// Get the name, extension
|
||||||
String name = f.getName();
|
String name = f.getName();
|
||||||
@ -195,9 +206,39 @@ public final class ExifParserFileIngestModule implements IngestModuleAbstractFil
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return isJpegFileHeader(f);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if is jpeg file based on header
|
||||||
|
* @param file
|
||||||
|
* @return true if jpeg file, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean isJpegFileHeader(AbstractFile file) {
|
||||||
|
if (file.getSize() < readHeaderSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
try {
|
||||||
|
bytesRead = file.read(fileHeaderBuffer, 0, readHeaderSize);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
//ignore if can't read the first few bytes, not a JPEG
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bytesRead != readHeaderSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer bytes = ByteBuffer.wrap(fileHeaderBuffer);
|
||||||
|
char signature = bytes.getChar();
|
||||||
|
|
||||||
|
return signature == JPEG_SIGNATURE_BE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void complete() {
|
public void complete() {
|
||||||
logger.log(Level.INFO, "completed exif parsing " + this.toString());
|
logger.log(Level.INFO, "completed exif parsing " + this.toString());
|
||||||
|
2
NEWS.txt
2
NEWS.txt
@ -11,6 +11,8 @@ Improvements:
|
|||||||
Bugfixes:
|
Bugfixes:
|
||||||
- show error message in hex and string viewer if specific offset of a file could not be read.
|
- show error message in hex and string viewer if specific offset of a file could not be read.
|
||||||
- file search actions not always enabled when new case is open.
|
- file search actions not always enabled when new case is open.
|
||||||
|
- fixed directory tree history being reset when tree is refreshed.
|
||||||
|
- exif module better jpeg detection using signature and not only file extension.
|
||||||
|
|
||||||
---------------- VERSION 3.0.4 --------------
|
---------------- VERSION 3.0.4 --------------
|
||||||
|
|
||||||
|
@ -24,6 +24,7 @@ import java.io.FileNotFoundException;
|
|||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
@ -90,6 +91,11 @@ public final class SevenZipIngestModule implements IngestModuleAbstractFile {
|
|||||||
private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
|
private static final long MIN_FREE_DISK_SPACE = 1 * 1000 * 1000000L; //1GB
|
||||||
//counts archive depth
|
//counts archive depth
|
||||||
private ArchiveDepthCountTree archiveDepthCountTree;
|
private ArchiveDepthCountTree archiveDepthCountTree;
|
||||||
|
//buffer for checking file headers and signatures
|
||||||
|
private static final int readHeaderSize = 4;
|
||||||
|
private final byte[] fileHeaderBuffer = new byte[readHeaderSize];
|
||||||
|
private static final int ZIP_SIGNATURE_BE = 0x504B0304;
|
||||||
|
|
||||||
|
|
||||||
//private constructor to ensure singleton instance
|
//private constructor to ensure singleton instance
|
||||||
private SevenZipIngestModule() {
|
private SevenZipIngestModule() {
|
||||||
@ -617,9 +623,42 @@ public final class SevenZipIngestModule implements IngestModuleAbstractFile {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//if no extension match, check for zip signature
|
||||||
|
//(note, in near future, we will use pre-detected content type)
|
||||||
|
return isZipFileHeader(file);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if is zip file based on header
|
||||||
|
* @param file
|
||||||
|
* @return true if zip file, false otherwise
|
||||||
|
*/
|
||||||
|
private boolean isZipFileHeader(AbstractFile file) {
|
||||||
|
if (file.getSize() < readHeaderSize) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int bytesRead = 0;
|
||||||
|
try {
|
||||||
|
bytesRead = file.read(fileHeaderBuffer, 0, readHeaderSize);
|
||||||
|
} catch (TskCoreException ex) {
|
||||||
|
//ignore if can't read the first few bytes, not a ZIP
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (bytesRead != readHeaderSize) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ByteBuffer bytes = ByteBuffer.wrap(fileHeaderBuffer);
|
||||||
|
int signature = bytes.getInt();
|
||||||
|
|
||||||
|
return signature == ZIP_SIGNATURE_BE;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stream used to unpack the archive to local file
|
* Stream used to unpack the archive to local file
|
||||||
*/
|
*/
|
||||||
|
@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.timeline;
|
|||||||
|
|
||||||
import com.sun.javafx.application.PlatformImpl;
|
import com.sun.javafx.application.PlatformImpl;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.awt.Cursor;
|
||||||
import java.awt.Dimension;
|
import java.awt.Dimension;
|
||||||
import java.beans.PropertyChangeEvent;
|
import java.beans.PropertyChangeEvent;
|
||||||
import java.beans.PropertyChangeListener;
|
import java.beans.PropertyChangeListener;
|
||||||
@ -42,6 +43,8 @@ import java.util.Scanner;
|
|||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import javafx.beans.value.ObservableValue;
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import javafx.embed.swing.JFXPanel;
|
import javafx.embed.swing.JFXPanel;
|
||||||
@ -78,7 +81,6 @@ import org.openide.modules.InstalledFileLocator;
|
|||||||
import org.openide.nodes.ChildFactory;
|
import org.openide.nodes.ChildFactory;
|
||||||
import org.openide.nodes.Children;
|
import org.openide.nodes.Children;
|
||||||
import org.openide.nodes.Node;
|
import org.openide.nodes.Node;
|
||||||
import org.openide.util.Exceptions;
|
|
||||||
import org.openide.util.HelpCtx;
|
import org.openide.util.HelpCtx;
|
||||||
import org.openide.util.NbBundle;
|
import org.openide.util.NbBundle;
|
||||||
import org.openide.util.actions.CallableSystemAction;
|
import org.openide.util.actions.CallableSystemAction;
|
||||||
@ -104,15 +106,15 @@ import org.sleuthkit.datamodel.SleuthkitCase;
|
|||||||
import org.sleuthkit.datamodel.TskCoreException;
|
import org.sleuthkit.datamodel.TskCoreException;
|
||||||
import org.sleuthkit.datamodel.TskData;
|
import org.sleuthkit.datamodel.TskData;
|
||||||
|
|
||||||
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Simile2")
|
@ActionID(category = "Tools", id = "org.sleuthkit.autopsy.timeline.Timeline")
|
||||||
@ActionRegistration(displayName = "#CTL_MakeTimeline")
|
@ActionRegistration(displayName = "#CTL_MakeTimeline")
|
||||||
@ActionReferences(value = {
|
@ActionReferences(value = {
|
||||||
@ActionReference(path = "Menu/Tools", position = 100)})
|
@ActionReference(path = "Menu/Tools", position = 100)})
|
||||||
@NbBundle.Messages(value = "CTL_TimelineView=Generate Timeline")
|
@NbBundle.Messages(value = "CTL_TimelineView=Generate Timeline")
|
||||||
|
public class Timeline extends CallableSystemAction implements Presenter.Toolbar, PropertyChangeListener {
|
||||||
|
|
||||||
public class Simile2 extends CallableSystemAction implements Presenter.Toolbar, PropertyChangeListener {
|
private static final Logger logger = Logger.getLogger(Timeline.class.getName());
|
||||||
private static final Logger logger = Logger.getLogger(Simile2.class.getName());
|
private final java.io.File macRoot = InstalledFileLocator.getDefault().locate("mactime", Timeline.class.getPackage().getName(), false);
|
||||||
private final java.io.File macRoot = InstalledFileLocator.getDefault().locate("mactime", Simile2.class.getPackage().getName(), false);
|
|
||||||
private JFrame jf; //frame for holding all the elements
|
private JFrame jf; //frame for holding all the elements
|
||||||
private Group group_Charts; //Orders the charts
|
private Group group_Charts; //Orders the charts
|
||||||
private Scene scene_Charts; //Displays the charts
|
private Scene scene_Charts; //Displays the charts
|
||||||
@ -123,8 +125,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
private ScrollPane scroll_Events; //Scroll Panes for dealing with oversized an oversized chart
|
private ScrollPane scroll_Events; //Scroll Panes for dealing with oversized an oversized chart
|
||||||
private final int Height_Frame = 850; //Sizing constants
|
private final int Height_Frame = 850; //Sizing constants
|
||||||
private final int Width_Frame = 1300;
|
private final int Width_Frame = 1300;
|
||||||
private Button button_DrillUp; //Navigation buttons
|
private Button zoomOutButton; //Navigation buttons
|
||||||
private Button button_Go;
|
|
||||||
private ComboBox<String> dropdown_SelectYears; //Dropdown box for selecting years. Useful when the charts' scale means some years are unclickable, despite having events.
|
private ComboBox<String> dropdown_SelectYears; //Dropdown box for selecting years. Useful when the charts' scale means some years are unclickable, despite having events.
|
||||||
private final Stack<BarChart> stack_PrevCharts = new Stack<BarChart>(); //Stack for storing drill-up information.
|
private final Stack<BarChart> stack_PrevCharts = new Stack<BarChart>(); //Stack for storing drill-up information.
|
||||||
private BarChart chart_TopLevel; //the topmost chart, used for resetting to default view.
|
private BarChart chart_TopLevel; //the topmost chart, used for resetting to default view.
|
||||||
@ -136,26 +137,34 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
private List<YearEpoch> data;
|
private List<YearEpoch> data;
|
||||||
private boolean listeningToAddImage = false;
|
private boolean listeningToAddImage = false;
|
||||||
private long lastObjectId = -1;
|
private long lastObjectId = -1;
|
||||||
private TimelineProgressDialog dialog;
|
private TimelineProgressDialog progressDialog;
|
||||||
|
private EventHandler mouseEnteredListener;
|
||||||
|
private EventHandler mouseExitedListener;
|
||||||
|
private JSplitPane splitXPane, splitYPane;
|
||||||
|
private SleuthkitCase skCase;
|
||||||
|
|
||||||
//Swing components and JavafX components don't play super well together
|
//Swing components and JavafX components don't play super well together
|
||||||
//Swing components need to be initialized first, in the swing specific thread
|
//Swing components need to be initialized first, in the swing specific thread
|
||||||
//Next, the javafx components may be initialized.
|
//Next, the javafx components may be initialized.
|
||||||
private void customizeSwing() {
|
private void customize() {
|
||||||
|
|
||||||
|
//listeners
|
||||||
|
mouseEnteredListener = new EventHandler<MouseEvent>() {
|
||||||
|
@Override
|
||||||
|
public void handle(MouseEvent e) {
|
||||||
|
panel_Charts.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
mouseExitedListener = new EventHandler<MouseEvent>() {
|
||||||
|
@Override
|
||||||
|
public void handle(MouseEvent e) {
|
||||||
|
panel_Charts.setCursor(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
dataContentPanel = new DataContentPanel();
|
|
||||||
dataResult = DataResultPanel.createInstance("Timeline Results", "", Node.EMPTY, 0, dataContentPanel);
|
|
||||||
dataResult.setContentViewer(new DataContentPanel());
|
|
||||||
dataResult.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
||||||
dataResult.setPreferredSize(new Dimension(700, 300));
|
|
||||||
logger.log(Level.INFO, "Successfully created viewers");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void customize() {
|
|
||||||
//Making the main frame *
|
//Making the main frame *
|
||||||
jf = new JFrame(Case.getCurrentCase().getName() + " - Autopsy Timeline (Beta)");
|
jf = new JFrame(Case.getCurrentCase().getName() + " - Autopsy Timeline (Beta)");
|
||||||
|
|
||||||
@ -163,24 +172,41 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
jf.setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
|
jf.setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
|
||||||
jf.setSize(Width_Frame, Height_Frame); //(Width, Height)
|
jf.setSize(Width_Frame, Height_Frame); //(Width, Height)
|
||||||
|
|
||||||
//JPanels are used as the cohesive glue that binds everything together.*/
|
|
||||||
//The chartJpanel holds the chart,
|
dataContentPanel = new DataContentPanel();
|
||||||
//aligned vertically (Y_AXIS)
|
|
||||||
final JPanel chartJPanel = new JPanel();
|
dataResult = DataResultPanel.createInstance("Timeline Results", "", Node.EMPTY, 0, dataContentPanel);
|
||||||
chartJPanel.setLayout(new BoxLayout(chartJPanel, BoxLayout.Y_AXIS));
|
dataResult.setContentViewer(new DataContentPanel());
|
||||||
|
dataResult.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
dataResult.setPreferredSize(new Dimension(700, 300));
|
||||||
|
logger.log(Level.INFO, "Successfully created viewers");
|
||||||
|
|
||||||
//ViewerJPanel holds both of the DataResult/DataContent viewers,
|
//ViewerJPanel holds both of the DataResult/DataContent viewers,
|
||||||
//aligned horizontally (X_AXIS)
|
//aligned horizontally (X_AXIS)
|
||||||
final JPanel viewerJPanel = new JPanel();
|
final JPanel viewerJPanel = new JPanel();
|
||||||
viewerJPanel.setLayout(new BoxLayout(viewerJPanel, BoxLayout.X_AXIS));
|
viewerJPanel.setLayout(new BoxLayout(viewerJPanel, BoxLayout.X_AXIS));
|
||||||
|
|
||||||
//ComboJPanel holds both of the above JPanels together,
|
splitXPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, dataResult, dataContentPanel);
|
||||||
|
splitXPane.setDividerLocation(Width_Frame / 2);
|
||||||
|
viewerJPanel.add(splitXPane);
|
||||||
|
viewerJPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
|
||||||
|
//The chartJpanel holds the chart,
|
||||||
//aligned vertically (Y_AXIS)
|
//aligned vertically (Y_AXIS)
|
||||||
|
final JPanel chartJPanel = new JPanel();
|
||||||
|
chartJPanel.setLayout(new BoxLayout(chartJPanel, BoxLayout.Y_AXIS));
|
||||||
|
|
||||||
// create a horizontal split pane
|
splitYPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartJPanel, viewerJPanel);
|
||||||
final JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, chartJPanel, viewerJPanel);
|
splitYPane.setDividerLocation(Height_Frame / 2);
|
||||||
splitPane.setDividerLocation(450);
|
|
||||||
|
|
||||||
|
runJavaFxThread(chartJPanel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runJavaFxThread(final JPanel chartJPanel) {
|
||||||
//JavaFX thread
|
//JavaFX thread
|
||||||
//JavaFX components MUST be run in the JavaFX thread, otherwise massive amounts of exceptions will be thrown and caught. Liable to freeze up and crash.
|
//JavaFX components MUST be run in the JavaFX thread, otherwise massive amounts of exceptions will be thrown and caught. Liable to freeze up and crash.
|
||||||
//Components can be declared whenever, but initialization and manipulation must take place here.
|
//Components can be declared whenever, but initialization and manipulation must take place here.
|
||||||
@ -192,6 +218,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
progress = ProgressHandleFactory.createHandle("Creating timeline . . .");
|
progress = ProgressHandleFactory.createHandle("Creating timeline . . .");
|
||||||
progress.start();
|
progress.start();
|
||||||
|
|
||||||
|
chart_Events = null;
|
||||||
panel_Charts = new JFXPanel();
|
panel_Charts = new JFXPanel();
|
||||||
group_Charts = new Group();
|
group_Charts = new Group();
|
||||||
scene_Charts = new Scene(group_Charts, Width_Frame, Math.round(Height_Frame / .75)); //Width, Height
|
scene_Charts = new Scene(group_Charts, Width_Frame, Math.round(Height_Frame / .75)); //Width, Height
|
||||||
@ -211,19 +238,31 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
moduleDir.mkdir();
|
moduleDir.mkdir();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int currentProgress = 0;
|
||||||
java.io.File mactimeFile = new java.io.File(moduleDir, mactimeFileName);
|
java.io.File mactimeFile = new java.io.File(moduleDir, mactimeFileName);
|
||||||
if (!mactimeFile.exists()) {
|
if (!mactimeFile.exists()) {
|
||||||
logger.log(Level.INFO, "Creating mactime file.");
|
progressDialog.setProgressTotal(3); //total 3 units
|
||||||
|
logger.log(Level.INFO, "Creating body file");
|
||||||
|
progressDialog.updateProgressBar("Generating Bodyfile");
|
||||||
String bodyFilePath = makeBodyFile();
|
String bodyFilePath = makeBodyFile();
|
||||||
|
progressDialog.updateProgressBar(++currentProgress);
|
||||||
|
logger.log(Level.INFO, "Creating mactime file: " + mactimeFile.getAbsolutePath());
|
||||||
|
progressDialog.updateProgressBar("Generating Mactime");
|
||||||
makeMacTime(bodyFilePath);
|
makeMacTime(bodyFilePath);
|
||||||
|
progressDialog.updateProgressBar(++currentProgress);
|
||||||
data = null;
|
data = null;
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.INFO, "mactime file already exists; parsing that.");
|
progressDialog.setProgressTotal(1); //total 1 units
|
||||||
|
logger.log(Level.INFO, "Mactime file already exists; parsing that: " + mactimeFile.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
progressDialog.updateProgressBar("Parsing Mactime");
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
|
logger.log(Level.INFO, "Parsing mactime file: " + mactimeFile.getAbsolutePath());
|
||||||
data = parseMacTime(mactimeFile); //The sum total of the mactime parsing. YearEpochs contain everything you need to make a timeline.
|
data = parseMacTime(mactimeFile); //The sum total of the mactime parsing. YearEpochs contain everything you need to make a timeline.
|
||||||
}
|
}
|
||||||
|
progressDialog.updateProgressBar(++currentProgress);
|
||||||
|
|
||||||
//Making a dropdown box to select years.
|
//Making a dropdown box to select years.
|
||||||
List<String> lsi = new ArrayList<String>(); //List is in the format of {Year : Number of Events}, used for selecting from the dropdown.
|
List<String> lsi = new ArrayList<String>(); //List is in the format of {Year : Number of Events}, used for selecting from the dropdown.
|
||||||
@ -234,8 +273,8 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
dropdown_SelectYears = new ComboBox(listSelect);
|
dropdown_SelectYears = new ComboBox(listSelect);
|
||||||
|
|
||||||
//Buttons for navigating up and down the timeline
|
//Buttons for navigating up and down the timeline
|
||||||
button_DrillUp = new Button("Zoom Out");
|
zoomOutButton = new Button("Zoom Out");
|
||||||
button_DrillUp.setOnAction(new EventHandler<ActionEvent>() {
|
zoomOutButton.setOnAction(new EventHandler<ActionEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent e) {
|
public void handle(ActionEvent e) {
|
||||||
BarChart bc;
|
BarChart bc;
|
||||||
@ -249,20 +288,24 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
button_Go = new Button("►");
|
dropdown_SelectYears.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<String>() {
|
||||||
button_Go.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(ActionEvent e) {
|
public void changed(ObservableValue<? extends String> ov, String t, String t1) {
|
||||||
if (dropdown_SelectYears.getValue() != null) {
|
if (dropdown_SelectYears.getValue() != null) {
|
||||||
|
chartJPanel.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
|
try {
|
||||||
chart_Events = createMonthsWithDrill(findYear(data, Integer.valueOf(dropdown_SelectYears.getValue().split(" ")[0])));
|
chart_Events = createMonthsWithDrill(findYear(data, Integer.valueOf(dropdown_SelectYears.getValue().split(" ")[0])));
|
||||||
scroll_Events.setContent(chart_Events);
|
scroll_Events.setContent(chart_Events);
|
||||||
|
} finally {
|
||||||
|
chartJPanel.setCursor(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//Adding things to the V and H boxes.
|
//Adding things to the V and H boxes.
|
||||||
//hBox_Charts stores the pseudo menu bar at the top of the timeline. |Zoom Out|View Year: [Select Year]|►|
|
//hBox_Charts stores the pseudo menu bar at the top of the timeline. |Zoom Out|View Year: [Select Year]|►|
|
||||||
hBox_Charts.getChildren().addAll(button_DrillUp, new Label("View Year:"), dropdown_SelectYears, button_Go);
|
hBox_Charts.getChildren().addAll(zoomOutButton, new Label("Go To:"), dropdown_SelectYears);
|
||||||
vBox_FX.getChildren().addAll(hBox_Charts, scroll_Events); //FxBox_V holds things in a visual stack.
|
vBox_FX.getChildren().addAll(hBox_Charts, scroll_Events); //FxBox_V holds things in a visual stack.
|
||||||
group_Charts.getChildren().add(vBox_FX); //Adding the FxBox to the group. Groups make things easier to manipulate without having to update a hundred things every change.
|
group_Charts.getChildren().add(vBox_FX); //Adding the FxBox to the group. Groups make things easier to manipulate without having to update a hundred things every change.
|
||||||
panel_Charts.setScene(scene_Charts);
|
panel_Charts.setScene(scene_Charts);
|
||||||
@ -271,24 +314,22 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
panel_Charts.setAlignmentX(Component.LEFT_ALIGNMENT);
|
panel_Charts.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
|
|
||||||
chartJPanel.add(panel_Charts);
|
chartJPanel.add(panel_Charts);
|
||||||
viewerJPanel.add(dataResult);
|
|
||||||
|
|
||||||
viewerJPanel.add(dataContentPanel);
|
|
||||||
chartJPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
chartJPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||||
viewerJPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
||||||
|
|
||||||
chart_TopLevel = createYearChartWithDrill(data);
|
chart_TopLevel = createYearChartWithDrill(data);
|
||||||
chart_Events = chart_TopLevel;
|
chart_Events = chart_TopLevel;
|
||||||
scroll_Events.setContent(chart_Events);
|
scroll_Events.setContent(chart_Events);
|
||||||
|
|
||||||
jf.add(splitPane);
|
jf.add(splitYPane);
|
||||||
jf.setVisible(true);
|
jf.setVisible(true);
|
||||||
} finally {
|
} finally {
|
||||||
// stop the progress bar
|
// stop the progress bar
|
||||||
progress.finish();
|
progress.finish();
|
||||||
|
|
||||||
// close the dialog
|
// close the progressDialog
|
||||||
dialog.doClose(0);
|
progressDialog.doClose(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -298,7 +339,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
* Creates a BarChart with datapoints for all the years from the parsed
|
* Creates a BarChart with datapoints for all the years from the parsed
|
||||||
* mactime file.
|
* mactime file.
|
||||||
*
|
*
|
||||||
* @param allYears The list of years that have data from the mactime file
|
* @param allYears The list of years that have barData from the mactime file
|
||||||
* @return BarChart scaled to the year level
|
* @return BarChart scaled to the year level
|
||||||
*/
|
*/
|
||||||
private BarChart createYearChartWithDrill(final List<YearEpoch> allYears) {
|
private BarChart createYearChartWithDrill(final List<YearEpoch> allYears) {
|
||||||
@ -309,7 +350,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
l.setTextFill(Color.AZURE);
|
l.setTextFill(Color.AZURE);
|
||||||
xAxis.setLabel("Years");
|
xAxis.setLabel("Years");
|
||||||
yAxis.setLabel("Number of Events");
|
yAxis.setLabel("Number of Events");
|
||||||
//Charts are made up of individual pieces of Chart.Data. In this case, a piece of data is a single bar on the graph.
|
//Charts are made up of individual pieces of Chart.Data. In this case, a piece of barData is a single bar on the graph.
|
||||||
//Data is packaged into a series, which can be assigned custom colors or styling
|
//Data is packaged into a series, which can be assigned custom colors or styling
|
||||||
//After the series are created, 1 or more series are packaged into a single chart.
|
//After the series are created, 1 or more series are packaged into a single chart.
|
||||||
ObservableList<BarChart.Series<String, Number>> bcData = FXCollections.observableArrayList();
|
ObservableList<BarChart.Series<String, Number>> bcData = FXCollections.observableArrayList();
|
||||||
@ -326,27 +367,30 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
// But it is for this reason that the chart generating functions have two forloops. I do not believe they can be condensed into a single loop due to the nodes being null until
|
// But it is for this reason that the chart generating functions have two forloops. I do not believe they can be condensed into a single loop due to the nodes being null until
|
||||||
// an undetermined point in time.
|
// an undetermined point in time.
|
||||||
BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) { //.get(0) refers to the BarChart.Series class to work on. There is only one series in this graph, so get(0) is safe.
|
for (final BarChart.Data barData : bc.getData().get(0).getData()) { //.get(0) refers to the BarChart.Series class to work on. There is only one series in this graph, so get(0) is safe.
|
||||||
data.getNode().setScaleX(.5);
|
barData.getNode().setScaleX(.5);
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_CLICKED,
|
|
||||||
|
final javafx.scene.Node barNode = barData.getNode();
|
||||||
|
//hover listener
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, mouseEnteredListener);
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, mouseExitedListener);
|
||||||
|
|
||||||
|
//click listener
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_CLICKED,
|
||||||
new EventHandler<MouseEvent>() {
|
new EventHandler<MouseEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(MouseEvent e) {
|
public void handle(MouseEvent e) {
|
||||||
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
||||||
if (e.getClickCount() == 2) { //Checking for a doubleclick
|
if (e.getClickCount() == 1) {
|
||||||
PlatformImpl.startup(new Runnable() {
|
Platform.runLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
BarChart b = createMonthsWithDrill((YearEpoch) findYear(allYears, Integer.valueOf((String) data.getXValue())));
|
BarChart b = createMonthsWithDrill((YearEpoch) findYear(allYears, Integer.valueOf((String) barData.getXValue())));
|
||||||
chart_Events = b;
|
chart_Events = b;
|
||||||
scroll_Events.setContent(chart_Events);
|
scroll_Events.setContent(chart_Events);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
//If a single click, hover a label over the cursor with information about the selection
|
|
||||||
} else if (e.getClickCount() == 1) {
|
|
||||||
l.setText(findYear(allYears, Integer.valueOf((String) data.getXValue())).getNumFiles() + " events");
|
|
||||||
l.setTranslateX(e.getX());
|
|
||||||
l.setTranslateY(e.getY());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -376,30 +420,36 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
String monthName = new DateFormatSymbols().getMonths()[monthNum];
|
String monthName = new DateFormatSymbols().getMonths()[monthNum];
|
||||||
MonthEpoch month = ye.getMonth(monthNum);
|
MonthEpoch month = ye.getMonth(monthNum);
|
||||||
int numEvents = month == null ? 0 : month.getNumFiles();
|
int numEvents = month == null ? 0 : month.getNumFiles();
|
||||||
se.getData().add(new BarChart.Data<String, Number>(monthName, numEvents)); //Adding new data at {X-pos, Y-Pos}
|
se.getData().add(new BarChart.Data<String, Number>(monthName, numEvents)); //Adding new barData at {X-pos, Y-Pos}
|
||||||
}
|
}
|
||||||
bcData.add(se);
|
bcData.add(se);
|
||||||
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) {
|
for (final BarChart.Data barData : bc.getData().get(0).getData()) {
|
||||||
//Note:
|
//Note:
|
||||||
// All the charts of this package have a problem where when the chart gets below a certain pixel ratio, the data stops drawing. The axes and the labels remain,
|
// All the charts of this package have a problem where when the chart gets below a certain pixel ratio, the barData stops drawing. The axes and the labels remain,
|
||||||
// But the actual chart data is invisible, unclickable, and unrendered. To partially compensate for that, data.getNode() can be manually scaled up to increase visibility.
|
// But the actual chart barData is invisible, unclickable, and unrendered. To partially compensate for that, barData.getNode() can be manually scaled up to increase visibility.
|
||||||
// Sometimes I've had it jacked up to as much as x2400 just to see a sliver of information.
|
// Sometimes I've had it jacked up to as much as x2400 just to see a sliver of information.
|
||||||
// But that doesn't work all the time. Adding it to a scrollpane and letting the user scroll up and down to view the chart is the other workaround. Both of these fixes suck.
|
// But that doesn't work all the time. Adding it to a scrollpane and letting the user scroll up and down to view the chart is the other workaround. Both of these fixes suck.
|
||||||
|
final javafx.scene.Node barNode = barData.getNode();
|
||||||
|
barNode.setScaleX(.5);
|
||||||
|
|
||||||
data.getNode().setScaleX(.5);
|
//hover listener
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
barNode.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, mouseEnteredListener);
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, mouseExitedListener);
|
||||||
|
|
||||||
|
//clicks
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_PRESSED,
|
||||||
new EventHandler<MouseEvent>() {
|
new EventHandler<MouseEvent>() {
|
||||||
@Override
|
@Override
|
||||||
public void handle(MouseEvent e) {
|
public void handle(MouseEvent e) {
|
||||||
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
||||||
if (e.getClickCount() == 2) {
|
if (e.getClickCount() == 1) {
|
||||||
PlatformImpl.startup(new Runnable() {
|
Platform.runLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
chart_Events = createEventsByMonth(findMonth(ye.months, monthStringToInt((String) data.getXValue())), ye);
|
chart_Events = createEventsByMonth(findMonth(ye.months, monthStringToInt((String) barData.getXValue())), ye);
|
||||||
scroll_Events.setContent(chart_Events);
|
scroll_Events.setContent(chart_Events);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -435,35 +485,45 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
ObservableList<BarChart.Series<String, Number>> ol = FXCollections.observableArrayList(series);
|
ObservableList<BarChart.Series<String, Number>> ol = FXCollections.observableArrayList(series);
|
||||||
|
|
||||||
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, ol);
|
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, ol);
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) {
|
for (final BarChart.Data barData : bc.getData().get(0).getData()) {
|
||||||
//data.getNode().setScaleX(2);
|
//data.getNode().setScaleX(2);
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
|
final javafx.scene.Node barNode = barData.getNode();
|
||||||
|
|
||||||
|
//hover listener
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_ENTERED_TARGET, mouseEnteredListener);
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_EXITED_TARGET, mouseExitedListener);
|
||||||
|
|
||||||
|
barNode.addEventHandler(MouseEvent.MOUSE_PRESSED,
|
||||||
new EventHandler<MouseEvent>() {
|
new EventHandler<MouseEvent>() {
|
||||||
MonthEpoch myme = me;
|
MonthEpoch myme = me;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handle(MouseEvent e) {
|
public void handle(MouseEvent e) {
|
||||||
int day = (Integer.valueOf(((String) data.getXValue()).split("-")[1]));
|
final int day = (Integer.valueOf(((String) barData.getXValue()).split("-")[1]));
|
||||||
DayEpoch de = myme.getDay(day);
|
final DayEpoch de = myme.getDay(day);
|
||||||
List<AbstractFile> afs = Collections.EMPTY_LIST;
|
final List<AbstractFile> afs;
|
||||||
if (de != null) {
|
if (de != null) {
|
||||||
afs = de.getEvents();
|
afs = de.getEvents();
|
||||||
} else {
|
} else {
|
||||||
logger.log(Level.SEVERE, "There were no events for the clicked-on day.");
|
logger.log(Level.SEVERE, "There were no events for the clicked-on day: " + day);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
final FsContentRootNode d = new FsContentRootNode("Test Root", afs);
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
final FsContentRootNode d = new FsContentRootNode("Test Root", afs);
|
||||||
dataResult.setNode(d);
|
dataResult.setNode(d);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//set result viewer title path with the current date
|
//set result viewer title path with the current date
|
||||||
String dateString = ye.getYear() + "-" + (1 + me.getMonthInt()) + "-" + +de.dayNum;
|
String dateString = ye.getYear() + "-" + (1 + me.getMonthInt()) + "-" + +de.dayNum;
|
||||||
dataResult.setPath(dateString);
|
dataResult.setPath(dateString);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
bc.autosize();
|
bc.autosize();
|
||||||
bc.setPrefWidth(Width_Frame);
|
bc.setPrefWidth(Width_Frame);
|
||||||
@ -488,7 +548,8 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param mon The month to convert. Must be minimum 4 characters long "February" and "Febr" are acceptable.
|
* @param mon The month to convert. Must be minimum 4 characters long
|
||||||
|
* "February" and "Febr" are acceptable.
|
||||||
* @return The integer value of the month. February = 1, July = 6
|
* @return The integer value of the month. February = 1, July = 6
|
||||||
*/
|
*/
|
||||||
private static int monthStringToInt(String mon) {
|
private static int monthStringToInt(String mon) {
|
||||||
@ -505,7 +566,9 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for finding the proper month in a list of available months
|
* Used for finding the proper month in a list of available months
|
||||||
* @param lst The list of months to search through. It is assumed that the desired match is in this list.
|
*
|
||||||
|
* @param lst The list of months to search through. It is assumed that the
|
||||||
|
* desired match is in this list.
|
||||||
* @param match The month, in integer format, to retrieve.
|
* @param match The month, in integer format, to retrieve.
|
||||||
* @return The month epoch as specified by match.
|
* @return The month epoch as specified by match.
|
||||||
*/
|
*/
|
||||||
@ -520,7 +583,9 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Used for finding the proper year in a list of available years
|
* Used for finding the proper year in a list of available years
|
||||||
* @param lst The list of years to search through. It is assumed that the desired match is in this list.
|
*
|
||||||
|
* @param lst The list of years to search through. It is assumed that the
|
||||||
|
* desired match is in this list.
|
||||||
* @param match The year to retrieve.
|
* @param match The year to retrieve.
|
||||||
* @return The year epoch as specified by match.
|
* @return The year epoch as specified by match.
|
||||||
*/
|
*/
|
||||||
@ -538,7 +603,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
String prop = evt.getPropertyName();
|
String prop = evt.getPropertyName();
|
||||||
if (prop.equals(Case.CASE_ADD_IMAGE)) {
|
if (prop.equals(Case.CASE_ADD_IMAGE)) {
|
||||||
if (jf != null && !jf.isVisible()) {
|
if (jf != null && !jf.isVisible()) {
|
||||||
// change the lastObjectId to trigger a reparse of mactime data
|
// change the lastObjectId to trigger a reparse of mactime barData
|
||||||
++lastObjectId;
|
++lastObjectId;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -563,7 +628,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void clearMactimeData() {
|
private void clearMactimeData() {
|
||||||
// get rid of the old data
|
// get rid of the old barData
|
||||||
data = null;
|
data = null;
|
||||||
|
|
||||||
// get rid of the mactime file
|
// get rid of the mactime file
|
||||||
@ -571,15 +636,16 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
mactimeFile.delete();
|
mactimeFile.delete();
|
||||||
|
|
||||||
// close the jframe
|
// close the jframe
|
||||||
|
if (jf != null) {
|
||||||
jf.setVisible(false);
|
jf.setVisible(false);
|
||||||
jf.dispose();
|
jf.dispose();
|
||||||
|
jf = null;
|
||||||
|
}
|
||||||
|
|
||||||
// remove ourself as change listener on Case
|
// remove ourself as change listener on Case
|
||||||
Case currcase = Case.getCurrentCase();
|
Case.removePropertyChangeListener(this);
|
||||||
if (currcase != null) {
|
|
||||||
currcase.removePropertyChangeListener(this);
|
|
||||||
listeningToAddImage = false;
|
listeningToAddImage = false;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -587,6 +653,7 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
* All of those are Epochs.
|
* All of those are Epochs.
|
||||||
*/
|
*/
|
||||||
abstract class Epoch {
|
abstract class Epoch {
|
||||||
|
|
||||||
abstract public int getNumFiles();
|
abstract public int getNumFiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -743,15 +810,19 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
|
|
||||||
// The node factories used to make lists of files to send to the result viewer
|
// The node factories used to make lists of files to send to the result viewer
|
||||||
private class FsContentNodeChildFactory extends ChildFactory<AbstractFile> {
|
private class FsContentNodeChildFactory extends ChildFactory<AbstractFile> {
|
||||||
|
|
||||||
List<AbstractFile> l;
|
List<AbstractFile> l;
|
||||||
|
|
||||||
FsContentNodeChildFactory(List<AbstractFile> l) {
|
FsContentNodeChildFactory(List<AbstractFile> l) {
|
||||||
this.l = l;
|
this.l = l;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean createKeys(List<AbstractFile> list) {
|
protected boolean createKeys(List<AbstractFile> list) {
|
||||||
list.addAll(l);
|
list.addAll(l);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Node createNodeForKey(AbstractFile file) {
|
protected Node createNodeForKey(AbstractFile file) {
|
||||||
Node wrapped;
|
Node wrapped;
|
||||||
@ -765,15 +836,18 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private class FsContentRootNode extends DisplayableItemNode {
|
private class FsContentRootNode extends DisplayableItemNode {
|
||||||
|
|
||||||
FsContentRootNode(String NAME, List<AbstractFile> l) {
|
FsContentRootNode(String NAME, List<AbstractFile> l) {
|
||||||
super(Children.create(new FsContentNodeChildFactory(l), true));
|
super(Children.create(new FsContentNodeChildFactory(l), true));
|
||||||
super.setName(NAME);
|
super.setName(NAME);
|
||||||
super.setDisplayName(NAME);
|
super.setDisplayName(NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DisplayableItemNode.TYPE getDisplayableItemNodeType() {
|
public DisplayableItemNode.TYPE getDisplayableItemNodeType() {
|
||||||
return DisplayableItemNode.TYPE.CONTENT;
|
return DisplayableItemNode.TYPE.CONTENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
|
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
|
||||||
return null;
|
return null;
|
||||||
@ -791,7 +865,6 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
}
|
}
|
||||||
scan.useDelimiter(",");
|
scan.useDelimiter(",");
|
||||||
scan.nextLine(); // skip the header line
|
scan.nextLine(); // skip the header line
|
||||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
|
||||||
|
|
||||||
int prevYear = -1;
|
int prevYear = -1;
|
||||||
YearEpoch ye = null;
|
YearEpoch ye = null;
|
||||||
@ -829,14 +902,19 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
return years;
|
return years;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crate a body file and return its path or null if error
|
||||||
|
*
|
||||||
|
* @return absolute path string or null if error
|
||||||
|
*/
|
||||||
private String makeBodyFile() {
|
private String makeBodyFile() {
|
||||||
// Setup timestamp
|
// Setup timestamp
|
||||||
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
|
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
|
||||||
Date date = new Date();
|
Date date = new Date();
|
||||||
String datenotime = dateFormat.format(date);
|
String datenotime = dateFormat.format(date);
|
||||||
|
|
||||||
Case currentCase = Case.getCurrentCase();
|
final Case currentCase = Case.getCurrentCase();
|
||||||
SleuthkitCase skCase = currentCase.getSleuthkitCase();
|
|
||||||
// Get report path
|
// Get report path
|
||||||
String bodyFilePath = moduleDir.getAbsolutePath()
|
String bodyFilePath = moduleDir.getAbsolutePath()
|
||||||
+ java.io.File.separator + currentCase.getName() + "-" + datenotime + ".txt";
|
+ java.io.File.separator + currentCase.getName() + "-" + datenotime + ".txt";
|
||||||
@ -845,34 +923,39 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
String filesAndDirs = "type = '" + TskData.TSK_DB_FILES_TYPE_ENUM.FS.getFileType() + "' "
|
String filesAndDirs = "type = '" + TskData.TSK_DB_FILES_TYPE_ENUM.FS.getFileType() + "' "
|
||||||
+ "AND name != '.' "
|
+ "AND name != '.' "
|
||||||
+ "AND name != '..'";
|
+ "AND name != '..'";
|
||||||
List<FsContent> fs = Collections.EMPTY_LIST;
|
List<FsContent> fs = null;
|
||||||
try {
|
try {
|
||||||
fs = skCase.findFilesWhere(filesAndDirs);
|
fs = skCase.findFilesWhere(filesAndDirs);
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
logger.log(Level.SEVERE, "Error querying image files to make a body file: " + bodyFilePath, ex);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loop files and write info to report
|
// Loop files and write info to report
|
||||||
|
FileWriter fileWriter = null;
|
||||||
|
try {
|
||||||
|
fileWriter = new FileWriter(bodyFilePath, true);
|
||||||
|
} catch (IOException ex) {
|
||||||
|
logger.log(Level.SEVERE, "Error creating output stream to write body file to: " + bodyFilePath, ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
BufferedWriter out = null;
|
BufferedWriter out = null;
|
||||||
try {
|
try {
|
||||||
out = new BufferedWriter(new FileWriter(bodyFilePath, true));
|
out = new BufferedWriter(fileWriter);
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Could not create new BufferedWriter for body file.", ex);
|
|
||||||
}
|
|
||||||
for (FsContent file : fs) {
|
for (FsContent file : fs) {
|
||||||
try {
|
// try {
|
||||||
// MD5|name|inode|mode_as_string|ObjId|GID|size|atime|mtime|ctime|crtime
|
// MD5|name|inode|mode_as_string|ObjId|GID|size|atime|mtime|ctime|crtime
|
||||||
//out = new BufferedWriter(new FileWriter(bodyFilePath, true));
|
|
||||||
|
|
||||||
if (file.getMd5Hash() != null) {
|
if (file.getMd5Hash() != null) {
|
||||||
out.write(file.getMd5Hash());
|
out.write(file.getMd5Hash());
|
||||||
}
|
}
|
||||||
out.write("|");
|
out.write("|");
|
||||||
String path = "";
|
String path = null;
|
||||||
try {
|
try {
|
||||||
path = file.getUniquePath();
|
path = file.getUniquePath();
|
||||||
} catch (TskCoreException e) {
|
} catch (TskCoreException e) {
|
||||||
logger.log(Level.WARNING, "Failed to get the unique path.", e);
|
logger.log(Level.SEVERE, "Failed to get the unique path of: " + file + " and writing body file.", e);
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
out.write(path);
|
out.write(path);
|
||||||
@ -899,18 +982,21 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
out.write("|");
|
out.write("|");
|
||||||
out.write(Long.toString(file.getCrtime()));
|
out.write(Long.toString(file.getCrtime()));
|
||||||
out.write("\n");
|
out.write("\n");
|
||||||
|
}
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
logger.log(Level.WARNING, "Probelm while trying to write data to the body file.", ex);
|
logger.log(Level.WARNING, "Error while trying to write data to the body file.", ex);
|
||||||
break;
|
return null;
|
||||||
}
|
} finally {
|
||||||
}
|
if (out != null) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
out.flush();
|
out.flush();
|
||||||
out.close();
|
out.close();
|
||||||
} catch (IOException ex1) {
|
} catch (IOException ex1) {
|
||||||
logger.log(Level.WARNING, "Could not flush and/or close body file.", ex1);
|
logger.log(Level.WARNING, "Could not flush and/or close body file.", ex1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
return bodyFilePath;
|
return bodyFilePath;
|
||||||
}
|
}
|
||||||
@ -948,12 +1034,19 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performAction() {
|
public void performAction() {
|
||||||
|
initTimeline();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initTimeline() {
|
||||||
if (!Case.existsCurrentCase()) {
|
if (!Case.existsCurrentCase()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final Case currentCase = Case.getCurrentCase();
|
||||||
|
skCase = currentCase.getSleuthkitCase();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (Case.getCurrentCase().getImages().isEmpty()) {
|
if (currentCase.getImages().isEmpty()) {
|
||||||
logger.log(Level.INFO, "Error creating timeline, there are no images to parse");
|
logger.log(Level.INFO, "Error creating timeline, there are no images to parse");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@ -978,37 +1071,37 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
Platform.setImplicitExit(false);
|
Platform.setImplicitExit(false);
|
||||||
|
|
||||||
// listen for case changes (specifically images being added).
|
// listen for case changes (specifically images being added).
|
||||||
Case currcase = Case.getCurrentCase();
|
if (Case.isCaseOpen() && !listeningToAddImage) {
|
||||||
if (currcase != null && !listeningToAddImage) {
|
Case.addPropertyChangeListener(this);
|
||||||
currcase.addPropertyChangeListener(this);
|
|
||||||
listeningToAddImage = true;
|
listeningToAddImage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// create the modal dialog
|
// create the modal progressDialog
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
SwingUtilities.invokeLater(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
dialog = new TimelineProgressDialog(jf, true);
|
progressDialog = new TimelineProgressDialog(jf, true);
|
||||||
dialog.setVisible(true);
|
progressDialog.setVisible(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// initialize mactimeFileName
|
// initialize mactimeFileName
|
||||||
mactimeFileName = Case.getCurrentCase().getName() + "-MACTIME.txt";
|
mactimeFileName = currentCase.getName() + "-MACTIME.txt";
|
||||||
|
|
||||||
// see if data has been added to the database since the last
|
// see if barData has been added to the database since the last
|
||||||
// time timeline ran
|
// time timeline ran
|
||||||
long objId = Case.getCurrentCase().getSleuthkitCase().getLastObjectId();
|
long objId = skCase.getLastObjectId();
|
||||||
if (objId != lastObjectId && lastObjectId != -1) {
|
if (objId != lastObjectId && lastObjectId != -1) {
|
||||||
clearMactimeData();
|
clearMactimeData();
|
||||||
}
|
}
|
||||||
lastObjectId = objId;
|
lastObjectId = objId;
|
||||||
|
|
||||||
customizeSwing();
|
|
||||||
customize();
|
customize();
|
||||||
}
|
}
|
||||||
} catch (TskCoreException ex) {
|
} catch (TskCoreException ex) {
|
||||||
Exceptions.printStackTrace(ex);
|
logger.log(Level.SEVERE, "Error when generating timeline, ", ex);
|
||||||
|
} catch (Exception ex) {
|
||||||
|
logger.log(Level.SEVERE, "Unexpected error when generating timeline, ", ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1026,154 +1119,4 @@ public class Simile2 extends CallableSystemAction implements Presenter.Toolbar,
|
|||||||
public boolean asynchronous() {
|
public boolean asynchronous() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//<editor-fold defaultstate="collapsed" desc="Old Functions">
|
|
||||||
/**
|
|
||||||
* Old Functions *
|
|
||||||
*/
|
|
||||||
// private BarChart createEventsByYear(final YearEpoch ye) {
|
|
||||||
// final CategoryAxis xAxis = new CategoryAxis();
|
|
||||||
// final NumberAxis yAxis = new NumberAxis();
|
|
||||||
// final int maxEvents = ye.max;
|
|
||||||
// final int maxNumDays = ye.numDays;
|
|
||||||
// xAxis.setLabel("Day of Year");
|
|
||||||
// yAxis.setLabel("Number of Events");
|
|
||||||
// ObservableList<BarChart.Series<String, Number>> seriesList = FXCollections.observableArrayList();
|
|
||||||
// for (final MonthEpoch me : ye.months) {
|
|
||||||
// BarChart.Series<String, Number> series = new BarChart.Series(makeObservableListByMonthAllDays(me));
|
|
||||||
// series.setName(me.getMonthName() + " " + me.year);
|
|
||||||
// seriesList.add(series);
|
|
||||||
// }
|
|
||||||
// final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, seriesList);
|
|
||||||
// for (int i = 0; i < bc.getData().size(); i++) {
|
|
||||||
// for (final BarChart.Data data : bc.getData().get(i).getData()) {
|
|
||||||
// data.getNode().setScaleX(240);
|
|
||||||
// data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
// new EventHandler<MouseEvent>() {
|
|
||||||
// @Override
|
|
||||||
// public void handle(MouseEvent e) {
|
|
||||||
// final FsContentRootNode d = new FsContentRootNode("Test Year Nodes", (((MonthEpoch) data.getExtraValue()).getDays().get(Integer.valueOf(((String) data.getXValue()).split("-")[1]))).getEvents());
|
|
||||||
// SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public void run() {
|
|
||||||
// dataResult.setNode(d);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// bc.autosize();
|
|
||||||
// bc.setPrefWidth(63 * maxNumDays);
|
|
||||||
// //bc.setScaleX(1.25);
|
|
||||||
// //bc.setMinSize(30 * maxNumDays, 4 * maxEvents); // 40 * numDays in year, 4 * maxEvents
|
|
||||||
// //bc.setMaxSize(30 * maxNumDays, 4 * maxEvents); //Width, Height
|
|
||||||
// return bc;
|
|
||||||
// }
|
|
||||||
/*
|
|
||||||
* the small 12-month chart
|
|
||||||
*/
|
|
||||||
// private BarChart createMonthBarChartFromYear(final YearEpoch ye) {
|
|
||||||
// final CategoryAxis xAxis = new CategoryAxis();
|
|
||||||
// final NumberAxis yAxis = new NumberAxis();
|
|
||||||
// final int numMonths = 12;
|
|
||||||
// final int maxEvents = ye.max;
|
|
||||||
// xAxis.setLabel("Month (" + ye.year + ")");
|
|
||||||
// yAxis.setLabel("Number of Events");
|
|
||||||
// ObservableList<BarChart.Series<String, Number>> bcData = FXCollections.observableArrayList();
|
|
||||||
//
|
|
||||||
// BarChart.Series<String, Number> se = new BarChart.Series<String, Number>();
|
|
||||||
// for (final MonthEpoch me : ye.months) {
|
|
||||||
// se.getData().add(new BarChart.Data<String, Number>(me.getMonthName(), me.total));
|
|
||||||
// }
|
|
||||||
// bcData.add(se);
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);//bcData);
|
|
||||||
// for (int i = 0; i < numMonths; i++) {
|
|
||||||
// for (final BarChart.Data data : bc.getData().get(0).getData()) {
|
|
||||||
// data.getNode().setScaleX(26);
|
|
||||||
// data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
// new EventHandler<MouseEvent>() {
|
|
||||||
// @Override
|
|
||||||
// public void handle(MouseEvent e) {
|
|
||||||
//
|
|
||||||
// PlatformImpl.startup(new Runnable() {
|
|
||||||
// @Override
|
|
||||||
// public void run() {
|
|
||||||
// chart_Events = createEventsByMonth(findMonth(ye.months, month_StringtoInt((String) data.getXValue())));
|
|
||||||
// scroll_Events.setContent(chart_Events);
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// bc.setPrefHeight(Height_PieBoxes);
|
|
||||||
// bc.setPrefWidth(Width_PieBoxes);
|
|
||||||
// bc.setLegendVisible(false);
|
|
||||||
// return bc;
|
|
||||||
// }
|
|
||||||
// boolean checkCache(List<YearEpoch> epochs, int match) {
|
|
||||||
// for (Epoch e : epochs) {
|
|
||||||
// if (e.year == match) {
|
|
||||||
// return true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// YearEpoch getFromCache(List<YearEpoch> epochs, int match) {
|
|
||||||
// for (YearEpoch e : epochs) {
|
|
||||||
// if (e.year == match) {
|
|
||||||
// return e;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// private PieChart initYearChart() {
|
|
||||||
// ObservableList<PieChart.Data> pieChartData =
|
|
||||||
// FXCollections.observableArrayList();
|
|
||||||
//
|
|
||||||
// double totalEvents = 0.0;
|
|
||||||
// String sep = java.io.File.separator;
|
|
||||||
// java.io.File mactime = new java.io.File("C:" + sep + "Users" + sep + "nflower" + sep + "Downloads" + sep + "kanarazu-12-21-2012-11-15-46-mactime.txt");
|
|
||||||
// List<YearEpoch> ls = parseMacTime(mactime);
|
|
||||||
//
|
|
||||||
// for (int i = 0; i < ls.size(); i++) {
|
|
||||||
// cachedYears.add(ls.get(i));
|
|
||||||
// totalEvents += ls.get(i).total;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// for (YearEpoch e : ls) {
|
|
||||||
// PieChart.Data d = new PieChart.Data(String.valueOf(e.year), (e.total / totalEvents) * 100);
|
|
||||||
// pieChartData.add(d);
|
|
||||||
// }
|
|
||||||
// final PieChart chart = new PieChart(pieChartData);
|
|
||||||
// chart.setTitle("Years with Activity");
|
|
||||||
// for (int i = 0; i < chart.getData().size(); i++) {
|
|
||||||
// final PieChart.Data data = chart.getData().get(i);
|
|
||||||
// final YearEpoch y = findYear(ls, Integer.valueOf(data.getName()));
|
|
||||||
// data.getNode().setUserData(y);
|
|
||||||
// data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
// new EventHandler<MouseEvent>() {
|
|
||||||
// @Override
|
|
||||||
// public void handle(MouseEvent e) {
|
|
||||||
// chart_Months = createMonthBarChartFromYear(((YearEpoch) data.getNode().getUserData()));
|
|
||||||
// scroll_Months.setContent(chart_Months);
|
|
||||||
//
|
|
||||||
// chart_Events = createEventsByYear(y);
|
|
||||||
// scroll_Events.setContent(chart_Events);
|
|
||||||
// chart_Events.getBoundsInParent();
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
// chart.setLegendVisible(false);
|
|
||||||
// chart.setPrefHeight(Height_PieBoxes / 1.3);
|
|
||||||
// chart.setPrefWidth(Width_PieBoxes / 1.1);
|
|
||||||
// return chart;
|
|
||||||
// }
|
|
||||||
//</editor-fold>
|
|
||||||
}
|
}
|
@ -1,28 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" ?>
|
|
||||||
|
|
||||||
<Form version="1.4" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
|
|
||||||
<AuxValues>
|
|
||||||
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="1"/>
|
|
||||||
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
|
|
||||||
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="true"/>
|
|
||||||
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
|
|
||||||
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
|
|
||||||
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
|
|
||||||
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
|
|
||||||
</AuxValues>
|
|
||||||
|
|
||||||
<Layout>
|
|
||||||
<DimensionLayout dim="0">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<EmptySpace min="0" pref="400" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
<DimensionLayout dim="1">
|
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
|
||||||
<EmptySpace min="0" pref="300" max="32767" attributes="0"/>
|
|
||||||
</Group>
|
|
||||||
</DimensionLayout>
|
|
||||||
</Layout>
|
|
||||||
</Form>
|
|
@ -1,835 +0,0 @@
|
|||||||
/*
|
|
||||||
* To change this template, choose Tools | Templates
|
|
||||||
* and open the template in the editor.
|
|
||||||
*/
|
|
||||||
package org.sleuthkit.autopsy.timeline;
|
|
||||||
|
|
||||||
import com.sun.javafx.application.PlatformImpl;
|
|
||||||
import java.awt.Component;
|
|
||||||
import java.awt.Dimension;
|
|
||||||
import java.io.BufferedWriter;
|
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileWriter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.sql.ResultSet;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
import java.text.DateFormat;
|
|
||||||
import java.text.DateFormatSymbols;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.text.SimpleDateFormat;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Calendar;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Scanner;
|
|
||||||
import java.util.Stack;
|
|
||||||
import java.util.logging.Level;
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
import javafx.embed.swing.JFXPanel;
|
|
||||||
import javafx.event.ActionEvent;
|
|
||||||
import javafx.event.EventHandler;
|
|
||||||
import javafx.geometry.Pos;
|
|
||||||
import javafx.scene.Group;
|
|
||||||
import javafx.scene.Scene;
|
|
||||||
import javafx.scene.chart.BarChart;
|
|
||||||
import javafx.scene.chart.CategoryAxis;
|
|
||||||
import javafx.scene.chart.NumberAxis;
|
|
||||||
import javafx.scene.control.Button;
|
|
||||||
import javafx.scene.control.ComboBox;
|
|
||||||
import javafx.scene.control.Label;
|
|
||||||
import javafx.scene.control.ScrollPane;
|
|
||||||
import javafx.scene.input.MouseButton;
|
|
||||||
import javafx.scene.input.MouseEvent;
|
|
||||||
import javafx.scene.layout.HBox;
|
|
||||||
import javafx.scene.layout.VBox;
|
|
||||||
import javafx.scene.paint.Color;
|
|
||||||
import javax.swing.BoxLayout;
|
|
||||||
import javax.swing.JFrame;
|
|
||||||
import javax.swing.JPanel;
|
|
||||||
import javax.swing.SwingUtilities;
|
|
||||||
import org.openide.modules.InstalledFileLocator;
|
|
||||||
import org.openide.nodes.ChildFactory;
|
|
||||||
import org.openide.nodes.Children;
|
|
||||||
import org.openide.nodes.Node;
|
|
||||||
import org.sleuthkit.autopsy.casemodule.Case;
|
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataContentPanel;
|
|
||||||
import org.sleuthkit.autopsy.corecomponents.DataResultPanel;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.Logger;
|
|
||||||
import org.sleuthkit.autopsy.coreutils.PlatformUtil;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.DirectoryNode;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNode;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.DisplayableItemNodeVisitor;
|
|
||||||
import org.sleuthkit.autopsy.datamodel.FileNode;
|
|
||||||
import org.sleuthkit.autopsy.recentactivity.JavaSystemCaller;
|
|
||||||
import org.sleuthkit.datamodel.AbstractFile;
|
|
||||||
import org.sleuthkit.datamodel.Directory;
|
|
||||||
import org.sleuthkit.datamodel.File;
|
|
||||||
import org.sleuthkit.datamodel.FsContent;
|
|
||||||
import org.sleuthkit.datamodel.SleuthkitCase;
|
|
||||||
import org.sleuthkit.datamodel.TskCoreException;
|
|
||||||
import org.sleuthkit.datamodel.TskData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class TimelineJPanel extends javax.swing.JPanel {
|
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(TimelineJPanel.class.getName());
|
|
||||||
private final java.io.File macRoot = InstalledFileLocator.getDefault().locate("mactime", TimelineJPanel.class.getPackage().getName(), false);
|
|
||||||
//private JFrame jf; //frame for holding all the elements
|
|
||||||
private Group group_Charts; //Orders the charts
|
|
||||||
private Scene scene_Charts; //Displays the charts
|
|
||||||
private HBox hBox_Charts; //Holds the navigation buttons in horiztonal fashion.
|
|
||||||
private VBox vBox_FX; //Holds the JavaFX Elements in vertical fashion.
|
|
||||||
private JFXPanel panel_Charts; //FX panel to hold the group
|
|
||||||
private BarChart chart_Events; //Yearly/Monthly events - Bar chart
|
|
||||||
private ScrollPane scroll_Events; //Scroll Panes for dealing with oversized an oversized chart
|
|
||||||
private final int Height_Frame = 850; //Sizing constants
|
|
||||||
private final int Width_Frame = 1300;
|
|
||||||
private Button button_DrillUp; //Navigation buttons
|
|
||||||
private Button button_DrillDown;
|
|
||||||
private Button button_Reset;
|
|
||||||
private Button button_Go;
|
|
||||||
private ComboBox<String> dropdown_SelectYears; //Dropdown box for selecting years. Useful when the charts' scale means some years are unclickable, despite having events.
|
|
||||||
private final Stack<BarChart> stack_PrevCharts = new Stack<BarChart>(); //Stack for storing drill-up information.
|
|
||||||
private BarChart chart_TopLevel; //the topmost chart, used for resetting to default view.
|
|
||||||
private DataResultPanel dataResultPanel;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates new form Timeline
|
|
||||||
*/
|
|
||||||
public TimelineJPanel() {
|
|
||||||
initComponents();
|
|
||||||
//customize();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDataResultPanel(DataResultPanel dataResultPanel) {
|
|
||||||
this.dataResultPanel = dataResultPanel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize() {
|
|
||||||
customize();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called from within the constructor to initialize the form.
|
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
|
||||||
* regenerated by the Form Editor.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
|
|
||||||
private void initComponents() {
|
|
||||||
|
|
||||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
|
|
||||||
this.setLayout(layout);
|
|
||||||
layout.setHorizontalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 400, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
layout.setVerticalGroup(
|
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
|
||||||
.addGap(0, 300, Short.MAX_VALUE)
|
|
||||||
);
|
|
||||||
}// </editor-fold>//GEN-END:initComponents
|
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
|
||||||
// End of variables declaration//GEN-END:variables
|
|
||||||
|
|
||||||
private void customize() {
|
|
||||||
|
|
||||||
//Making the main frame *
|
|
||||||
//jf = new JFrame(Case.getCurrentCase().getName() + " - Autopsy Timeline");
|
|
||||||
//jf.setSize(Width_Frame, Height_Frame); //(Width, Height)
|
|
||||||
|
|
||||||
//JPanels are used as the cohesive glue that binds everything together.*/
|
|
||||||
//The chartJpanel holds the chart,
|
|
||||||
//aligned vertically (Y_AXIS)
|
|
||||||
final JPanel chartJPanel = new JPanel();
|
|
||||||
chartJPanel.setLayout(new BoxLayout(chartJPanel, BoxLayout.Y_AXIS));
|
|
||||||
|
|
||||||
//ComboJPanel holds both of the above JPanels together,
|
|
||||||
//aligned vertically (Y_AXIS)
|
|
||||||
final JPanel comboJPanel = new JPanel();
|
|
||||||
comboJPanel.setLayout(new BoxLayout(comboJPanel, BoxLayout.Y_AXIS));
|
|
||||||
|
|
||||||
//JavaFX thread
|
|
||||||
//JavaFX components MUST be run in the JavaFX thread, otherwise massive amounts of exceptions will be thrown and caught. Liable to freeze up and crash.
|
|
||||||
//Components can be declared whenever, but initialization and manipulation must take place here.
|
|
||||||
PlatformImpl.startup(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
System.out.println("JavaFX thread running . . .");
|
|
||||||
panel_Charts = new JFXPanel();
|
|
||||||
group_Charts = new Group();
|
|
||||||
scene_Charts = new Scene(group_Charts, Width_Frame, Math.round(Height_Frame / .75)); //Width, Height
|
|
||||||
vBox_FX = new VBox(5);
|
|
||||||
vBox_FX.setAlignment(Pos.BOTTOM_CENTER);
|
|
||||||
hBox_Charts = new HBox(10);
|
|
||||||
hBox_Charts.setAlignment(Pos.BOTTOM_CENTER);
|
|
||||||
|
|
||||||
//Initializing default values for the scroll pane
|
|
||||||
scroll_Events = new ScrollPane();
|
|
||||||
scroll_Events.setPrefSize(Width_Frame, Math.round(Height_Frame / .75)); //Width, Height
|
|
||||||
scroll_Events.setContent(null); //Needs some content, otherwise it crashes
|
|
||||||
|
|
||||||
//mactime file placeholder, goes to pre-grenerated one on my desktop
|
|
||||||
String bodyFilePath = makeBodyFile();
|
|
||||||
java.io.File mactimePath = new java.io.File(makeMacTime(bodyFilePath));
|
|
||||||
//java.io.File mactime = new java.io.File("C:" + java.io.File.separator + "Users" + java.io.File.separator + "nflower" + java.io.File.separator + "Downloads" + java.io.File.separator + "kanarazu-12-21-2012-11-15-46-mactime.txt");
|
|
||||||
final List<YearEpoch> lsye = parseMacTime(mactimePath); //The sum total of the mactime parsing. YearEpochs contain everything you need to make a timeline.
|
|
||||||
|
|
||||||
//Making a dropdown box to select years.
|
|
||||||
List<String> lsi = new ArrayList<String>(); //List is in the format of {Year : Number of Events}, used for selecting from the dropdown.
|
|
||||||
for (YearEpoch ye : lsye) {
|
|
||||||
lsi.add(ye.year + " : " + ye.getNumFiles());
|
|
||||||
}
|
|
||||||
ObservableList<String> listSelect = FXCollections.observableArrayList(lsi);
|
|
||||||
dropdown_SelectYears = new ComboBox(listSelect);
|
|
||||||
|
|
||||||
//Buttons for navigating up and down the timeline
|
|
||||||
button_DrillDown = new Button("Drill down");
|
|
||||||
button_DrillDown.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
//Placeholder, does nothing. Need to store a chart_LastSelected or something
|
|
||||||
}
|
|
||||||
});
|
|
||||||
button_DrillUp = new Button("Drill up");
|
|
||||||
button_DrillUp.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
BarChart bc;
|
|
||||||
if (stack_PrevCharts.size() == 0) {
|
|
||||||
bc = chart_TopLevel;
|
|
||||||
} else {
|
|
||||||
bc = stack_PrevCharts.pop();
|
|
||||||
}
|
|
||||||
chart_Events = bc;
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
button_Reset = new Button("Reset");
|
|
||||||
button_Reset.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
stack_PrevCharts.clear();
|
|
||||||
chart_Events = chart_TopLevel;
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
button_Go = new Button("►");
|
|
||||||
button_Go.setOnAction(new EventHandler<ActionEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(ActionEvent e) {
|
|
||||||
if (dropdown_SelectYears.getValue() != null) {
|
|
||||||
chart_Events = createMonthsWithDrill(findYear(lsye, Integer.valueOf(dropdown_SelectYears.getValue().split(" ")[0])));
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Adding things to the V and H boxes.
|
|
||||||
//hBox_Charts stores the pseudo menu bar at the top of the timeline. |Drill Up|Drill Down|Reset|View Year: [Select Year]|►|
|
|
||||||
hBox_Charts.getChildren().addAll(button_DrillUp, button_DrillDown, button_Reset, new Label("View Year:"), dropdown_SelectYears, button_Go);
|
|
||||||
vBox_FX.getChildren().addAll(hBox_Charts, scroll_Events); //FxBox_V holds things in a visual stack.
|
|
||||||
group_Charts.getChildren().add(vBox_FX); //Adding the FxBox to the group. Groups make things easier to manipulate without having to update a hundred things every change.
|
|
||||||
panel_Charts.setScene(scene_Charts);
|
|
||||||
|
|
||||||
|
|
||||||
panel_Charts.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
||||||
|
|
||||||
chartJPanel.add(panel_Charts);
|
|
||||||
chartJPanel.setAlignmentX(Component.LEFT_ALIGNMENT);
|
|
||||||
comboJPanel.add(chartJPanel);
|
|
||||||
|
|
||||||
chart_TopLevel = createYearChartWithDrill(lsye);
|
|
||||||
chart_Events = chart_TopLevel;
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
|
|
||||||
// set bg color of comboJPanel
|
|
||||||
comboJPanel.setBackground(java.awt.Color.red);
|
|
||||||
comboJPanel.setVisible(true);
|
|
||||||
|
|
||||||
setLayout(new BoxLayout(TimelineJPanel.this, BoxLayout.PAGE_AXIS));
|
|
||||||
add(comboJPanel);
|
|
||||||
//setVisible(true);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeBodyFile() {
|
|
||||||
// Setup timestamp
|
|
||||||
DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy-HH-mm-ss");
|
|
||||||
Date date = new Date();
|
|
||||||
String datenotime = dateFormat.format(date);
|
|
||||||
|
|
||||||
Case currentCase = Case.getCurrentCase();
|
|
||||||
SleuthkitCase skCase = currentCase.getSleuthkitCase();
|
|
||||||
// Get report path
|
|
||||||
String bodyFilePath = currentCase.getCaseDirectory() + java.io.File.separator + "temp"
|
|
||||||
+ java.io.File.separator + currentCase.getName() + "-" + datenotime + ".txt";
|
|
||||||
|
|
||||||
// Run query to get all files
|
|
||||||
ResultSet rs = null;
|
|
||||||
try {
|
|
||||||
// exclude non-fs files/dirs and . and .. files
|
|
||||||
rs = skCase.runQuery("SELECT * FROM tsk_files "
|
|
||||||
+ "WHERE type = '" + TskData.TSK_DB_FILES_TYPE_ENUM.FS.getFileType() + "' "
|
|
||||||
+ "AND name != '.' "
|
|
||||||
+ "AND name != '..'");
|
|
||||||
List<FsContent> fs = skCase.resultSetToFsContents(rs);
|
|
||||||
// Loop files and write info to report
|
|
||||||
for (FsContent file : fs) {
|
|
||||||
|
|
||||||
BufferedWriter out = null;
|
|
||||||
try {
|
|
||||||
// MD5|name|inode|mode_as_string|ObjId|GID|size|atime|mtime|ctime|crtime
|
|
||||||
out = new BufferedWriter(new FileWriter(bodyFilePath, true));
|
|
||||||
|
|
||||||
if (file.getMd5Hash() != null) {
|
|
||||||
out.write(file.getMd5Hash());
|
|
||||||
}
|
|
||||||
out.write("|");
|
|
||||||
if (file.getUniquePath() != null) {
|
|
||||||
out.write(file.getUniquePath());
|
|
||||||
}
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getMetaAddr()));
|
|
||||||
out.write("|");
|
|
||||||
String modeString = file.getModesAsString();
|
|
||||||
if (modeString != null) {
|
|
||||||
out.write(modeString);
|
|
||||||
}
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getId()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getGid()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getSize()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getAtime()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getMtime()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getCtime()));
|
|
||||||
out.write("|");
|
|
||||||
out.write(Long.toString(file.getCrtime()));
|
|
||||||
out.write("\n");
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Could not write the temp body file report.", ex);
|
|
||||||
} finally {
|
|
||||||
try {
|
|
||||||
out.flush();
|
|
||||||
out.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
logger.log(Level.WARNING, "Could not flush and close the BufferedWriter.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
logger.log(Level.WARNING, "Failed to get all file information.", ex);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.WARNING, "Failed to get the unique path.", ex);
|
|
||||||
} finally {
|
|
||||||
try {// Close the query
|
|
||||||
if (rs != null) {
|
|
||||||
skCase.closeRunQuery(rs);
|
|
||||||
}
|
|
||||||
} catch (SQLException ex) {
|
|
||||||
logger.log(Level.WARNING, "Failed to close the query.", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return bodyFilePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String makeMacTime(String pathToBodyFile) {
|
|
||||||
String macpath = "";
|
|
||||||
final String machome = macRoot.getAbsolutePath();
|
|
||||||
if (PlatformUtil.isWindowsOS()) {
|
|
||||||
macpath = machome + java.io.File.separator + "mactime.exe";
|
|
||||||
} else {
|
|
||||||
macpath = "perl " + machome + java.io.File.separator + "mactime.pl";
|
|
||||||
}
|
|
||||||
String macfile = Case.getCurrentCase().getCaseDirectory() + java.io.File.separator + "temp" + java.io.File.separator + Case.getCurrentCase().getName() + "-MACTIME.txt";
|
|
||||||
String command = macpath + " -b " + "\"" + pathToBodyFile + "\"" + " -d " + " -y " + ">" + "\"" + macfile + "\"";
|
|
||||||
try {
|
|
||||||
JavaSystemCaller.Exec.execute("\"" + command + "\"");
|
|
||||||
return macfile;
|
|
||||||
} catch (InterruptedException ie) {
|
|
||||||
logger.log(Level.WARNING, "Mactime process was interrupted by user", ie);
|
|
||||||
} catch (IOException ioe) {
|
|
||||||
logger.log(Level.SEVERE, "Could not create mactime file, encountered error ", ioe);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<YearEpoch> parseMacTime(java.io.File f) {
|
|
||||||
//System.out.println("Parsing mactime file.");
|
|
||||||
List<YearEpoch> years = new ArrayList<>();
|
|
||||||
Scanner scan;
|
|
||||||
try {
|
|
||||||
scan = new Scanner(new FileInputStream(f));
|
|
||||||
} catch (FileNotFoundException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Error: could not find mactime file.", ex);
|
|
||||||
return years;
|
|
||||||
}
|
|
||||||
scan.useDelimiter(",");
|
|
||||||
scan.nextLine(); // skip the header line
|
|
||||||
SleuthkitCase skCase = Case.getCurrentCase().getSleuthkitCase();
|
|
||||||
|
|
||||||
int prevYear = -1;
|
|
||||||
YearEpoch ye = null;
|
|
||||||
while (scan.hasNextLine()) {
|
|
||||||
String[] s = scan.nextLine().split(","); //1999-02-08T11:08:08Z, 78706, m..b, rrwxrwxrwx, 0, 0, 8355, /img...
|
|
||||||
String[] datetime = s[0].split("T"); //{1999-02-08, 11:08:08Z}
|
|
||||||
String[] date = datetime[0].split("-"); // {1999, 02, 08}
|
|
||||||
int year = Integer.valueOf(date[0]);
|
|
||||||
int month = Integer.valueOf(date[1]) - 1; //Months are zero indexed: 1 = February, 6 = July, 11 = December
|
|
||||||
int day = Integer.valueOf(date[2]); //Days are 1 indexed
|
|
||||||
long ObjId = Long.valueOf(s[4]);
|
|
||||||
|
|
||||||
// when the year changes, create and add a new YearEpoch object to the list
|
|
||||||
if (year != prevYear) {
|
|
||||||
ye = new YearEpoch(year);
|
|
||||||
years.add(ye);
|
|
||||||
prevYear = year;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create and add the file
|
|
||||||
AbstractFile file;
|
|
||||||
try {
|
|
||||||
file = skCase.getAbstractFileById(ObjId);
|
|
||||||
} catch (TskCoreException ex) {
|
|
||||||
logger.log(Level.SEVERE, "Could not find a file with ID " + ObjId, ex);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
ye.add(file, month, day);
|
|
||||||
}
|
|
||||||
|
|
||||||
return years;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* The backbone of the timeline functionality, years are split into months, months into days, and days contain the events of that given day.
|
|
||||||
* All of those are Epochs.
|
|
||||||
*/
|
|
||||||
abstract class Epoch {
|
|
||||||
abstract public int getNumFiles();
|
|
||||||
}
|
|
||||||
|
|
||||||
private class YearEpoch extends Epoch {
|
|
||||||
|
|
||||||
private int year;
|
|
||||||
private List<MonthEpoch> months = new ArrayList<>();
|
|
||||||
|
|
||||||
YearEpoch(int year) {
|
|
||||||
this.year = year;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getYear() {
|
|
||||||
return year;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNumFiles() {
|
|
||||||
int size = 0;
|
|
||||||
for (MonthEpoch me : months) {
|
|
||||||
size += me.getNumFiles();
|
|
||||||
}
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(AbstractFile af, int month, int day) {
|
|
||||||
// see if this month is in the list
|
|
||||||
MonthEpoch monthEpoch = null;
|
|
||||||
for (MonthEpoch me : months) {
|
|
||||||
if (me.getMonthInt() == month) {
|
|
||||||
monthEpoch = me;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (monthEpoch == null) {
|
|
||||||
monthEpoch = new MonthEpoch(month);
|
|
||||||
months.add(monthEpoch);
|
|
||||||
}
|
|
||||||
|
|
||||||
// add the file the the MonthEpoch object
|
|
||||||
monthEpoch.add(af, day);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MonthEpoch extends Epoch {
|
|
||||||
|
|
||||||
private int month; //Zero-indexed: June = 5, August = 7, etc
|
|
||||||
private List<DayEpoch> days = new ArrayList<>(); //List of DayEpochs in this month, max 31
|
|
||||||
|
|
||||||
MonthEpoch(int month) {
|
|
||||||
this.month = month;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getMonthInt() {
|
|
||||||
return month;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getTotalNumDays(int year) {
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.set(year, month, 1);
|
|
||||||
return cal.getActualMaximum(Calendar.DAY_OF_MONTH);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getNumFiles() {
|
|
||||||
int numFiles = 0;
|
|
||||||
for (DayEpoch de : days) {
|
|
||||||
numFiles += de.getNumFiles();
|
|
||||||
}
|
|
||||||
return numFiles;
|
|
||||||
}
|
|
||||||
|
|
||||||
public DayEpoch getDay(int dayNum) {
|
|
||||||
DayEpoch de = null;
|
|
||||||
for (DayEpoch d : days) {
|
|
||||||
if (d.dayNum == dayNum) {
|
|
||||||
de = d;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return de;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(AbstractFile af, int day) {
|
|
||||||
DayEpoch dayEpoch = null;
|
|
||||||
for (DayEpoch de : days) {
|
|
||||||
if (de.getDayInt() == day) {
|
|
||||||
dayEpoch = de;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dayEpoch == null) {
|
|
||||||
dayEpoch = new DayEpoch(day);
|
|
||||||
days.add(dayEpoch);
|
|
||||||
}
|
|
||||||
|
|
||||||
dayEpoch.add(af);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the month's name in String format, e.g., September, July,
|
|
||||||
*/
|
|
||||||
String getMonthName() {
|
|
||||||
return new DateFormatSymbols().getMonths()[month];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the list of days in this month
|
|
||||||
*/
|
|
||||||
List<DayEpoch> getDays() {
|
|
||||||
return this.days;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class DayEpoch extends Epoch {
|
|
||||||
|
|
||||||
private List<AbstractFile> files = new ArrayList<>();
|
|
||||||
int dayNum = 0; //Day of the month this Epoch represents, 1 indexed: 28=28.
|
|
||||||
|
|
||||||
DayEpoch(int dayOfMonth) {
|
|
||||||
this.dayNum = dayOfMonth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getDayInt() {
|
|
||||||
return dayNum;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumFiles() {
|
|
||||||
return files.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void add(AbstractFile af) {
|
|
||||||
files.add(af);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<AbstractFile> getEvents() {
|
|
||||||
return this.files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Displays a chart with events from one year only, separated into 1-month chunks.
|
|
||||||
* Always 12 per year, empty months are represented by no bar.
|
|
||||||
*/
|
|
||||||
private BarChart createMonthsWithDrill(final YearEpoch ye) {
|
|
||||||
|
|
||||||
final CategoryAxis xAxis = new CategoryAxis();
|
|
||||||
final NumberAxis yAxis = new NumberAxis();
|
|
||||||
xAxis.setLabel("Month (" + ye.year + ")");
|
|
||||||
yAxis.setLabel("Number of Events");
|
|
||||||
ObservableList<BarChart.Series<String, Number>> bcData = FXCollections.observableArrayList();
|
|
||||||
|
|
||||||
BarChart.Series<String, Number> se = new BarChart.Series<String, Number>();
|
|
||||||
for (final MonthEpoch me : ye.months) {
|
|
||||||
se.getData().add(new BarChart.Data<String, Number>(me.getMonthName(), me.getNumFiles())); //Adding new data at {X-pos, Y-Pos}
|
|
||||||
}
|
|
||||||
bcData.add(se);
|
|
||||||
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
|
||||||
|
|
||||||
for (int i = 0; i < 12; i++) {
|
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) {
|
|
||||||
//Note:
|
|
||||||
// All the charts of this package have a problem where when the chart gets below a certain pixel ratio, the data stops drawing. The axes and the labels remain,
|
|
||||||
// But the actual chart data is invisible, unclickable, and unrendered. To partially compensate for that, data.getNode() can be manually scaled up to increase visibility.
|
|
||||||
// Sometimes I've had it jacked up to as much as x2400 just to see a sliver of information.
|
|
||||||
// But that doesn't work all the time. Adding it to a scrollpane and letting the user scroll up and down to view the chart is the other workaround. Both of these fixes suck.
|
|
||||||
|
|
||||||
data.getNode().setScaleX(.5);
|
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
new EventHandler<MouseEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(MouseEvent e) {
|
|
||||||
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
|
||||||
if (e.getClickCount() == 2) {
|
|
||||||
PlatformImpl.startup(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
chart_Events = createEventsByMonth(findMonth(ye.months, month_StringtoInt((String) data.getXValue())), ye);
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bc.autosize();
|
|
||||||
bc.setPrefWidth(Width_Frame);
|
|
||||||
bc.setLegendVisible(false);
|
|
||||||
stack_PrevCharts.push(bc);
|
|
||||||
return bc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Displays a chart with events from one month only.
|
|
||||||
* Up to 31 days per month, as low as 28 as determined by the specific MonthEpoch
|
|
||||||
*/
|
|
||||||
private BarChart createEventsByMonth(final MonthEpoch me, final YearEpoch ye) {
|
|
||||||
final CategoryAxis xAxis = new CategoryAxis();
|
|
||||||
final NumberAxis yAxis = new NumberAxis();
|
|
||||||
xAxis.setLabel("Day of Month");
|
|
||||||
yAxis.setLabel("Number of Events");
|
|
||||||
ObservableList<BarChart.Data> bcData = makeObservableListByMonthAllDays(me, ye.getYear());
|
|
||||||
BarChart.Series<String, Number> series = new BarChart.Series(bcData);
|
|
||||||
series.setName(me.getMonthName() + " " + ye.getYear());
|
|
||||||
|
|
||||||
ObservableList<BarChart.Series<String, Number>> ol = FXCollections.observableArrayList(series);
|
|
||||||
|
|
||||||
final BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, ol);
|
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) {
|
|
||||||
//data.getNode().setScaleX(2);
|
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_PRESSED,
|
|
||||||
new EventHandler<MouseEvent>() {
|
|
||||||
MonthEpoch myme = me;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handle(MouseEvent e) {
|
|
||||||
int day = (Integer.valueOf(((String) data.getXValue()).split("-")[1]));
|
|
||||||
DayEpoch de = myme.getDay(day);
|
|
||||||
List<AbstractFile> afs = Collections.EMPTY_LIST;
|
|
||||||
if (de != null) {
|
|
||||||
afs = de.getEvents();
|
|
||||||
} else {
|
|
||||||
logger.log(Level.SEVERE, "There were no events for the clicked-on day.");
|
|
||||||
}
|
|
||||||
final FsContentRootNode d = new FsContentRootNode("Test Root", afs);
|
|
||||||
SwingUtilities.invokeLater(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
dataResultPanel.setNode(d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
bc.autosize();
|
|
||||||
bc.setPrefWidth(Width_Frame);
|
|
||||||
return bc;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param mon The month to convert. Must be minimum 4 characters long "February" and "Febr" are acceptable.
|
|
||||||
* @return The integer value of the month. February = 1, July = 6
|
|
||||||
*/
|
|
||||||
private int month_StringtoInt(String mon) {
|
|
||||||
try {
|
|
||||||
Date date = new SimpleDateFormat("MMMM", Locale.ENGLISH).parse(mon);
|
|
||||||
Calendar cal = Calendar.getInstance();
|
|
||||||
cal.setTime(date);
|
|
||||||
return cal.get(Calendar.MONTH);
|
|
||||||
} catch (ParseException ex) {
|
|
||||||
logger.log(Level.WARNING, "Unable to convert string " + mon + " to integer", ex);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for finding the proper month in a list of available months
|
|
||||||
* @param lst The list of months to search through. It is assumed that the desired match is in this list.
|
|
||||||
* @param match The month, in integer format, to retrieve.
|
|
||||||
* @return The month epoch as specified by match.
|
|
||||||
*/
|
|
||||||
private MonthEpoch findMonth(List<MonthEpoch> lst, int match) {
|
|
||||||
for (MonthEpoch e : lst) {
|
|
||||||
if (e.month == match) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObservableList<BarChart.Data> makeObservableListByMonthAllDays(final MonthEpoch me, int year) {
|
|
||||||
ObservableList<BarChart.Data> bcData = FXCollections.observableArrayList();
|
|
||||||
int totalDays = me.getTotalNumDays(year);
|
|
||||||
for (int i = 1; i <= totalDays; ++i) {
|
|
||||||
DayEpoch day = me.getDay(i);
|
|
||||||
int numFiles = day == null ? 0 : day.getNumFiles();
|
|
||||||
BarChart.Data d = new BarChart.Data(me.month + 1 + "-" + i, numFiles);
|
|
||||||
d.setExtraValue(me);
|
|
||||||
bcData.add(d);
|
|
||||||
}
|
|
||||||
return bcData;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The node factories used to make lists of files to send to the result viewer
|
|
||||||
private class FsContentNodeChildFactory extends ChildFactory<AbstractFile> {
|
|
||||||
List<AbstractFile> l;
|
|
||||||
FsContentNodeChildFactory(List<AbstractFile> l) {
|
|
||||||
this.l = l;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected boolean createKeys(List<AbstractFile> list) {
|
|
||||||
list.addAll(l);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
protected Node createNodeForKey(AbstractFile file) {
|
|
||||||
Node n;
|
|
||||||
if (file.isDir()) {
|
|
||||||
n = new DirectoryNode((Directory) file);
|
|
||||||
} else {
|
|
||||||
n = new FileNode((File) file) {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isLeafTypeNode() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class FsContentRootNode extends DisplayableItemNode {
|
|
||||||
FsContentRootNode(String NAME, List<AbstractFile> l) {
|
|
||||||
super(Children.create(new FsContentNodeChildFactory(l), true));
|
|
||||||
super.setName(NAME);
|
|
||||||
super.setDisplayName(NAME);
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public DisplayableItemNode.TYPE getDisplayableItemNodeType() {
|
|
||||||
return DisplayableItemNode.TYPE.CONTENT;
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public <T> T accept(DisplayableItemNodeVisitor<T> v) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for finding the proper year in a list of available years
|
|
||||||
* @param lst The list of years to search through. It is assumed that the desired match is in this list.
|
|
||||||
* @param match The year to retrieve.
|
|
||||||
* @return The year epoch as specified by match.
|
|
||||||
*/
|
|
||||||
private YearEpoch findYear(List<YearEpoch> lst, int match) {
|
|
||||||
for (YearEpoch e : lst) {
|
|
||||||
if (e.year == match) {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a BarChart with datapoints for all the years from the parsed
|
|
||||||
* mactime file.
|
|
||||||
*
|
|
||||||
* @param allYears The list of years that have data from the mactime file
|
|
||||||
* @return BarChart scaled to the year level
|
|
||||||
*/
|
|
||||||
private BarChart createYearChartWithDrill(final List<YearEpoch> allYears) {
|
|
||||||
final CategoryAxis xAxis = new CategoryAxis(); //Axes are very specific types. Categorys are strings.
|
|
||||||
final NumberAxis yAxis = new NumberAxis();
|
|
||||||
final Label l = new Label("");
|
|
||||||
l.setStyle("-fx-font: 24 arial;");
|
|
||||||
l.setTextFill(Color.AZURE);
|
|
||||||
xAxis.setLabel("Years");
|
|
||||||
yAxis.setLabel("Number of Events");
|
|
||||||
//Charts are made up of individual pieces of Chart.Data. In this case, a piece of data is a single bar on the graph.
|
|
||||||
//Data is packaged into a series, which can be assigned custom colors or styling
|
|
||||||
//After the series are created, 1 or more series are packaged into a single chart.
|
|
||||||
ObservableList<BarChart.Series<String, Number>> bcData = FXCollections.observableArrayList();
|
|
||||||
BarChart.Series<String, Number> se = new BarChart.Series<String, Number>();
|
|
||||||
for (final YearEpoch ye : allYears) {
|
|
||||||
se.getData().add(new BarChart.Data<String, Number>(String.valueOf(ye.year), ye.getNumFiles()));
|
|
||||||
}
|
|
||||||
bcData.add(se);
|
|
||||||
|
|
||||||
//Note:
|
|
||||||
// BarChart.Data wraps the Java Nodes class. BUT, until a BarChart.Data gets added to an actual series, it's node is null, and you can perform no operations on it.
|
|
||||||
// When the Data is added to a series(or a chart? I am unclear on where), a node is automaticaly generated for it, after which you can perform any of the operations it offers.
|
|
||||||
// In addtion, you are free to set the node to whatever you want. It wraps the most generic Node class.
|
|
||||||
// But it is for this reason that the chart generating functions have two forloops. I do not believe they can be condensed into a single loop due to the nodes being null until
|
|
||||||
// an undetermined point in time.
|
|
||||||
BarChart<String, Number> bc = new BarChart<String, Number>(xAxis, yAxis, bcData);
|
|
||||||
for (final BarChart.Data data : bc.getData().get(0).getData()) { //.get(0) refers to the BarChart.Series class to work on. There is only one series in this graph, so get(0) is safe.
|
|
||||||
data.getNode().setScaleX(.5);
|
|
||||||
data.getNode().addEventHandler(MouseEvent.MOUSE_CLICKED,
|
|
||||||
new EventHandler<MouseEvent>() {
|
|
||||||
@Override
|
|
||||||
public void handle(MouseEvent e) {
|
|
||||||
if (e.getButton().equals(MouseButton.PRIMARY)) {
|
|
||||||
if (e.getClickCount() == 2) { //Checking for a doubleclick
|
|
||||||
PlatformImpl.startup(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
BarChart b = createMonthsWithDrill((YearEpoch) findYear(allYears, Integer.valueOf((String) data.getXValue())));
|
|
||||||
chart_Events = b;
|
|
||||||
scroll_Events.setContent(chart_Events);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
//If a single click, hover a label over the cursor with information about the selection
|
|
||||||
} else if (e.getClickCount() == 1) {
|
|
||||||
l.setText(findYear(allYears, Integer.valueOf((String) data.getXValue())).getNumFiles() + " events");
|
|
||||||
l.setTranslateX(e.getX());
|
|
||||||
l.setTranslateY(e.getY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
bc.autosize(); //Get an auto height
|
|
||||||
bc.setPrefWidth(Width_Frame); //but override the width
|
|
||||||
bc.setLegendVisible(false); //The legend adds too much extra chart space, it's not necessary.
|
|
||||||
return bc;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,19 +23,27 @@
|
|||||||
<Layout>
|
<Layout>
|
||||||
<DimensionLayout dim="0">
|
<DimensionLayout dim="0">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Group type="102" attributes="0">
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
|
<Component id="progressBar" pref="504" max="32767" attributes="0"/>
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<EmptySpace min="-2" pref="51" max="-2" attributes="0"/>
|
|
||||||
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace pref="131" max="32767" attributes="0"/>
|
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||||
|
</Group>
|
||||||
|
</Group>
|
||||||
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
<DimensionLayout dim="1">
|
<DimensionLayout dim="1">
|
||||||
<Group type="103" groupAlignment="0" attributes="0">
|
<Group type="103" groupAlignment="0" attributes="0">
|
||||||
<Group type="102" alignment="0" attributes="0">
|
<Group type="102" alignment="0" attributes="0">
|
||||||
<EmptySpace min="-2" pref="37" max="-2" attributes="0"/>
|
<EmptySpace max="-2" attributes="0"/>
|
||||||
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
|
||||||
<EmptySpace pref="64" max="32767" attributes="0"/>
|
<EmptySpace min="-2" pref="7" max="-2" attributes="0"/>
|
||||||
|
<Component id="progressBar" min="-2" max="-2" attributes="0"/>
|
||||||
|
<EmptySpace pref="16" max="32767" attributes="0"/>
|
||||||
</Group>
|
</Group>
|
||||||
</Group>
|
</Group>
|
||||||
</DimensionLayout>
|
</DimensionLayout>
|
||||||
@ -48,5 +56,7 @@
|
|||||||
</Property>
|
</Property>
|
||||||
</Properties>
|
</Properties>
|
||||||
</Component>
|
</Component>
|
||||||
|
<Component class="javax.swing.JProgressBar" name="progressBar">
|
||||||
|
</Component>
|
||||||
</SubComponents>
|
</SubComponents>
|
||||||
</Form>
|
</Form>
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.sleuthkit.autopsy.timeline;
|
package org.sleuthkit.autopsy.timeline;
|
||||||
|
|
||||||
|
import java.awt.EventQueue;
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
import javax.swing.AbstractAction;
|
import javax.swing.AbstractAction;
|
||||||
@ -29,8 +29,7 @@ import javax.swing.KeyStroke;
|
|||||||
import org.openide.windows.WindowManager;
|
import org.openide.windows.WindowManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* Dialog with progress bar that pops up when timeline is being generated
|
||||||
* @author mciver
|
|
||||||
*/
|
*/
|
||||||
public class TimelineProgressDialog extends javax.swing.JDialog {
|
public class TimelineProgressDialog extends javax.swing.JDialog {
|
||||||
|
|
||||||
@ -54,6 +53,10 @@ public class TimelineProgressDialog extends javax.swing.JDialog {
|
|||||||
//set icon the same as main app
|
//set icon the same as main app
|
||||||
setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
|
setIconImage(WindowManager.getDefault().getMainWindow().getIconImage());
|
||||||
|
|
||||||
|
//progressBar.setIndeterminate(true);
|
||||||
|
|
||||||
|
setName("Make Timeline (Beta)");
|
||||||
|
|
||||||
// Close the dialog when Esc is pressed
|
// Close the dialog when Esc is pressed
|
||||||
String cancelName = "cancel";
|
String cancelName = "cancel";
|
||||||
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
|
||||||
@ -73,6 +76,40 @@ public class TimelineProgressDialog extends javax.swing.JDialog {
|
|||||||
return returnStatus;
|
return returnStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void updateProgressBar(final int progress) {
|
||||||
|
EventQueue.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setValue(progress);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateProgressBar(final String message) {
|
||||||
|
EventQueue.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
progressBar.setString(message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void setProgressTotal(final int total) {
|
||||||
|
EventQueue.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
//progressBar.setIndeterminate(false);
|
||||||
|
progressBar.setMaximum(total);
|
||||||
|
//progressBar.setValue(0);
|
||||||
|
progressBar.setStringPainted(true);
|
||||||
|
progressBar.setVisible(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called from within the constructor to initialize the form.
|
* This method is called from within the constructor to initialize the form.
|
||||||
* WARNING: Do NOT modify this code. The content of this method is always
|
* WARNING: Do NOT modify this code. The content of this method is always
|
||||||
@ -83,6 +120,7 @@ public class TimelineProgressDialog extends javax.swing.JDialog {
|
|||||||
private void initComponents() {
|
private void initComponents() {
|
||||||
|
|
||||||
jLabel1 = new javax.swing.JLabel();
|
jLabel1 = new javax.swing.JLabel();
|
||||||
|
progressBar = new javax.swing.JProgressBar();
|
||||||
|
|
||||||
addWindowListener(new java.awt.event.WindowAdapter() {
|
addWindowListener(new java.awt.event.WindowAdapter() {
|
||||||
public void windowClosing(java.awt.event.WindowEvent evt) {
|
public void windowClosing(java.awt.event.WindowEvent evt) {
|
||||||
@ -97,16 +135,22 @@ public class TimelineProgressDialog extends javax.swing.JDialog {
|
|||||||
layout.setHorizontalGroup(
|
layout.setHorizontalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addGap(51, 51, 51)
|
.addContainerGap()
|
||||||
|
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
|
.addComponent(progressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 504, Short.MAX_VALUE)
|
||||||
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addComponent(jLabel1)
|
.addComponent(jLabel1)
|
||||||
.addContainerGap(131, Short.MAX_VALUE))
|
.addGap(0, 0, Short.MAX_VALUE)))
|
||||||
|
.addContainerGap())
|
||||||
);
|
);
|
||||||
layout.setVerticalGroup(
|
layout.setVerticalGroup(
|
||||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||||
.addGroup(layout.createSequentialGroup()
|
.addGroup(layout.createSequentialGroup()
|
||||||
.addGap(37, 37, 37)
|
.addContainerGap()
|
||||||
.addComponent(jLabel1)
|
.addComponent(jLabel1)
|
||||||
.addContainerGap(64, Short.MAX_VALUE))
|
.addGap(7, 7, 7)
|
||||||
|
.addComponent(progressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||||
|
.addContainerGap(16, Short.MAX_VALUE))
|
||||||
);
|
);
|
||||||
|
|
||||||
pack();
|
pack();
|
||||||
@ -119,14 +163,20 @@ public class TimelineProgressDialog extends javax.swing.JDialog {
|
|||||||
doClose(RET_CANCEL);
|
doClose(RET_CANCEL);
|
||||||
}//GEN-LAST:event_closeDialog
|
}//GEN-LAST:event_closeDialog
|
||||||
|
|
||||||
public void doClose(int retStatus) {
|
void doClose(final int retStatus) {
|
||||||
|
EventQueue.invokeLater(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
returnStatus = retStatus;
|
returnStatus = retStatus;
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
dispose();
|
dispose();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
// Variables declaration - do not modify//GEN-BEGIN:variables
|
// Variables declaration - do not modify//GEN-BEGIN:variables
|
||||||
private javax.swing.JLabel jLabel1;
|
private javax.swing.JLabel jLabel1;
|
||||||
|
private javax.swing.JProgressBar progressBar;
|
||||||
// End of variables declaration//GEN-END:variables
|
// End of variables declaration//GEN-END:variables
|
||||||
private int returnStatus = RET_CANCEL;
|
private int returnStatus = RET_CANCEL;
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,10 @@
|
|||||||
Windows2
|
Windows2
|
||||||
====================================================== -->
|
====================================================== -->
|
||||||
<folder name="Actions">
|
<folder name="Actions">
|
||||||
|
<folder name="Tools">
|
||||||
|
<file name="org-sleuthkit-autopsy-timeline-Simile2.instance_hidden"/>
|
||||||
|
<file name="org-sleuthkit-autopsy-timeline-Timeline.instance"/>
|
||||||
|
</folder>
|
||||||
<folder name="Window">
|
<folder name="Window">
|
||||||
<file name="org-sleuthkit-autopsy-timeline-Timeline2TopComponent.instance_hidden"/>
|
<file name="org-sleuthkit-autopsy-timeline-Timeline2TopComponent.instance_hidden"/>
|
||||||
<file name="org-sleuthkit-autopsy-timeline-TimelineTopComponent.instance_hidden"/>
|
<file name="org-sleuthkit-autopsy-timeline-TimelineTopComponent.instance_hidden"/>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user