From ef7f2c845ebcd89a9ecc697a11d3af66c63959ae Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 4 Jan 2018 15:09:05 +0100 Subject: [PATCH 001/347] first stab a jgraphx based visualization --- Core/ivy.xml | 7 + Core/ivysettings.xml | 3 +- Core/nbproject/project.properties | 1 + Core/nbproject/project.xml | 6 +- .../communications/AccountsRootChildren.java | 39 ++- .../autopsy/communications/Bundle.properties | 2 + .../autopsy/communications/CVTEvents.java | 21 ++ .../communications/CVTTopComponent.form | 18 +- .../communications/CVTTopComponent.java | 26 +- .../OpenCommVisualizationToolAction.java | 20 +- .../communications/PinAccountAction.java | 7 + .../communications/PinAccountEvent.java | 24 ++ .../communications/RelationshipNode.java | 2 +- .../communications/VisualizationPanel.form | 44 +++ .../communications/VisualizationPanel.java | 258 ++++++++++++++++++ 15 files changed, 460 insertions(+), 18 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form create mode 100644 Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java diff --git a/Core/ivy.xml b/Core/ivy.xml index 35cb8de904..2a099d4e31 100755 --- a/Core/ivy.xml +++ b/Core/ivy.xml @@ -6,6 +6,13 @@ + + + + + + + diff --git a/Core/ivysettings.xml b/Core/ivysettings.xml index c27d905255..7a4d38c65e 100755 --- a/Core/ivysettings.xml +++ b/Core/ivysettings.xml @@ -3,7 +3,8 @@ - + + diff --git a/Core/nbproject/project.properties b/Core/nbproject/project.properties index d98228ef4e..ff1f4f81de 100755 --- a/Core/nbproject/project.properties +++ b/Core/nbproject/project.properties @@ -5,6 +5,7 @@ file.reference.commons-dbcp2-2.1.1.jar=release\\modules\\ext\\commons-dbcp2-2.1. file.reference.commons-pool2-2.4.2.jar=release\\modules\\ext\\commons-pool2-2.4.2.jar file.reference.jdom-2.0.5-contrib.jar=release/modules/ext/jdom-2.0.5-contrib.jar file.reference.jdom-2.0.5.jar=release/modules/ext/jdom-2.0.5.jar +file.reference.jgraphx-v3.8.0.jar=release/modules/ext/jgraphx-v3.8.0.jar file.reference.jsoup-1.10.3.jar=release/modules/ext/jsoup-1.10.3.jar file.reference.jython-standalone-2.7.0.jar=release/modules/ext/jython-standalone-2.7.0.jar file.reference.mchange-commons-java-0.2.9.jar=release/modules/ext/mchange-commons-java-0.2.9.jar diff --git a/Core/nbproject/project.xml b/Core/nbproject/project.xml index 1663abca2b..baaf061b92 100755 --- a/Core/nbproject/project.xml +++ b/Core/nbproject/project.xml @@ -346,7 +346,11 @@ ext/commons-dbcp2-2.1.1.jar - release\modules\ext\commons-dbcp2-2.1.1.jar + release/modules/ext/commons-dbcp2-2.1.1.jar + + + ext/jgraphx-v3.8.0.jar + release/modules/ext/jgraphx-v3.8.0.jar ext/tika-parsers-1.14.jar diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index 63e1e17d82..881b14b67d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -18,10 +18,14 @@ */ package org.sleuthkit.autopsy.communications; +import java.awt.event.ActionEvent; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import javax.swing.AbstractAction; +import javax.swing.Action; import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; @@ -87,17 +91,18 @@ class AccountsRootChildren extends ChildFactory { } return accountDeviceInstance.getDeviceId(); } - + /** * Node to represent an Account in the AccountsBrowser */ static class AccountDeviceInstanceNode extends AbstractNode { private final AccountDeviceInstanceKey accountDeviceInstanceKey; + private final CommunicationsManager commsManager; private final Account account; - private AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); this.accountDeviceInstanceKey = accountDeviceInstanceKey; this.commsManager = commsManager; @@ -110,10 +115,18 @@ class AccountsRootChildren extends ChildFactory { return accountDeviceInstanceKey.getAccountDeviceInstance(); } + public AccountDeviceInstanceKey getAccountDeviceInstanceKey() { + return accountDeviceInstanceKey; + } + public CommunicationsManager getCommsManager() { return commsManager; } + public long getMessageCount() { + return accountDeviceInstanceKey.getMessageCount(); + } + public CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } @@ -139,5 +152,27 @@ class AccountsRootChildren extends ChildFactory { accountDeviceInstanceKey.getDataSourceName())); // NON-NLS return s; } + + @Override + public Action[] getActions(boolean context) { + ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); + actions.add(new PinAccountAction()); + return actions.toArray(new Action[actions.size()]); + } + + /** + * + */ + private class PinAccountAction extends AbstractAction { + + public PinAccountAction() { + super("Visualize Account"); + } + + @Override + public void actionPerformed(ActionEvent e) { + CVTEvents.getCVTEventBus().post(new PinAccountEvent(AccountDeviceInstanceNode.this)); + } + } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 0f1b490d78..5e7d93adf0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,3 +15,5 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. +VisualizationPanel.jButton1.text=jButton1 +CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java new file mode 100644 index 0000000000..d4c76ce2d8 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -0,0 +1,21 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + +import com.google.common.eventbus.EventBus; + +/** + * + */ +public class CVTEvents { + +private final static EventBus cvtEventBus = new EventBus(); + + public static EventBus getCVTEventBus() { + return cvtEventBus; + } + +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index df30cb548f..a24037b927 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -18,9 +18,9 @@ - + - + @@ -75,6 +75,20 @@ + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 1c0582a300..f19e998fb0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; import org.openide.explorer.ExplorerManager; @@ -37,11 +38,11 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; - + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); @@ -63,6 +64,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); + + vizPanel.initVisualization(acctsBrowserExplorerManager); + CVTEvents.getCVTEventBus().register(this); + } + + @Subscribe + public void pinAccount(PinAccountEvent pinEvent) { + browseVisualizeTabPane.setSelectedIndex(1); } /** @@ -76,6 +85,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag splitPane = new javax.swing.JSplitPane(); browseVisualizeTabPane = new javax.swing.JTabbedPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); + vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); splitPane.setDividerLocation(400); @@ -83,6 +93,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N splitPane.setLeftComponent(browseVisualizeTabPane); @@ -94,9 +105,9 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 250, javax.swing.GroupLayout.PREFERRED_SIZE) + .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1339, Short.MAX_VALUE) + .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1324, Short.MAX_VALUE) .addGap(0, 0, 0)) ); layout.setVerticalGroup( @@ -116,6 +127,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; private javax.swing.JSplitPane splitPane; + private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; // End of variables declaration//GEN-END:variables @Override @@ -123,12 +135,12 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag super.componentOpened(); WindowManager.getDefault().setTopComponentFloating(this, true); } - + @Override public ExplorerManager getExplorerManager() { return acctsBrowserExplorerManager; } - + @Override public void open() { super.open(); @@ -140,7 +152,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag */ filtersPane.updateAndApplyFilters(); } - + @Override public List availableModes(List modes) { /* diff --git a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java index fdccf56af7..a977ff2c09 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java +++ b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java @@ -19,6 +19,8 @@ package org.sleuthkit.autopsy.communications; import java.awt.Component; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import javax.swing.ImageIcon; import javax.swing.JButton; import org.openide.awt.ActionID; @@ -29,9 +31,10 @@ import org.openide.util.HelpCtx; import org.openide.util.NbBundle; import org.openide.util.NbBundle.Messages; import org.openide.util.actions.CallableSystemAction; -import org.openide.util.actions.Presenter; import org.openide.windows.TopComponent; import org.openide.windows.WindowManager; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.core.RuntimeProperties; /** * Action that opens the CVT. Available through the Tools menu and the main @@ -41,18 +44,27 @@ import org.openide.windows.WindowManager; id = "org.sleuthkit.autopsy.communicationsVisualization.OpenCVTAction") @ActionRegistration(displayName = "#CTL_OpenCVTAction", lazy = false) @ActionReferences(value = { - @ActionReference(path = "Menu/Tools", position = 102)}) + @ActionReference(path = "Menu/Tools", position = 102) + , + @ActionReference(path = "Toolbars/Case", position = 102)}) @Messages("CTL_OpenCVTAction=Communications") -public final class OpenCommVisualizationToolAction extends CallableSystemAction implements Presenter.Toolbar { +public final class OpenCommVisualizationToolAction extends CallableSystemAction { private static final long serialVersionUID = 1L; - + PropertyChangeListener pcl; private final JButton toolbarButton = new JButton(getName(), new ImageIcon(getClass().getResource("images/emblem-web24.png"))); //NON-NLS public OpenCommVisualizationToolAction() { toolbarButton.addActionListener(actionEvent -> performAction()); setEnabled(false); //disabled by default. Will be enabled in Case.java when a case is opened. + pcl = (PropertyChangeEvent evt) -> { + if (evt.getPropertyName().equals(Case.Events.CURRENT_CASE.toString())) { + setEnabled(RuntimeProperties.runningWithGUI() && evt.getNewValue() != null); + } + }; + Case.addPropertyChangeListener(pcl); + } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java new file mode 100644 index 0000000000..f204507299 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java @@ -0,0 +1,7 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java new file mode 100644 index 0000000000..181e160041 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -0,0 +1,24 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + +import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; + +/** + * + */ +class PinAccountEvent { + + private final AccountDeviceInstanceNode accountDeviceInstance; + + public AccountDeviceInstanceNode getAccountDeviceInstanceNode() { + return accountDeviceInstance; + } + + PinAccountEvent(AccountDeviceInstanceNode accountDeviceInstance) { + this.accountDeviceInstance = accountDeviceInstance; + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 73fa033240..70d70b5325 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -127,7 +127,7 @@ public class RelationshipNode extends BlackboardArtifactNode { * @return The display string, or an empty string if there is no such * attribute or an an error. */ - private static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) { + static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) { try { BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID()))); if (attribute == null) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form new file mode 100644 index 0000000000..fd725cc140 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -0,0 +1,44 @@ + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java new file mode 100644 index 0000000000..fb2bd2eab0 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -0,0 +1,258 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import com.google.common.collect.ImmutableSet; +import com.google.common.eventbus.Subscribe; +import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.model.mxCell; +import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.view.mxGraph; +import java.awt.Color; +import java.util.Collection; +import static java.util.Collections.singleton; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.logging.Level; +import javax.swing.JPanel; +import org.apache.commons.lang3.StringUtils; +import org.openide.explorer.ExplorerManager; +import org.openide.util.Exceptions; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; +import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; +import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * + */ +public class VisualizationPanel extends JPanel { + + private ExplorerManager explorerManager; + private final mxGraph graph; + + private Map nodeMap = new HashMap<>(); + private final mxGraphComponent graphComponent; + + /** + * Creates new form VizPanel + */ + public VisualizationPanel() { + initComponents(); + graph = new mxGraph(); + graph.setCellsEditable(false); + graph.setCellsResizable(false); + graph.setCellsMovable(true); + graph.setCellsDisconnectable(false); + graph.setConnectableEdges(false); + graph.setDisconnectOnMove(false); + graph.setEdgeLabelsMovable(false); + graph.setVertexLabelsMovable(false); + graphComponent = new mxGraphComponent(graph); + graphComponent.setAutoScroll(true); + graphComponent.setOpaque(true); + graphComponent.setBackground(Color.WHITE); + this.add(graphComponent); + + CVTEvents.getCVTEventBus().register(this); + } + + public void initVisualization(ExplorerManager em) { + explorerManager = em; + + graph.getSelectionModel().addListener(null, (sender, evt) -> { + Object[] selectionCells = graph.getSelectionCells(); + if (selectionCells.length == 1) { + mxCell selectionCell = (mxCell) selectionCells[0]; + + if (selectionCell.isVertex()) { + try { + + CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + + AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) selectionCell.getValue(); + explorerManager.setRootContext(new AccountDetailsNode( + singleton(adiKey.getAccountDeviceInstance()), + adiKey.getCommunicationsFilter(), + commsManager)); + } catch (TskCoreException tskCoreException) { + Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, + "Could not get communications manager for current case", tskCoreException); + } + } + } + }); + } + + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { + BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); + if (null != artfType) { + + String from = null; + String[] tos = new String[0]; + + //Consider refactoring this to reduce boilerplate + switch (artfType) { + case TSK_EMAIL_MSG: + from = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"); + tos = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;").split(";"); + break; + case TSK_MESSAGE: + from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); + tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); + break; + case TSK_CALLLOG: + from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); + tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); + break; + default: + break; + } + for (String to : tos) { + if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { + + mxCell fromV = getOrCreateNodeDraft(from, 10); + mxCell toV = getOrCreateNodeDraft(to, 10); + + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); + + if (edgesBetween.length == 0) { + final String edgeName = from + "->" + to; + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + } + } + } + } + } + + @Subscribe + public void pinAccount(PinAccountEvent pinEvent) { + + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); + final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); + + graph.getModel().beginUpdate(); + try { +// final String nodeId = /* +// * adiKey.getDataSourceName() + ":" + +// */ adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + + mxCell v = getOrCreateNodeDraft(adiKey); + CommunicationsManager commsManager = adiNode.getCommsManager(); + + Collection relationshipSources = + commsManager.getRelationshipSources(ImmutableSet.of(adiKey.getAccountDeviceInstance()), adiNode.getFilter()); + + for (Content source : relationshipSources) { + if (source instanceof BlackboardArtifact) { + addEdge((BlackboardArtifact) source); + } + } + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + revalidate(); + } + + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + } + + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { + final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); + final String name =// accountDeviceInstance.getDeviceId() + ":" + + accountDeviceInstance.getAccount().getTypeSpecificID(); + mxCell nodeDraft = nodeMap.get(name); + if (nodeDraft == null) { + double size = accountDeviceInstanceKey.getMessageCount() / 10; + nodeDraft = (mxCell) graph.insertVertex( + graph.getDefaultParent(), + name, accountDeviceInstanceKey, + new Random().nextInt(200), + new Random().nextInt(200), + size, + size); + nodeMap.put(name, nodeDraft); + } + return nodeDraft; + } + + private mxCell getOrCreateNodeDraft(String name, long size) { + mxCell nodeDraft = nodeMap.get(name); + if (nodeDraft == null) { + nodeDraft = (mxCell) graph.insertVertex( + graph.getDefaultParent(), + name, + null, + new Random().nextInt(200), + new Random().nextInt(200), + size, + size); +// nodeDraft.setLabel(name); + nodeMap.put(name, nodeDraft); + } + return nodeDraft; + } + + /** + * 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") + // //GEN-BEGIN:initComponents + private void initComponents() { + + jToolBar1 = new javax.swing.JToolBar(); + jButton1 = new javax.swing.JButton(); + + setLayout(new java.awt.BorderLayout()); + + jToolBar1.setRollover(true); + + jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton1.setFocusable(false); + jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jToolBar1.add(jButton1); + + add(jToolBar1, java.awt.BorderLayout.PAGE_START); + }// //GEN-END:initComponents + + + // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JButton jButton1; + private javax.swing.JToolBar jToolBar1; + // End of variables declaration//GEN-END:variables +} From c507a033b52791af7dcce035913d8e2a8df5e5ac Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 4 Jan 2018 23:49:58 +0100 Subject: [PATCH 002/347] build out jgraphx based visualization. --- .../AccountDeviceInstanceKey.java | 40 +++++ .../communications/AccountsRootChildren.java | 29 ++- .../autopsy/communications/Bundle.properties | 3 +- .../communications/MessageBrowser.java | 41 +++-- .../communications/VisualizationPanel.form | 16 ++ .../communications/VisualizationPanel.java | 169 +++++++++++++----- 6 files changed, 228 insertions(+), 70 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index fbc2c17feb..343db541e2 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -18,6 +18,7 @@ */ package org.sleuthkit.autopsy.communications; +import java.util.Objects; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; @@ -57,4 +58,43 @@ class AccountDeviceInstanceKey { String getDataSourceName() { return dataSourceName; } + + @Override + public String toString() { + return accountDeviceInstance.getAccount().getTypeSpecificID(); + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + Objects.hashCode(this.accountDeviceInstance); + hash = 37 * hash + (int) (this.messageCount ^ (this.messageCount >>> 32)); + hash = 37 * hash + Objects.hashCode(this.dataSourceName); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AccountDeviceInstanceKey other = (AccountDeviceInstanceKey) obj; + if (this.messageCount != other.messageCount) { + return false; + } + if (!Objects.equals(this.dataSourceName, other.dataSourceName)) { + return false; + } + if (!Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance)) { + return false; + } + return true; + } + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index 881b14b67d..b0d59bd183 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -22,6 +22,7 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -78,7 +79,7 @@ class AccountsRootChildren extends ChildFactory { return new AccountDeviceInstanceNode(key, commsManager); } - private String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { + static String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { try { final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); for (DataSource dataSource : sleuthkitCase.getDataSources()) { @@ -153,6 +154,32 @@ class AccountsRootChildren extends ChildFactory { return s; } + @Override + public int hashCode() { + int hash = 3; + hash = 73 * hash + Objects.hashCode(this.accountDeviceInstanceKey); + return hash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AccountDeviceInstanceNode other = (AccountDeviceInstanceNode) obj; + if (!Objects.equals(this.accountDeviceInstanceKey, other.accountDeviceInstanceKey)) { + return false; + } + + return true; + } + @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 5e7d93adf0..6f78ddf19b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,5 +15,6 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=jButton1 +VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize +VisualizationPanel.jButton2.text=pan diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index e7628e71f9..e58a1f027b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -77,27 +77,30 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager //reset panel when there is no selection messagesResultPanel.setPath(""); } else { - AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; - CommunicationsFilter filter = adiNode.getFilter(); - CommunicationsManager commsManager = adiNode.getCommsManager(); - final Set accountDeviceInstances; + final Node selectedNode = selectedNodes[0]; + if (selectedNode instanceof AccountDeviceInstanceNode) { + AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode; + CommunicationsFilter filter = adiNode.getFilter(); + CommunicationsManager commsManager = adiNode.getCommsManager(); + final Set accountDeviceInstances; - if (selectedNodes.length == 1) { - final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); - accountDeviceInstances = Collections.singleton(accountDeviceInstance); - messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); - } else { - accountDeviceInstances = Stream.of(selectedNodes) - .map(node -> (AccountDeviceInstanceNode) node) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - messagesResultPanel.setPath(selectedNodes.length + " accounts"); + if (selectedNodes.length == 1) { + final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); + accountDeviceInstances = Collections.singleton(accountDeviceInstance); + messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); + } else { + accountDeviceInstances = Stream.of(selectedNodes) + .map(node -> (AccountDeviceInstanceNode) node) + .map(AccountDeviceInstanceNode::getAccountDeviceInstance) + .collect(Collectors.toSet()); + messagesResultPanel.setPath(selectedNodes.length + " accounts"); + } + AccountDetailsNode accountDetailsNode = + new AccountDetailsNode(accountDeviceInstances, filter, commsManager); + TableFilterNode wrappedNode = + new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); + messagesResultPanel.setNode(wrappedNode); } - AccountDetailsNode accountDetailsNode - = new AccountDetailsNode(accountDeviceInstances, filter, commsManager); - TableFilterNode wrappedNode - = new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); - messagesResultPanel.setNode(wrappedNode); } } }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index fd725cc140..3c45fd7568 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -37,6 +37,22 @@ + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index fb2bd2eab0..1a860c0671 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,22 +18,28 @@ */ package org.sleuthkit.autopsy.communications; -import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; +import com.mxgraph.swing.handler.mxPanningHandler; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxStylesheet; import java.awt.Color; -import java.util.Collection; -import static java.util.Collections.singleton; +import java.awt.event.MouseWheelEvent; +import java.awt.event.MouseWheelListener; +import java.beans.PropertyVetoException; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Random; import java.util.logging.Level; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; +import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; @@ -45,20 +51,26 @@ import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMA import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; +import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; -import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** * */ public class VisualizationPanel extends JPanel { - + + static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; private final mxGraph graph; - - private Map nodeMap = new HashMap<>(); + + private final Map nodeMap = new HashMap<>(); private final mxGraphComponent graphComponent; + + static { + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + } /** * Creates new form VizPanel @@ -79,41 +91,67 @@ public class VisualizationPanel extends JPanel { graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); this.add(graphComponent); - + graphComponent.addMouseWheelListener(new MouseWheelListener() { + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); + } + }); + CVTEvents.getCVTEventBus().register(this); + + graph.setStylesheet(mxStylesheet); + } - + public void initVisualization(ExplorerManager em) { explorerManager = em; - + graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { mxCell selectionCell = (mxCell) selectionCells[0]; - - if (selectionCell.isVertex()) { - try { - - CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - - AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) selectionCell.getValue(); - explorerManager.setRootContext(new AccountDetailsNode( - singleton(adiKey.getAccountDeviceInstance()), - adiKey.getCommunicationsFilter(), - commsManager)); - } catch (TskCoreException tskCoreException) { - Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, - "Could not get communications manager for current case", tskCoreException); + try { + CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + + if (selectionCell.isVertex()) { + final AccountDeviceInstanceNode accountDeviceInstanceNode = + new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), + commsManager); + explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + + } else if (selectionCell.isEdge()) { + System.out.println(selectionCell.getId()); +// explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); } + } catch (TskCoreException tskCoreException) { + Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, + "Could not get communications manager for current case", tskCoreException); + } catch (PropertyVetoException ex) { + Exceptions.printStackTrace(ex); } } }); } - + + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { + + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + + if (edgesBetween.length == 0) { + final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + } + } + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { - + String from = null; String[] tos = new String[0]; @@ -136,12 +174,12 @@ public class VisualizationPanel extends JPanel { } for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - + mxCell fromV = getOrCreateNodeDraft(from, 10); mxCell toV = getOrCreateNodeDraft(to, 10); - + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - + if (edgesBetween.length == 0) { final String edgeName = from + "->" + to; mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); @@ -154,29 +192,31 @@ public class VisualizationPanel extends JPanel { } } } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { - + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); - + graph.getModel().beginUpdate(); try { -// final String nodeId = /* -// * adiKey.getDataSourceName() + ":" + -// */ adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); - - mxCell v = getOrCreateNodeDraft(adiKey); + nodeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + + mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); - - Collection relationshipSources = - commsManager.getRelationshipSources(ImmutableSet.of(adiKey.getAccountDeviceInstance()), adiNode.getFilter()); - - for (Content source : relationshipSources) { - if (source instanceof BlackboardArtifact) { - addEdge((BlackboardArtifact) source); - } + final CommunicationsFilter commsFilter = adiNode.getFilter(); + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter); + + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); + String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI); + AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName); + mxCell relatedAccountVertex = getOrCreateNodeDraft(relatedADIKey); + + addEdge(pinnedAccountVertex, relatedAccountVertex); } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); @@ -188,7 +228,7 @@ public class VisualizationPanel extends JPanel { new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -203,23 +243,24 @@ public class VisualizationPanel extends JPanel { new Random().nextInt(200), size, size); + graph.getView().getState(nodeDraft, true).setLabel(name); nodeMap.put(name, nodeDraft); } return nodeDraft; } - + private mxCell getOrCreateNodeDraft(String name, long size) { mxCell nodeDraft = nodeMap.get(name); if (nodeDraft == null) { nodeDraft = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, - null, + name, new Random().nextInt(200), new Random().nextInt(200), size, size); -// nodeDraft.setLabel(name); + graph.getView().getState(nodeDraft, true).setLabel(name); nodeMap.put(name, nodeDraft); } return nodeDraft; @@ -236,6 +277,7 @@ public class VisualizationPanel extends JPanel { jToolBar1 = new javax.swing.JToolBar(); jButton1 = new javax.swing.JButton(); + jButton2 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); @@ -245,14 +287,43 @@ public class VisualizationPanel extends JPanel { jButton1.setFocusable(false); jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); jToolBar1.add(jButton1); + jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N + jButton2.setFocusable(false); + jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton2.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2ActionPerformed(evt); + } + }); + jToolBar1.add(jButton2); + add(jToolBar1, java.awt.BorderLayout.PAGE_START); }// //GEN-END:initComponents + private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); + + }//GEN-LAST:event_jButton2ActionPerformed + + private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + new mxOrganicLayout(graph).execute(graph.getDefaultParent()); +// new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + }//GEN-LAST:event_jButton1ActionPerformed + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; + private javax.swing.JButton jButton2; private javax.swing.JToolBar jToolBar1; // End of variables declaration//GEN-END:variables + } From 79437b9bacb8360891fb703d5acf0e8378f65a91 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 5 Jan 2018 12:46:09 +0100 Subject: [PATCH 003/347] WIP getting graph selection to populate MessageBrowser WIP --- .../communications/CVTTopComponent.java | 20 +++-- .../communications/MessageBrowser.java | 29 ++++--- .../communications/VisualizationPanel.java | 78 +++++++++++-------- 3 files changed, 77 insertions(+), 50 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index f19e998fb0..9e54b5d87f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -38,15 +38,16 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; - + @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); setName(Bundle.CVTTopComponent_name()); + /* * Associate an explorer manager with the GlobalActionContext (GAC) for * use by the messages browser so that selections in the messages @@ -64,11 +65,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); - - vizPanel.initVisualization(acctsBrowserExplorerManager); + + vizPanel.initVisualization(acctsBrowserExplorerManager, messagesBrowserExplorerManager); + + CVTEvents.getCVTEventBus().register(this); + } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); @@ -135,12 +139,12 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag super.componentOpened(); WindowManager.getDefault().setTopComponentFloating(this, true); } - + @Override public ExplorerManager getExplorerManager() { return acctsBrowserExplorerManager; } - + @Override public void open() { super.open(); @@ -152,7 +156,7 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag */ filtersPane.updateAndApplyFilters(); } - + @Override public List availableModes(List modes) { /* diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index e58a1f027b..a97364de65 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.CommunicationsManager; * other account details, and a ContentViewer to show individual */ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { - + private static final long serialVersionUID = 1L; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; @@ -61,18 +61,27 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); } - + @Override public void addNotify() { super.addNotify(); - ExplorerManager parentExplorerManager = ExplorerManager.find(this); - parentExplorerManager.addPropertyChangeListener(pce -> { + ExplorerManager parentEm = ExplorerManager.find(this); +// parentEm.addPropertyChangeListener(pce -> { +// gacExplorerManager.setRootContext(parentEm.getRootContext()); +// try { +// gacExplorerManager.setSelectedNodes(parentEm.getSelectedNodes()); +// } catch (PropertyVetoException ex) { +// Exceptions.printStackTrace(ex); +// } +// }); + + parentEm.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = parentExplorerManager.getSelectedNodes(); - + final Node[] selectedNodes = parentEm.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); - + if (selectedNodes.length == 0) { //reset panel when there is no selection messagesResultPanel.setPath(""); @@ -83,7 +92,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager CommunicationsFilter filter = adiNode.getFilter(); CommunicationsManager commsManager = adiNode.getCommsManager(); final Set accountDeviceInstances; - + if (selectedNodes.length == 1) { final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); accountDeviceInstances = Collections.singleton(accountDeviceInstance); @@ -98,7 +107,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager AccountDetailsNode accountDetailsNode = new AccountDetailsNode(accountDeviceInstances, filter, commsManager); TableFilterNode wrappedNode = - new TableFilterNode(new DataResultFilterNode(accountDetailsNode, parentExplorerManager), true); + new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); messagesResultPanel.setNode(wrappedNode); } } @@ -112,7 +121,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager } messagesResultPanel.open(); } - + @Override public ExplorerManager getExplorerManager() { return gacExplorerManager; diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 1a860c0671..a2ec62f8ff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,7 +22,6 @@ import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; -import com.mxgraph.swing.handler.mxPanningHandler; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; @@ -39,6 +38,8 @@ import java.util.logging.Level; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; @@ -59,14 +60,14 @@ import org.sleuthkit.datamodel.TskCoreException; * */ public class VisualizationPanel extends JPanel { - + static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; private final mxGraph graph; - + private final Map nodeMap = new HashMap<>(); private final mxGraphComponent graphComponent; - + static { mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); @@ -97,29 +98,30 @@ public class VisualizationPanel extends JPanel { graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); } }); - + CVTEvents.getCVTEventBus().register(this); - + graph.setStylesheet(mxStylesheet); - + } - - public void initVisualization(ExplorerManager em) { + + public void initVisualization(ExplorerManager em, ExplorerManager messageBrowserManager) { explorerManager = em; - + graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { mxCell selectionCell = (mxCell) selectionCells[0]; try { CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - + if (selectionCell.isVertex()) { final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); - + messageBrowserManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + messageBrowserManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); // explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); @@ -133,11 +135,11 @@ public class VisualizationPanel extends JPanel { } }); } - + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - + if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); @@ -147,11 +149,11 @@ public class VisualizationPanel extends JPanel { edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); } } - + private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { - + String from = null; String[] tos = new String[0]; @@ -174,12 +176,12 @@ public class VisualizationPanel extends JPanel { } for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - + mxCell fromV = getOrCreateNodeDraft(from, 10); mxCell toV = getOrCreateNodeDraft(to, 10); - + Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - + if (edgesBetween.length == 0) { final String edgeName = from + "->" + to; mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); @@ -192,30 +194,30 @@ public class VisualizationPanel extends JPanel { } } } - + @Subscribe public void pinAccount(PinAccountEvent pinEvent) { - + final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); - + graph.getModel().beginUpdate(); try { nodeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - + mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); final CommunicationsFilter commsFilter = adiNode.getFilter(); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter); - + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI); AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName); mxCell relatedAccountVertex = getOrCreateNodeDraft(relatedADIKey); - + addEdge(pinnedAccountVertex, relatedAccountVertex); } } catch (TskCoreException ex) { @@ -225,10 +227,10 @@ public class VisualizationPanel extends JPanel { graph.getModel().endUpdate(); revalidate(); } - + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - + private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -248,7 +250,7 @@ public class VisualizationPanel extends JPanel { } return nodeDraft; } - + private mxCell getOrCreateNodeDraft(String name, long size) { mxCell nodeDraft = nodeMap.get(name); if (nodeDraft == null) { @@ -309,8 +311,8 @@ public class VisualizationPanel extends JPanel { }// //GEN-END:initComponents private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed - graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - +// graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); +graphComponent.getPanningHandler().setEnabled(true); }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed @@ -326,4 +328,16 @@ public class VisualizationPanel extends JPanel { private javax.swing.JToolBar jToolBar1; // End of variables declaration//GEN-END:variables + static class SimpleParentNode extends AbstractNode { + + static SimpleParentNode createFromChildNodes(Node... nodes) { + Children.Array array = new Children.Array(); + array.add(nodes); + return new SimpleParentNode(array); + } + + public SimpleParentNode(Children children) { + super(children); + } + } } From 3e49de2c1b9d85f85ef4593813a602a9ac52b42c Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 5 Jan 2018 16:00:15 +0100 Subject: [PATCH 004/347] WIP 2 getting graph selection to populate MessageBrowser WIP 2 --- .../communications/AccountsBrowser.java | 19 +++-- .../autopsy/communications/Bundle.properties | 2 + .../communications/CVTTopComponent.form | 84 ++++++++++--------- .../communications/CVTTopComponent.java | 68 ++++++++++----- .../communications/VisualizationPanel.java | 63 +++++++------- 5 files changed, 137 insertions(+), 99 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 8e418d65fb..c90572bff8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -31,7 +31,7 @@ import org.openide.explorer.ExplorerManager; * A panel that goes in the Browse tab of the Communications Visualization Tool. * Hosts an OutlineView that shows information about Accounts. */ -public class AccountsBrowser extends JPanel { +public class AccountsBrowser extends JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; @@ -56,10 +56,8 @@ public class AccountsBrowser extends JPanel { outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded } - @Override - public void addNotify() { - super.addNotify(); - em = ExplorerManager.find(this); + void init(ExplorerManager acctsBrowserExplorerManager) { + em = acctsBrowserExplorerManager; em.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); @@ -67,8 +65,8 @@ public class AccountsBrowser extends JPanel { SwingUtilities.invokeLater(this::setColumnWidths); } }); - } - + + } private void setColumnWidths() { int margin = 4; int padding = 8; @@ -126,4 +124,11 @@ public class AccountsBrowser extends JPanel { // Variables declaration - do not modify//GEN-BEGIN:variables private org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables + + @Override + public ExplorerManager getExplorerManager() { + return em; + } + + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 6f78ddf19b..d4740c7644 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -18,3 +18,5 @@ FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan +CVTTopComponent.browseSplitPane.TabConstraints.tabTitle=Browse +CVTTopComponent.vizSplitPane.TabConstraints.tabTitle=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index a24037b927..5a2d718964 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -19,9 +19,9 @@ - - - + + + @@ -29,63 +29,71 @@ - - - - + + + + +
- + - - + + + - + - - - - - - + - - + + + + + + + + + - + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 9e54b5d87f..d395f9d1ab 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,9 +21,12 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; +import javax.swing.JSplitPane; import org.openide.explorer.ExplorerManager; -import org.openide.explorer.ExplorerUtils; +import static org.openide.explorer.ExplorerUtils.createLookup; +import org.openide.util.Lookup; import org.openide.util.NbBundle; +import org.openide.util.lookup.ProxyLookup; import org.openide.windows.Mode; import org.openide.windows.RetainLocation; import org.openide.windows.TopComponent; @@ -40,23 +43,25 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; - private final ExplorerManager messagesBrowserExplorerManager; private final ExplorerManager acctsBrowserExplorerManager; @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); + setName(Bundle.CVTTopComponent_name()); /* * Associate an explorer manager with the GlobalActionContext (GAC) for - * use by the messages browser so that selections in the messages + * use by the messages browsers so that selections in the messages * browser can be exposed to context-sensitive actions. */ - messagesBrowserExplorerManager = new ExplorerManager(); - associateLookup(ExplorerUtils.createLookup(messagesBrowserExplorerManager, getActionMap())); - splitPane.setRightComponent(new MessageBrowser(messagesBrowserExplorerManager)); - + ExplorerManager browserExplorerManager = new ExplorerManager(); + ExplorerManager vizExplorerManager = new ExplorerManager(); + ProxyLookupImpl proxyLookup = new ProxyLookupImpl(); + associateLookup(proxyLookup); + browseSplitPane.setRightComponent(new MessageBrowser(browserExplorerManager)); + vizSplitPane.setRightComponent(new MessageBrowser(vizExplorerManager)); /* * Create a second explorer manager to be discovered by the accounts * browser and the message browser so that the browsers can both listen @@ -65,9 +70,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * Nodes from the accounts browser to the messages browser. */ acctsBrowserExplorerManager = new ExplorerManager(); + accountsBrowser.init(acctsBrowserExplorerManager); + vizPanel.initVisualization(acctsBrowserExplorerManager); - vizPanel.initVisualization(acctsBrowserExplorerManager, messagesBrowserExplorerManager); - + browseVisualizeTabPane.addChangeListener(changeEvent -> { + JSplitPane selectedComponent = (JSplitPane) browseVisualizeTabPane.getSelectedComponent(); + ExplorerManager.Provider leftComponent = (ExplorerManager.Provider) selectedComponent.getLeftComponent(); + proxyLookup.changeLookup(createLookup(leftComponent.getExplorerManager(), getActionMap())); + }); CVTEvents.getCVTEventBus().register(this); @@ -86,20 +96,22 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag // //GEN-BEGIN:initComponents private void initComponents() { - splitPane = new javax.swing.JSplitPane(); browseVisualizeTabPane = new javax.swing.JTabbedPane(); + browseSplitPane = new javax.swing.JSplitPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); + vizSplitPane = new javax.swing.JSplitPane(); vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); - splitPane.setDividerLocation(400); - splitPane.setResizeWeight(0.7); - browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N - splitPane.setLeftComponent(browseVisualizeTabPane); + browseSplitPane.setLeftComponent(accountsBrowser); + + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), browseSplitPane); // NOI18N + + vizSplitPane.setLeftComponent(vizPanel); + + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizSplitPane); // NOI18N filtersPane.setMinimumSize(new java.awt.Dimension(256, 495)); @@ -111,27 +123,29 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag .addGap(6, 6, 6) .addComponent(filtersPane, javax.swing.GroupLayout.PREFERRED_SIZE, 265, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) - .addComponent(splitPane, javax.swing.GroupLayout.DEFAULT_SIZE, 1324, Short.MAX_VALUE) - .addGap(0, 0, 0)) + .addComponent(browseVisualizeTabPane, javax.swing.GroupLayout.DEFAULT_SIZE, 786, Short.MAX_VALUE) + .addContainerGap()) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(6, 6, 6) - .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(splitPane)) + .addComponent(filtersPane, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addGap(5, 5, 5)) + .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() + .addComponent(browseVisualizeTabPane) + .addContainerGap()) ); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser; + private javax.swing.JSplitPane browseSplitPane; private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; - private javax.swing.JSplitPane splitPane; private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; + private javax.swing.JSplitPane vizSplitPane; // End of variables declaration//GEN-END:variables @Override @@ -167,4 +181,14 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag return modes.stream().filter(mode -> mode.getName().equals("cvt")) .collect(Collectors.toList()); } + + private static class ProxyLookupImpl extends ProxyLookup { + + public ProxyLookupImpl() { + } + + void changeLookup(Lookup l){ + setLookups(l); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index a2ec62f8ff..15e71dc88f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -27,8 +27,6 @@ import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.Color; -import java.awt.event.MouseWheelEvent; -import java.awt.event.MouseWheelListener; import java.beans.PropertyVetoException; import java.util.HashMap; import java.util.List; @@ -59,7 +57,7 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel { +public class VisualizationPanel extends JPanel implements ExplorerManager.Provider { static final private mxStylesheet mxStylesheet = new mxStylesheet(); private ExplorerManager explorerManager; @@ -92,12 +90,6 @@ public class VisualizationPanel extends JPanel { graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); this.add(graphComponent); - graphComponent.addMouseWheelListener(new MouseWheelListener() { - @Override - public void mouseWheelMoved(MouseWheelEvent e) { - graphComponent.zoomTo(graphComponent.getZoomFactor() + e.getPreciseWheelRotation(), true); - } - }); CVTEvents.getCVTEventBus().register(this); @@ -105,7 +97,7 @@ public class VisualizationPanel extends JPanel { } - public void initVisualization(ExplorerManager em, ExplorerManager messageBrowserManager) { + public void initVisualization(ExplorerManager em) { explorerManager = em; graph.getSelectionModel().addListener(null, (sender, evt) -> { @@ -119,8 +111,8 @@ public class VisualizationPanel extends JPanel { final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - messageBrowserManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - messageBrowserManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + explorerManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); @@ -136,6 +128,11 @@ public class VisualizationPanel extends JPanel { }); } + @Override + public ExplorerManager getExplorerManager() { + return explorerManager; + } + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); @@ -150,6 +147,7 @@ public class VisualizationPanel extends JPanel { } } + @Deprecated private void addEdge(BlackboardArtifact artifact) throws TskCoreException { BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); if (null != artfType) { @@ -177,8 +175,8 @@ public class VisualizationPanel extends JPanel { for (String to : tos) { if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - mxCell fromV = getOrCreateNodeDraft(from, 10); - mxCell toV = getOrCreateNodeDraft(to, 10); + mxCell fromV = getOrCreateVertex(from, 10); + mxCell toV = getOrCreateVertex(to, 10); Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); @@ -206,7 +204,7 @@ public class VisualizationPanel extends JPanel { nodeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - mxCell pinnedAccountVertex = getOrCreateNodeDraft(adiKey); + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); CommunicationsManager commsManager = adiNode.getCommsManager(); final CommunicationsFilter commsFilter = adiNode.getFilter(); List relatedAccountDeviceInstances = @@ -216,7 +214,7 @@ public class VisualizationPanel extends JPanel { long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI); AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName); - mxCell relatedAccountVertex = getOrCreateNodeDraft(relatedADIKey); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); addEdge(pinnedAccountVertex, relatedAccountVertex); } @@ -231,30 +229,31 @@ public class VisualizationPanel extends JPanel { new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); } - private mxCell getOrCreateNodeDraft(AccountDeviceInstanceKey accountDeviceInstanceKey) { + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - mxCell nodeDraft = nodeMap.get(name); - if (nodeDraft == null) { - double size = accountDeviceInstanceKey.getMessageCount() / 10; - nodeDraft = (mxCell) graph.insertVertex( + mxCell vertex = nodeMap.get(name); + if (vertex == null) { + double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; + vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, accountDeviceInstanceKey, new Random().nextInt(200), new Random().nextInt(200), size, size); - graph.getView().getState(nodeDraft, true).setLabel(name); - nodeMap.put(name, nodeDraft); + graph.getView().getState(vertex, true).setLabel(name); + nodeMap.put(name, vertex); } - return nodeDraft; + return vertex; } - private mxCell getOrCreateNodeDraft(String name, long size) { - mxCell nodeDraft = nodeMap.get(name); - if (nodeDraft == null) { - nodeDraft = (mxCell) graph.insertVertex( + @Deprecated + private mxCell getOrCreateVertex(String name, long size) { + mxCell vertex = nodeMap.get(name); + if (vertex == null) { + vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), name, name, @@ -262,10 +261,10 @@ public class VisualizationPanel extends JPanel { new Random().nextInt(200), size, size); - graph.getView().getState(nodeDraft, true).setLabel(name); - nodeMap.put(name, nodeDraft); + graph.getView().getState(vertex, true).setLabel(name); + nodeMap.put(name, vertex); } - return nodeDraft; + return vertex; } /** @@ -312,7 +311,7 @@ public class VisualizationPanel extends JPanel { private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed // graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); -graphComponent.getPanningHandler().setEnabled(true); + graphComponent.getPanningHandler().setEnabled(true); }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed From 461fc11c130cabe83b3d03a639d973698c2041d8 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 6 Jan 2018 13:39:13 +0100 Subject: [PATCH 005/347] WIP 3 getting graph selection to populate MessageBrowser WIP 3 --- .../communications/AccountsBrowser.form | 40 ++++----- .../communications/AccountsBrowser.java | 48 +++++------ .../autopsy/communications/Bundle.properties | 5 +- .../communications/CVTTopComponent.form | 39 +++------ .../communications/CVTTopComponent.java | 72 +++++++--------- .../autopsy/communications/FiltersPanel.java | 18 ++-- .../communications/MessageBrowser.java | 30 +++---- .../communications/VisualizationPanel.form | 86 ++++++++++++------- .../communications/VisualizationPanel.java | 76 +++++++++------- 9 files changed, 209 insertions(+), 205 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form index 83a3e75e9a..96ba675284 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form @@ -11,30 +11,28 @@ + - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index c90572bff8..25275d5e36 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -31,12 +31,13 @@ import org.openide.explorer.ExplorerManager; * A panel that goes in the Browse tab of the Communications Visualization Tool. * Hosts an OutlineView that shows information about Accounts. */ -public class AccountsBrowser extends JPanel implements ExplorerManager.Provider { +public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, CVTTopComponent.ProxiedExplorerManagerProvider { private static final long serialVersionUID = 1L; private final Outline outline; - private ExplorerManager em; + private final ExplorerManager gacEM = new ExplorerManager(); + private ExplorerManager tableEM; /** * Creates new form AccountsBrowser @@ -54,19 +55,22 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider ((DefaultOutlineModel) outline.getOutlineModel()).setNodesColumnLabel(Bundle.AccountNode_accountName()); outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded + } - void init(ExplorerManager acctsBrowserExplorerManager) { - em = acctsBrowserExplorerManager; - em.addPropertyChangeListener(evt -> { + void init(ExplorerManager tableExplorerManager) { + this.tableEM = tableExplorerManager; + tableExplorerManager.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } }); - - } + + jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, gacEM)); + } + private void setColumnWidths() { int margin = 4; int padding = 8; @@ -100,35 +104,29 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider // //GEN-BEGIN:initComponents private void initComponents() { + jSplitPane1 = new javax.swing.JSplitPane(); outlineView = new org.openide.explorer.view.OutlineView(); - javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); - this.setLayout(layout); - layout.setHorizontalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE) - .addGap(0, 0, 0)) - ); - layout.setVerticalGroup( - layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() - .addGap(0, 0, 0) - .addComponent(outlineView, javax.swing.GroupLayout.DEFAULT_SIZE, 300, Short.MAX_VALUE) - .addGap(0, 0, 0)) - ); + setLayout(new java.awt.BorderLayout()); + + jSplitPane1.setLeftComponent(outlineView); + + add(jSplitPane1, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables + private javax.swing.JSplitPane jSplitPane1; private org.openide.explorer.view.OutlineView outlineView; // End of variables declaration//GEN-END:variables @Override public ExplorerManager getExplorerManager() { - return em; + return tableEM; } - + @Override + public ExplorerManager getProxiedExplorerManager() { + return gacEM; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index d4740c7644..d0fa082fd9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -18,5 +18,6 @@ FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh VisualizationPanel.jButton1.text=redo layout CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan -CVTTopComponent.browseSplitPane.TabConstraints.tabTitle=Browse -CVTTopComponent.vizSplitPane.TabConstraints.tabTitle=Visualize +CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse +CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize +CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form index 5a2d718964..9315465965 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.form @@ -46,15 +46,20 @@ + + + + + - + - + @@ -62,24 +67,13 @@ - - - - - - - - - - - - - + + - + @@ -87,18 +81,7 @@ - - - - - - - - - - - - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index d395f9d1ab..fd6019c0d0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; -import javax.swing.JSplitPane; import org.openide.explorer.ExplorerManager; import static org.openide.explorer.ExplorerUtils.createLookup; import org.openide.util.Lookup; @@ -40,15 +39,16 @@ import org.sleuthkit.autopsy.coreutils.ThreadConfined; @TopComponent.Registration(mode = "cvt", openAtStartup = false) @RetainLocation("cvt") @NbBundle.Messages("CVTTopComponent.name= Communications Visualization") -public final class CVTTopComponent extends TopComponent implements ExplorerManager.Provider { +public final class CVTTopComponent extends TopComponent { private static final long serialVersionUID = 1L; - private final ExplorerManager acctsBrowserExplorerManager; + + private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager(); @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); - + filtersPane.setExplorerManager(filterToTableEXplorerManager); setName(Bundle.CVTTopComponent_name()); /* @@ -56,27 +56,25 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag * use by the messages browsers so that selections in the messages * browser can be exposed to context-sensitive actions. */ - ExplorerManager browserExplorerManager = new ExplorerManager(); - ExplorerManager vizExplorerManager = new ExplorerManager(); +// ExplorerManager browserExplorerManager = new ExplorerManager(); +// ExplorerManager vizExplorerManager = new ExplorerManager(); ProxyLookupImpl proxyLookup = new ProxyLookupImpl(); associateLookup(proxyLookup); - browseSplitPane.setRightComponent(new MessageBrowser(browserExplorerManager)); - vizSplitPane.setRightComponent(new MessageBrowser(vizExplorerManager)); - /* - * Create a second explorer manager to be discovered by the accounts - * browser and the message browser so that the browsers can both listen - * for explorer manager property events for the outline view of the - * accounts browser. This provides a mechanism for pushing selected - * Nodes from the accounts browser to the messages browser. - */ - acctsBrowserExplorerManager = new ExplorerManager(); - accountsBrowser.init(acctsBrowserExplorerManager); - vizPanel.initVisualization(acctsBrowserExplorerManager); + accountsBrowser.init(filterToTableEXplorerManager); +// browseSplitPane.setRightComponent(new MessageBrowser(browserExplorerManager)); +// vizSplitPane.setRightComponent(new MessageBrowser(vizExplorerManager)); +// /* +// * Create a second explorer manager to be discovered by the accounts +// * browser and the message browser so that the browsers can both listen +// * for explorer manager property events for the outline view of the +// * accounts browser. This provides a mechanism for pushing selected +// * Nodes from the accounts browser to the messages browser. +// */ +// acctsBrowserExplorerManager = new ExplorerManager(); browseVisualizeTabPane.addChangeListener(changeEvent -> { - JSplitPane selectedComponent = (JSplitPane) browseVisualizeTabPane.getSelectedComponent(); - ExplorerManager.Provider leftComponent = (ExplorerManager.Provider) selectedComponent.getLeftComponent(); - proxyLookup.changeLookup(createLookup(leftComponent.getExplorerManager(), getActionMap())); + ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); + proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap())); }); CVTEvents.getCVTEventBus().register(this); @@ -97,21 +95,13 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private void initComponents() { browseVisualizeTabPane = new javax.swing.JTabbedPane(); - browseSplitPane = new javax.swing.JSplitPane(); accountsBrowser = new org.sleuthkit.autopsy.communications.AccountsBrowser(); - vizSplitPane = new javax.swing.JSplitPane(); vizPanel = new org.sleuthkit.autopsy.communications.VisualizationPanel(); filtersPane = new org.sleuthkit.autopsy.communications.FiltersPanel(); browseVisualizeTabPane.setFont(new java.awt.Font("Tahoma", 0, 18)); // NOI18N - - browseSplitPane.setLeftComponent(accountsBrowser); - - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), browseSplitPane); // NOI18N - - vizSplitPane.setLeftComponent(vizPanel); - - browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizSplitPane.TabConstraints.tabTitle"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizSplitPane); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/table.png")), accountsBrowser); // NOI18N + browseVisualizeTabPane.addTab(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.vizPanel.TabConstraints.tabTitle_1"), new javax.swing.ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/emblem-web.png")), vizPanel); // NOI18N filtersPane.setMinimumSize(new java.awt.Dimension(256, 495)); @@ -136,16 +126,16 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag .addComponent(browseVisualizeTabPane) .addContainerGap()) ); + + browseVisualizeTabPane.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(CVTTopComponent.class, "CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName")); // NOI18N }// //GEN-END:initComponents // Variables declaration - do not modify//GEN-BEGIN:variables private org.sleuthkit.autopsy.communications.AccountsBrowser accountsBrowser; - private javax.swing.JSplitPane browseSplitPane; private javax.swing.JTabbedPane browseVisualizeTabPane; private org.sleuthkit.autopsy.communications.FiltersPanel filtersPane; private org.sleuthkit.autopsy.communications.VisualizationPanel vizPanel; - private javax.swing.JSplitPane vizSplitPane; // End of variables declaration//GEN-END:variables @Override @@ -154,11 +144,6 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag WindowManager.getDefault().setTopComponentFloating(this, true); } - @Override - public ExplorerManager getExplorerManager() { - return acctsBrowserExplorerManager; - } - @Override public void open() { super.open(); @@ -184,11 +169,16 @@ public final class CVTTopComponent extends TopComponent implements ExplorerManag private static class ProxyLookupImpl extends ProxyLookup { - public ProxyLookupImpl() { + ProxyLookupImpl() { } - - void changeLookup(Lookup l){ + + void changeLookup(Lookup l) { setLookups(l); } } + + static interface ProxiedExplorerManagerProvider { + + ExplorerManager getProxiedExplorerManager(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 84db631a86..9fc83ca1b5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -30,6 +30,7 @@ import java.util.Map.Entry; import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; +import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; @@ -59,7 +60,7 @@ import org.sleuthkit.datamodel.TskCoreException; * Panel that holds the Filter control widgets and translates user filtering * changes into queries against the CommunicationsManager. */ -final public class FiltersPanel extends javax.swing.JPanel { +final public class FiltersPanel extends JPanel { private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); private static final long serialVersionUID = 1L; @@ -119,9 +120,9 @@ final public class FiltersPanel extends javax.swing.JPanel { if (eventType.equals(DATA_ADDED.toString())) { // Indicate that a refresh may be needed, unless the data added is Keyword or Hashset hits ModuleDataEvent eventData = (ModuleDataEvent) pce.getOldValue(); - if (null != eventData && - eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() && - eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { + if (null != eventData + && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_KEYWORD_HIT.getTypeID() + && eventData.getBlackboardArtifactType().getTypeID() != BlackboardArtifact.ARTIFACT_TYPE.TSK_HASHSET_HIT.getTypeID()) { updateFilters(); needsRefresh = true; validateFilters(); @@ -173,11 +174,6 @@ final public class FiltersPanel extends javax.swing.JPanel { @Override public void addNotify() { super.addNotify(); - /* - * Since we get the exploreremanager from the parent JComponenet, wait - * till this FiltersPanel is actaully added to a parent. - */ - em = ExplorerManager.find(this); IngestManager.getInstance().addIngestModuleEventListener(ingestListener); Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { devicesMap.clear(); @@ -625,4 +621,8 @@ final public class FiltersPanel extends javax.swing.JPanel { private final javax.swing.JButton unCheckAllAccountTypesButton = new javax.swing.JButton(); private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables + + void setExplorerManager(ExplorerManager explorerManager) { + em = explorerManager; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index a97364de65..6cf7ebb823 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -22,6 +22,7 @@ import java.util.Collections; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; +import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; @@ -37,9 +38,10 @@ import org.sleuthkit.datamodel.CommunicationsManager; * The right hand side of the CVT. Has a DataResultPanel to show messages and * other account details, and a ContentViewer to show individual */ -final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager.Provider { - +public final class MessageBrowser extends JPanel { + private static final long serialVersionUID = 1L; + private final ExplorerManager tableEM; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; private DataResultViewerTable dataResultViewerTable; @@ -53,7 +55,8 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager * in the messages browser can be exposed to * context-sensitive actions. */ - MessageBrowser(ExplorerManager gacExplorerManager) { + public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); //create an uninitialized DataResultPanel so we can control the ResultViewers that get added. @@ -61,11 +64,11 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); } - + @Override public void addNotify() { super.addNotify(); - ExplorerManager parentEm = ExplorerManager.find(this); +// ExplorerManager parentEm = ExplorerManager.find(this); // parentEm.addPropertyChangeListener(pce -> { // gacExplorerManager.setRootContext(parentEm.getRootContext()); // try { @@ -74,14 +77,14 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager // Exceptions.printStackTrace(ex); // } // }); - - parentEm.addPropertyChangeListener(pce -> { + + tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = parentEm.getSelectedNodes(); - + final Node[] selectedNodes = tableEM.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); - + if (selectedNodes.length == 0) { //reset panel when there is no selection messagesResultPanel.setPath(""); @@ -92,7 +95,7 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager CommunicationsFilter filter = adiNode.getFilter(); CommunicationsManager commsManager = adiNode.getCommsManager(); final Set accountDeviceInstances; - + if (selectedNodes.length == 1) { final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); accountDeviceInstances = Collections.singleton(accountDeviceInstance); @@ -121,11 +124,6 @@ final class MessageBrowser extends javax.swing.JPanel implements ExplorerManager } messagesResultPanel.open(); } - - @Override - public ExplorerManager getExplorerManager() { - return gacExplorerManager; - } /** * This method is called from within the constructor to initialize the form. diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 3c45fd7568..3262c06010 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -16,44 +16,66 @@ - - - - + - + - + - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 15e71dc88f..14fa7bd5d3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,6 +26,7 @@ import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; +import java.awt.BorderLayout; import java.awt.Color; import java.beans.PropertyVetoException; import java.util.HashMap; @@ -42,6 +43,7 @@ import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.sleuthkit.autopsy.casemodule.Case; import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; +import org.sleuthkit.autopsy.communications.CVTTopComponent.ProxiedExplorerManagerProvider; import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -57,23 +59,25 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel implements ExplorerManager.Provider { +public class VisualizationPanel extends JPanel implements ExplorerManager.Provider, ProxiedExplorerManagerProvider { + + private static final long serialVersionUID = 1L; static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private ExplorerManager explorerManager; - private final mxGraph graph; - private final Map nodeMap = new HashMap<>(); + private ExplorerManager vizEM = new ExplorerManager(); + + private final ExplorerManager gacEM = new ExplorerManager(); private final mxGraphComponent graphComponent; + private final mxGraph graph; + private final Map nodeMap = new HashMap<>(); static { + //initialize defaul cell properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); } - /** - * Creates new form VizPanel - */ public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -89,17 +93,12 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); - this.add(graphComponent); - + jPanel1.add(graphComponent, BorderLayout.CENTER); + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); graph.setStylesheet(mxStylesheet); - } - - public void initVisualization(ExplorerManager em) { - explorerManager = em; - graph.getSelectionModel().addListener(null, (sender, evt) -> { Object[] selectionCells = graph.getSelectionCells(); if (selectionCells.length == 1) { @@ -111,8 +110,8 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid final AccountDeviceInstanceNode accountDeviceInstanceNode = new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), commsManager); - explorerManager.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - explorerManager.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); + vizEM.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); + vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { System.out.println(selectionCell.getId()); @@ -130,7 +129,12 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid @Override public ExplorerManager getExplorerManager() { - return explorerManager; + return vizEM; + } + + @Override + public ExplorerManager getProxiedExplorerManager() { + return gacEM; } private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { @@ -276,24 +280,17 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid // //GEN-BEGIN:initComponents private void initComponents() { + splitPane = new javax.swing.JSplitPane(); + jPanel1 = new javax.swing.JPanel(); jToolBar1 = new javax.swing.JToolBar(); - jButton1 = new javax.swing.JButton(); jButton2 = new javax.swing.JButton(); + jButton1 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); - jToolBar1.setRollover(true); + jPanel1.setLayout(new java.awt.BorderLayout()); - jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N - jButton1.setFocusable(false); - jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { - jButton1ActionPerformed(evt); - } - }); - jToolBar1.add(jButton1); + jToolBar1.setRollover(true); jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); @@ -306,7 +303,22 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid }); jToolBar1.add(jButton2); - add(jToolBar1, java.awt.BorderLayout.PAGE_START); + jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton1.setFocusable(false); + jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton1.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1ActionPerformed(evt); + } + }); + jToolBar1.add(jButton1); + + jPanel1.add(jToolBar1, java.awt.BorderLayout.PAGE_START); + + splitPane.setLeftComponent(jPanel1); + + add(splitPane, java.awt.BorderLayout.CENTER); }// //GEN-END:initComponents private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed @@ -324,7 +336,9 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; + private javax.swing.JPanel jPanel1; private javax.swing.JToolBar jToolBar1; + private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables static class SimpleParentNode extends AbstractNode { From d961ea6062652daeaada7cd1104c792c30e5a304 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 6 Jan 2018 14:27:26 +0100 Subject: [PATCH 006/347] finish wiring ExplorerManagers for seperate populaiton of MessageBrowsers by browse and visualize modes --- .../communications/AccountsRootChildren.java | 27 ------------------- .../communications/CVTTopComponent.java | 2 +- .../communications/MessageBrowser.java | 15 ++++------- .../communications/VisualizationPanel.java | 12 +++------ 4 files changed, 9 insertions(+), 47 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java index b0d59bd183..e4e888158b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java @@ -22,7 +22,6 @@ import java.awt.event.ActionEvent; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Objects; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.AbstractAction; @@ -154,32 +153,6 @@ class AccountsRootChildren extends ChildFactory { return s; } - @Override - public int hashCode() { - int hash = 3; - hash = 73 * hash + Objects.hashCode(this.accountDeviceInstanceKey); - return hash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final AccountDeviceInstanceNode other = (AccountDeviceInstanceNode) obj; - if (!Objects.equals(this.accountDeviceInstanceKey, other.accountDeviceInstanceKey)) { - return false; - } - - return true; - } - @Override public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index fd6019c0d0..3c796d6222 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -73,7 +73,7 @@ public final class CVTTopComponent extends TopComponent { // acctsBrowserExplorerManager = new ExplorerManager(); browseVisualizeTabPane.addChangeListener(changeEvent -> { - ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); + ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap())); }); diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 6cf7ebb823..7e7a5d2f2c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.CommunicationsManager; * The right hand side of the CVT. Has a DataResultPanel to show messages and * other account details, and a ContentViewer to show individual */ -public final class MessageBrowser extends JPanel { +public final class MessageBrowser extends JPanel implements ExplorerManager.Provider { private static final long serialVersionUID = 1L; private final ExplorerManager tableEM; @@ -68,15 +68,6 @@ public final class MessageBrowser extends JPanel { @Override public void addNotify() { super.addNotify(); -// ExplorerManager parentEm = ExplorerManager.find(this); -// parentEm.addPropertyChangeListener(pce -> { -// gacExplorerManager.setRootContext(parentEm.getRootContext()); -// try { -// gacExplorerManager.setSelectedNodes(parentEm.getSelectedNodes()); -// } catch (PropertyVetoException ex) { -// Exceptions.printStackTrace(ex); -// } -// }); tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { @@ -166,4 +157,8 @@ public final class MessageBrowser extends JPanel { private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables + @Override + public ExplorerManager getExplorerManager() { + return gacExplorerManager; + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 14fa7bd5d3..557e762a2e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -59,14 +59,13 @@ import org.sleuthkit.datamodel.TskCoreException; /** * */ -public class VisualizationPanel extends JPanel implements ExplorerManager.Provider, ProxiedExplorerManagerProvider { +public class VisualizationPanel extends JPanel implements ProxiedExplorerManagerProvider { private static final long serialVersionUID = 1L; static final private mxStylesheet mxStylesheet = new mxStylesheet(); - private ExplorerManager vizEM = new ExplorerManager(); - + private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); private final mxGraphComponent graphComponent; private final mxGraph graph; @@ -93,7 +92,7 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); - jPanel1.add(graphComponent, BorderLayout.CENTER); + jPanel1.add(graphComponent, BorderLayout.CENTER); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -127,11 +126,6 @@ public class VisualizationPanel extends JPanel implements ExplorerManager.Provid }); } - @Override - public ExplorerManager getExplorerManager() { - return vizEM; - } - @Override public ExplorerManager getProxiedExplorerManager() { return gacEM; From 0630f46897f6408432cb37c9ff8ca1e053a40bc0 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 8 Jan 2018 15:16:06 +0100 Subject: [PATCH 007/347] rewire explorermanagers and views to support multiple selection on account table. --- .../AccountDeviceInstanceKey.java | 40 +++- .../AccountDeviceInstanceNode.java | 132 +++++++++++++ .../AccountDeviceInstanceNodeFactory.java | 71 +++++++ .../communications/AccountsBrowser.form | 2 +- .../communications/AccountsBrowser.java | 40 ++-- .../communications/AccountsRootChildren.java | 178 ------------------ .../autopsy/communications/CVTEvents.java | 27 ++- .../communications/CVTTopComponent.java | 49 ++--- .../communications/FilterProvider.java | 26 +++ .../autopsy/communications/FiltersPanel.java | 52 +++-- .../communications/MessageBrowser.java | 27 +-- .../communications/PinAccountEvent.java | 13 +- .../communications/VisualizationPanel.java | 117 +++++++++--- 13 files changed, 471 insertions(+), 303 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index 343db541e2..5b3ab52fd4 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -19,23 +19,30 @@ package org.sleuthkit.autopsy.communications; import java.util.Objects; +import java.util.logging.Level; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.DataSource; +import org.sleuthkit.datamodel.SleuthkitCase; +import org.sleuthkit.datamodel.TskCoreException; /** * Key for AccountDeviceInstance node. * - * Encapsulates a AccountDeviceInstance, and CommunicationsFilter. + * Encapsulates a AccountDeviceInstanc,some meta data about it, and + * CommunicationsFilter. */ -class AccountDeviceInstanceKey { +final class AccountDeviceInstanceKey { + + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceKey.class.getName()); private final AccountDeviceInstance accountDeviceInstance; private final CommunicationsFilter filter; private final long messageCount; private final String dataSourceName; - - AccountDeviceInstanceKey(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter, long msgCount, String dataSourceName) { this.accountDeviceInstance = accountDeviceInstance; this.filter = filter; @@ -43,6 +50,13 @@ class AccountDeviceInstanceKey { this.dataSourceName = dataSourceName; } + AccountDeviceInstanceKey(AccountDeviceInstance accountDeviceInstance, CommunicationsFilter filter, long msgCount) { + this.accountDeviceInstance = accountDeviceInstance; + this.filter = filter; + this.messageCount = msgCount; + this.dataSourceName = getDataSourceName(accountDeviceInstance, Case.getCurrentCase().getSleuthkitCase()); + } + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstance; } @@ -54,7 +68,7 @@ class AccountDeviceInstanceKey { long getMessageCount() { return messageCount; } - + String getDataSourceName() { return dataSourceName; } @@ -96,5 +110,17 @@ class AccountDeviceInstanceKey { } return true; } - + + private static String getDataSourceName(AccountDeviceInstance accountDeviceInstance, SleuthkitCase db) { + try { + for (DataSource dataSource : db.getDataSources()) { + if (dataSource.getDeviceId().equals(accountDeviceInstance.getDeviceId())) { + return db.getContentById(dataSource.getId()).getName(); + } + } + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex); + } + return accountDeviceInstance.getDeviceId(); + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java new file mode 100644 index 0000000000..aa34a65e8e --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -0,0 +1,132 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-18 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import java.awt.event.ActionEvent; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import javax.swing.AbstractAction; +import javax.swing.Action; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; +import org.openide.nodes.Sheet; +import org.openide.util.NbBundle; +import org.openide.util.Utilities; +import org.openide.util.lookup.Lookups; +import org.sleuthkit.autopsy.datamodel.NodeProperty; +import org.sleuthkit.datamodel.Account; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsManager; + +/** + * Node to represent an Account Device Instance in the CVT + */ +final class AccountDeviceInstanceNode extends AbstractNode { + + private final AccountDeviceInstanceKey accountDeviceInstanceKey; + private final CommunicationsManager commsManager; + private final Account account; + + AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { + super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); + this.accountDeviceInstanceKey = accountDeviceInstanceKey; + this.commsManager = commsManager; + this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); + setName(account.getTypeSpecificID()); + setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); + } + + public AccountDeviceInstance getAccountDeviceInstance() { + return accountDeviceInstanceKey.getAccountDeviceInstance(); + } + + public AccountDeviceInstanceKey getAccountDeviceInstanceKey() { + return accountDeviceInstanceKey; + } + + public CommunicationsManager getCommsManager() { + return commsManager; + } + + public long getMessageCount() { + return accountDeviceInstanceKey.getMessageCount(); + } + + public CommunicationsFilter getFilter() { + return accountDeviceInstanceKey.getCommunicationsFilter(); + } + + @Override + @NbBundle.Messages(value = {"AccountNode.device=Device", "AccountNode.accountName=Account", "AccountNode.accountType=Type", "AccountNode.messageCount=Msgs"}) + protected Sheet createSheet() { + Sheet s = super.createSheet(); + Sheet.Set properties = s.get(Sheet.PROPERTIES); + if (properties == null) { + properties = Sheet.createPropertiesSet(); + s.put(properties); + } + properties.put(new NodeProperty<>("type", + Bundle.AccountNode_accountType(), + "type", + account.getAccountType().getDisplayName())); // NON-NLS + properties.put(new NodeProperty<>("count", + Bundle.AccountNode_messageCount(), + "count", + accountDeviceInstanceKey.getMessageCount())); // NON-NLS + properties.put(new NodeProperty<>("device", + Bundle.AccountNode_device(), + "device", + accountDeviceInstanceKey.getDataSourceName())); // NON-NLS + return s; + } + + @Override + public Action[] getActions(boolean context) { + ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); + actions.add(PinAccountsAction.getInstance()); + return actions.toArray(new Action[actions.size()]); + } + + /** + * Action that pins the selected AccountDeviceInstances to the + * visualization. + */ + static private class PinAccountsAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private static PinAccountsAction instance = new PinAccountsAction(); + + private static PinAccountsAction getInstance() { + return instance; + } + + private PinAccountsAction() { + super("Visualize Account"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Collection lookupAll = + Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); + CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll)); + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java new file mode 100644 index 0000000000..bb63d65270 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -0,0 +1,71 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2017-18 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Node; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * ChildFactory that creates AccountDeviceInstanceKeys and + * AccountDeviceInstanceNodes using a provided CommunicationsManager and + * CommunicationsFilter + */ +final class AccountDeviceInstanceNodeFactory extends ChildFactory { + + private static final Logger logger = Logger.getLogger(AccountDeviceInstanceNodeFactory.class.getName()); + + private final CommunicationsManager commsManager; + private final CommunicationsFilter commsFilter; + + AccountDeviceInstanceNodeFactory(CommunicationsManager commsManager, CommunicationsFilter commsFilter) { + this.commsManager = commsManager; + this.commsFilter = commsFilter; + } + + @Override + protected boolean createKeys(List list) { + List accountDeviceInstanceKeys = new ArrayList<>(); + try { + final List accountDeviceInstancesWithRelationships = + commsManager.getAccountDeviceInstancesWithRelationships(commsFilter); + for (AccountDeviceInstance accountDeviceInstance : accountDeviceInstancesWithRelationships) { + long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter); + accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount)); + }; + } catch (TskCoreException tskCoreException) { + logger.log(Level.SEVERE, "Error getting filtered account device instances", tskCoreException); + } + list.addAll(accountDeviceInstanceKeys); + + return true; + } + + @Override + protected Node createNodeForKey(AccountDeviceInstanceKey key) { + return new AccountDeviceInstanceNode(key, commsManager); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form index 96ba675284..8642d90664 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.form @@ -11,7 +11,7 @@ - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 25275d5e36..55fffa9c2b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -26,22 +26,34 @@ import javax.swing.table.TableCellRenderer; import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.ExplorerManager; +import org.openide.explorer.ExplorerUtils; +import org.openide.util.Lookup; +import org.openide.util.lookup.ProxyLookup; /** * A panel that goes in the Browse tab of the Communications Visualization Tool. - * Hosts an OutlineView that shows information about Accounts. + * Hosts an OutlineView that shows information about Accounts, and a + * MessageBrowser for viewing details of communications. + * + * The Lookup provided by getLookup will be proxied by the lookup of the + * CVTTopComponent when this tab is active allowing for context sensitive + * actions to work correctly. */ -public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, CVTTopComponent.ProxiedExplorerManagerProvider { +public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; private final Outline outline; - private final ExplorerManager gacEM = new ExplorerManager(); - private ExplorerManager tableEM; - /** - * Creates new form AccountsBrowser + private final ExplorerManager messageBrowserEM = new ExplorerManager(); + private ExplorerManager accountsTableEM; + + /* + * This lookup proxies the selection lookup of both he accounts table and + * the messages table. */ + private ProxyLookup proxyLookup; + public AccountsBrowser() { initComponents(); outline = outlineView.getOutline(); @@ -59,7 +71,7 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, } void init(ExplorerManager tableExplorerManager) { - this.tableEM = tableExplorerManager; + this.accountsTableEM = tableExplorerManager; tableExplorerManager.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); @@ -68,7 +80,11 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, } }); - jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, gacEM)); + jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, messageBrowserEM)); + + proxyLookup = new ProxyLookup( + ExplorerUtils.createLookup(messageBrowserEM, getActionMap()), + ExplorerUtils.createLookup(accountsTableEM, getActionMap())); } private void setColumnWidths() { @@ -122,11 +138,11 @@ public class AccountsBrowser extends JPanel implements ExplorerManager.Provider, @Override public ExplorerManager getExplorerManager() { - return tableEM; + return accountsTableEM; } @Override - public ExplorerManager getProxiedExplorerManager() { - return gacEM; + public Lookup getLookup() { + return proxyLookup; } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java deleted file mode 100644 index e4e888158b..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsRootChildren.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2017 Basis Technology Corp. - * Contact: carrier sleuthkit 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.communications; - -import java.awt.event.ActionEvent; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.logging.Level; -import java.util.logging.Logger; -import javax.swing.AbstractAction; -import javax.swing.Action; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.ChildFactory; -import org.openide.nodes.Children; -import org.openide.nodes.Node; -import org.openide.nodes.Sheet; -import org.openide.util.NbBundle; -import org.openide.util.lookup.Lookups; -import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.datamodel.NodeProperty; -import org.sleuthkit.datamodel.Account; -import org.sleuthkit.datamodel.AccountDeviceInstance; -import org.sleuthkit.datamodel.CommunicationsFilter; -import org.sleuthkit.datamodel.CommunicationsManager; -import org.sleuthkit.datamodel.DataSource; -import org.sleuthkit.datamodel.SleuthkitCase; -import org.sleuthkit.datamodel.TskCoreException; - -class AccountsRootChildren extends ChildFactory { - - private static final Logger logger = Logger.getLogger(AccountsRootChildren.class.getName()); - - private final CommunicationsManager commsManager; - private final CommunicationsFilter commsFilter; - - AccountsRootChildren(CommunicationsManager commsManager, CommunicationsFilter commsFilter) { - super(); - this.commsManager = commsManager; - this.commsFilter = commsFilter; - } - - @Override - protected boolean createKeys(List list) { - List accountDeviceInstanceKeys = new ArrayList<>(); - try { - for (AccountDeviceInstance accountDeviceInstance : commsManager.getAccountDeviceInstancesWithRelationships(commsFilter)) { - long communicationsCount = commsManager.getRelationshipSourcesCount(accountDeviceInstance, commsFilter); - String dataSourceName = getDataSourceName(accountDeviceInstance); - accountDeviceInstanceKeys.add(new AccountDeviceInstanceKey(accountDeviceInstance, commsFilter, communicationsCount, dataSourceName)); - }; - } catch (TskCoreException tskCoreException) { - logger.log(Level.SEVERE, "Error getting filtered account device instances", tskCoreException); - } - list.addAll(accountDeviceInstanceKeys); - - return true; - } - - @Override - protected Node createNodeForKey(AccountDeviceInstanceKey key) { - return new AccountDeviceInstanceNode(key, commsManager); - } - - static String getDataSourceName(AccountDeviceInstance accountDeviceInstance) { - try { - final SleuthkitCase sleuthkitCase = Case.getCurrentCase().getSleuthkitCase(); - for (DataSource dataSource : sleuthkitCase.getDataSources()) { - if (dataSource.getDeviceId().equals(accountDeviceInstance.getDeviceId())) { - return sleuthkitCase.getContentById(dataSource.getId()).getName(); - } - } - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "Error getting datasource name, falling back on device ID.", ex); - } - return accountDeviceInstance.getDeviceId(); - } - - /** - * Node to represent an Account in the AccountsBrowser - */ - static class AccountDeviceInstanceNode extends AbstractNode { - - private final AccountDeviceInstanceKey accountDeviceInstanceKey; - - private final CommunicationsManager commsManager; - private final Account account; - - AccountDeviceInstanceNode(AccountDeviceInstanceKey accountDeviceInstanceKey, CommunicationsManager commsManager) { - super(Children.LEAF, Lookups.fixed(accountDeviceInstanceKey, commsManager)); - this.accountDeviceInstanceKey = accountDeviceInstanceKey; - this.commsManager = commsManager; - this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); - setName(account.getTypeSpecificID()); - setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); - } - - public AccountDeviceInstance getAccountDeviceInstance() { - return accountDeviceInstanceKey.getAccountDeviceInstance(); - } - - public AccountDeviceInstanceKey getAccountDeviceInstanceKey() { - return accountDeviceInstanceKey; - } - - public CommunicationsManager getCommsManager() { - return commsManager; - } - - public long getMessageCount() { - return accountDeviceInstanceKey.getMessageCount(); - } - - public CommunicationsFilter getFilter() { - return accountDeviceInstanceKey.getCommunicationsFilter(); - } - - @Override - @NbBundle.Messages(value = {"AccountNode.device=Device", - "AccountNode.accountName=Account", - "AccountNode.accountType=Type", - "AccountNode.messageCount=Msgs"}) - protected Sheet createSheet() { - Sheet s = super.createSheet(); - Sheet.Set properties = s.get(Sheet.PROPERTIES); - if (properties == null) { - properties = Sheet.createPropertiesSet(); - s.put(properties); - } - - properties.put(new NodeProperty<>("type", Bundle.AccountNode_accountType(), "type", - account.getAccountType().getDisplayName())); // NON-NLS - properties.put(new NodeProperty<>("count", Bundle.AccountNode_messageCount(), "count", - accountDeviceInstanceKey.getMessageCount())); // NON-NLS - properties.put(new NodeProperty<>("device", Bundle.AccountNode_device(), "device", - accountDeviceInstanceKey.getDataSourceName())); // NON-NLS - return s; - } - - @Override - public Action[] getActions(boolean context) { - ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); - actions.add(new PinAccountAction()); - return actions.toArray(new Action[actions.size()]); - } - - /** - * - */ - private class PinAccountAction extends AbstractAction { - - public PinAccountAction() { - super("Visualize Account"); - } - - @Override - public void actionPerformed(ActionEvent e) { - CVTEvents.getCVTEventBus().post(new PinAccountEvent(AccountDeviceInstanceNode.this)); - } - } - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index d4c76ce2d8..a8e75fffe9 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -1,21 +1,34 @@ /* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. + * Autopsy Forensic Browser + * + * Copyright 2017-18 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; import com.google.common.eventbus.EventBus; /** - * + * Provide he singleton EventBus. */ -public class CVTEvents { +final class CVTEvents { -private final static EventBus cvtEventBus = new EventBus(); + private final static EventBus cvtEventBus = new EventBus(); public static EventBus getCVTEventBus() { return cvtEventBus; } - + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 3c796d6222..55d1ccb0fd 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,7 +22,6 @@ import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; import org.openide.explorer.ExplorerManager; -import static org.openide.explorer.ExplorerUtils.createLookup; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -48,35 +47,29 @@ public final class CVTTopComponent extends TopComponent { @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); - filtersPane.setExplorerManager(filterToTableEXplorerManager); setName(Bundle.CVTTopComponent_name()); + /* + * Connect the filtersPane and the accountsBrowser via a shared + * ExplorerMmanager + */ + filtersPane.setExplorerManager(filterToTableEXplorerManager); + accountsBrowser.init(filterToTableEXplorerManager); + /* - * Associate an explorer manager with the GlobalActionContext (GAC) for - * use by the messages browsers so that selections in the messages - * browser can be exposed to context-sensitive actions. + * Associate an Lookup with the GlobalActionContext (GAC) so that + * selections in the sub views can be exposed to context-sensitive + * actions. */ -// ExplorerManager browserExplorerManager = new ExplorerManager(); -// ExplorerManager vizExplorerManager = new ExplorerManager(); - ProxyLookupImpl proxyLookup = new ProxyLookupImpl(); + ProxyLookupImpl proxyLookup = new ProxyLookupImpl(accountsBrowser.getLookup()); associateLookup(proxyLookup); - accountsBrowser.init(filterToTableEXplorerManager); -// browseSplitPane.setRightComponent(new MessageBrowser(browserExplorerManager)); -// vizSplitPane.setRightComponent(new MessageBrowser(vizExplorerManager)); -// /* -// * Create a second explorer manager to be discovered by the accounts -// * browser and the message browser so that the browsers can both listen -// * for explorer manager property events for the outline view of the -// * accounts browser. This provides a mechanism for pushing selected -// * Nodes from the accounts browser to the messages browser. -// */ -// acctsBrowserExplorerManager = new ExplorerManager(); - + // Make sure the GAC is proxying the selection of the active tab. browseVisualizeTabPane.addChangeListener(changeEvent -> { - ProxiedExplorerManagerProvider selectedComponent = (ProxiedExplorerManagerProvider) browseVisualizeTabPane.getSelectedComponent(); - proxyLookup.changeLookup(createLookup(selectedComponent.getProxiedExplorerManager(), getActionMap())); + Lookup.Provider selectedComponent = (Lookup.Provider) browseVisualizeTabPane.getSelectedComponent(); + proxyLookup.changeLookups(selectedComponent.getLookup()); }); + vizPanel.setFilterProvider(filtersPane); CVTEvents.getCVTEventBus().register(this); } @@ -169,16 +162,12 @@ public final class CVTTopComponent extends TopComponent { private static class ProxyLookupImpl extends ProxyLookup { - ProxyLookupImpl() { + ProxyLookupImpl(Lookup... l) { + super(l); } - void changeLookup(Lookup l) { + void changeLookups(Lookup... l) { setLookups(l); } } - - static interface ProxiedExplorerManagerProvider { - - ExplorerManager getProxiedExplorerManager(); - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java new file mode 100644 index 0000000000..54f48f9dd1 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java @@ -0,0 +1,26 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import org.sleuthkit.datamodel.CommunicationsFilter; + +interface FilterProvider { + + CommunicationsFilter getFilter(); +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 9fc83ca1b5..24e589290c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -57,13 +57,13 @@ import org.sleuthkit.datamodel.SleuthkitCase; import org.sleuthkit.datamodel.TskCoreException; /** - * Panel that holds the Filter control widgets and translates user filtering - * changes into queries against the CommunicationsManager. + * Panel that holds the Filter control widgets and triggers queries against the + * CommunicationsManager on user filtering changes. */ -final public class FiltersPanel extends JPanel { +final public class FiltersPanel extends JPanel implements FilterProvider { - private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); private ExplorerManager em; @@ -134,6 +134,10 @@ final public class FiltersPanel extends JPanel { refreshButton.addActionListener(e -> applyFilters()); } + void setExplorerManager(ExplorerManager explorerManager) { + em = explorerManager; + } + /** * Validate that filters are in a consistent state and will result in some * results. Checks that at least one device and at least one account type is @@ -166,6 +170,9 @@ final public class FiltersPanel extends JPanel { dateRangeLabel.setText("Date Range ( " + Utils.getUserPreferredZoneId().toString() + "):"); } + /** + * Updates the filter widgets to reflect he data sources/types in the case. + */ private void updateFilters() { updateAccountTypeFilter(); updateDeviceFilter(); @@ -176,6 +183,7 @@ final public class FiltersPanel extends JPanel { super.addNotify(); IngestManager.getInstance().addIngestModuleEventListener(ingestListener); Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { + //clear the device filter widget when the case changes. devicesMap.clear(); devicesPane.removeAll(); }); @@ -482,20 +490,17 @@ final public class FiltersPanel extends JPanel { }// //GEN-END:initComponents /** - * Query for accounts using the selected filters, and send the results to - * the AccountsBrowser via the ExplorerManager. + * Push a new root AccountDeviceInstanceNodeFactory with he current filters + * into the explorer manager. The factory will do he actual queries. + * + * */ private void applyFilters() { - CommunicationsFilter commsFilter = new CommunicationsFilter(); - commsFilter.addAndFilter(getDeviceFilter()); - commsFilter.addAndFilter(getAccountTypeFilter()); - commsFilter.addAndFilter(getDateRangeFilter()); - commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter( - ImmutableSet.of(CALL_LOG, MESSAGE))); + CommunicationsFilter commsFilter = getFilter(); try { final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - em.setRootContext(new AbstractNode(Children.create(new AccountsRootChildren(commsManager, commsFilter), true))); + em.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, commsFilter), true))); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); } @@ -504,6 +509,17 @@ final public class FiltersPanel extends JPanel { validateFilters(); } + @Override + public CommunicationsFilter getFilter() { + CommunicationsFilter commsFilter = new CommunicationsFilter(); + commsFilter.addAndFilter(getDeviceFilter()); + commsFilter.addAndFilter(getAccountTypeFilter()); + commsFilter.addAndFilter(getDateRangeFilter()); + commsFilter.addAndFilter(new CommunicationsFilter.RelationshipTypeFilter( + ImmutableSet.of(CALL_LOG, MESSAGE))); + return commsFilter; + } + /** * Get a DeviceFilter that matches the state of the UI widgets. * @@ -532,6 +548,11 @@ final public class FiltersPanel extends JPanel { return accountTypeFilter; } + /** + * Get an DateRangeFilter that matches the state of the UI widgets + * + * @return an DateRangeFilter + */ private DateRangeFilter getDateRangeFilter() { ZoneId zone = Utils.getUserPreferredZoneId(); long start = startDatePicker.isEnabled() ? startDatePicker.getDate().atStartOfDay(zone).toEpochSecond() : 0; @@ -622,7 +643,4 @@ final public class FiltersPanel extends JPanel { private final javax.swing.JButton unCheckAllDevicesButton = new javax.swing.JButton(); // End of variables declaration//GEN-END:variables - void setExplorerManager(ExplorerManager explorerManager) { - em = explorerManager; - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 7e7a5d2f2c..eb81c23ce5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -25,7 +25,6 @@ import java.util.stream.Stream; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; -import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -50,6 +49,8 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * Constructs the right hand side of the Communications Visualization Tool * (CVT). * + * @param tableEM An explorer manager to listen to as the driver + * of the Message Table. * @param gacExplorerManager An explorer manager associated with the * GlobalActionsContext (GAC) so that selections * in the messages browser can be exposed to @@ -63,11 +64,9 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent); splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); - } - - @Override - public void addNotify() { - super.addNotify(); + dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages"); + messagesResultPanel.addResultViewer(dataResultViewerTable); + messagesResultPanel.open(); tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { @@ -107,13 +106,11 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov } } }); + } - //add the required result viewers and THEN open the panel - if (null == dataResultViewerTable) { - dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages"); - messagesResultPanel.addResultViewer(dataResultViewerTable); - } - messagesResultPanel.open(); + @Override + public ExplorerManager getExplorerManager() { + return gacExplorerManager; } /** @@ -157,8 +154,4 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables - @Override - public ExplorerManager getExplorerManager() { - return gacExplorerManager; - } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java index 181e160041..e77ab3f35d 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -5,20 +5,21 @@ */ package org.sleuthkit.autopsy.communications; -import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; +import com.google.common.collect.ImmutableSet; +import java.util.Collection; /** * */ class PinAccountEvent { - private final AccountDeviceInstanceNode accountDeviceInstance; + private final ImmutableSet accountDeviceInstances; - public AccountDeviceInstanceNode getAccountDeviceInstanceNode() { - return accountDeviceInstance; + public ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; } - PinAccountEvent(AccountDeviceInstanceNode accountDeviceInstance) { - this.accountDeviceInstance = accountDeviceInstance; + PinAccountEvent(Collection accountDeviceInstances) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 557e762a2e..d22359e0b8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -29,21 +29,25 @@ import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.beans.PropertyVetoException; +import java.util.EnumSet; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.logging.Level; import javax.swing.JPanel; import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; +import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; -import org.sleuthkit.autopsy.communications.AccountsRootChildren.AccountDeviceInstanceNode; -import org.sleuthkit.autopsy.communications.CVTTopComponent.ProxiedExplorerManagerProvider; +import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; @@ -57,25 +61,43 @@ import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.TskCoreException; /** + * * A panel that goes in the Visualize tab of the Communications Visualization + * Tool. Hosts an JGraphX mxGraphComponent that host the communications network + * visualization and a MessageBrowser for viewing details of communications. * + * The Lookup provided by getLookup will be proxied by the lookup of the + * CVTTopComponent when this tab is active allowing for context sensitive + * actions to work correctly. */ -public class VisualizationPanel extends JPanel implements ProxiedExplorerManagerProvider { +final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; + private Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); static final private mxStylesheet mxStylesheet = new mxStylesheet(); + static { + //initialize defaul cell (Vertex and/or Edge) properties + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + } + private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); + private final ProxyLookup proxyLookup = new ProxyLookup( + ExplorerUtils.createLookup(gacEM, getActionMap()), + ExplorerUtils.createLookup(vizEM, getActionMap())); + private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); - static { - //initialize defaul cell properties - mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); - mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + private CommunicationsManager commsManager; + + void setFilterProvider(FilterProvider filterProvider) { + this.filterProvider = filterProvider; } + private FilterProvider filterProvider; public VisualizationPanel() { initComponents(); @@ -103,7 +125,6 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager if (selectionCells.length == 1) { mxCell selectionCell = (mxCell) selectionCells[0]; try { - CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); if (selectionCell.isVertex()) { final AccountDeviceInstanceNode accountDeviceInstanceNode = @@ -116,19 +137,16 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager System.out.println(selectionCell.getId()); // explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); } - } catch (TskCoreException tskCoreException) { - Logger.getLogger(VisualizationPanel.class.getName()).log(Level.SEVERE, - "Could not get communications manager for current case", tskCoreException); } catch (PropertyVetoException ex) { - Exceptions.printStackTrace(ex); + logger.log(Level.SEVERE, "Account selection vetoed.", ex); } } }); } @Override - public ExplorerManager getProxiedExplorerManager() { - return gacEM; + public Lookup getLookup() { + return proxyLookup; } private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { @@ -192,29 +210,29 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager } @Subscribe - public void pinAccount(PinAccountEvent pinEvent) { + public void pinAccounts(PinAccountEvent pinEvent) { - final AccountDeviceInstanceNode adiNode = pinEvent.getAccountDeviceInstanceNode(); - final AccountDeviceInstanceKey adiKey = adiNode.getAccountDeviceInstanceKey(); + final Set adiKeys = pinEvent.getAccountDeviceInstances(); + final CommunicationsFilter commsFilter = filterProvider.getFilter(); graph.getModel().beginUpdate(); try { nodeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - CommunicationsManager commsManager = adiNode.getCommsManager(); - final CommunicationsFilter commsFilter = adiNode.getFilter(); - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiNode.getAccountDeviceInstance(), commsFilter); + for (AccountDeviceInstanceKey adiKey : adiKeys) { + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); - String dataSourceName = AccountsRootChildren.getDataSourceName(relatedADI); - AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount, dataSourceName); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); - addEdge(pinnedAccountVertex, relatedAccountVertex); + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); + AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + + addEdge(pinnedAccountVertex, relatedAccountVertex); + } } } catch (TskCoreException ex) { Exceptions.printStackTrace(ex); @@ -265,6 +283,49 @@ public class VisualizationPanel extends JPanel implements ProxiedExplorerManager return vertex; } + @Override + public void addNotify() { + super.addNotify(); +// IngestManager.getInstance().addIngestModuleEventListener(ingestListener); + try { + commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + + } catch (IllegalStateException ex) { + logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); + + } + + Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { + graph.getModel().beginUpdate(); + try { + nodeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + } finally { + graph.getModel().endUpdate(); + } + if (evt.getNewValue() != null) { + Case currentCase = (Case) evt.getNewValue(); + try { + commsManager = currentCase.getSleuthkitCase().getCommunicationsManager(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); + } + } else { + commsManager = null; + } + + }); + } + + @Override + + public void removeNotify() { + super.removeNotify(); +// IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); + } + /** * 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 From 685881462e9eaa26be7c7ef2563b5b5c28f79949 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 8 Jan 2018 15:23:34 +0100 Subject: [PATCH 008/347] cleanup --- .../autopsy/communications/PinAccountAction.java | 7 ------- .../communications/VisualizationPanel.java | 15 +++++++++------ 2 files changed, 9 insertions(+), 13 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java deleted file mode 100644 index f204507299..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountAction.java +++ /dev/null @@ -1,7 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.communications; - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d22359e0b8..f75eb5a7ec 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -19,7 +19,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; -import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; @@ -239,10 +238,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } finally { // Updates the display graph.getModel().endUpdate(); - revalidate(); + } - new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + applyOrganicLayout(); + revalidate(); } private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { @@ -382,11 +382,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - new mxOrganicLayout(graph).execute(graph.getDefaultParent()); -// new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed + private void applyOrganicLayout() { + new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; From 15d031d669dec2103d17b6931c1eb218bf96b743 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 12:27:07 +0100 Subject: [PATCH 009/347] fix Codacy issues --- .../AccountDeviceInstanceKey.java | 5 +- .../AccountDeviceInstanceNodeFactory.java | 2 +- .../communications/AccountsBrowser.java | 4 +- .../communications/CVTTopComponent.java | 4 +- .../communications/MessageBrowser.java | 11 +- .../OpenCommVisualizationToolAction.java | 6 +- .../communications/RelationshipNode.java | 4 +- .../communications/VisualizationPanel.java | 101 +++--------------- 8 files changed, 32 insertions(+), 105 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java index 5b3ab52fd4..0e89e3a1b6 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceKey.java @@ -105,10 +105,7 @@ final class AccountDeviceInstanceKey { if (!Objects.equals(this.dataSourceName, other.dataSourceName)) { return false; } - if (!Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance)) { - return false; - } - return true; + return Objects.equals(this.accountDeviceInstance, other.accountDeviceInstance); } private static String getDataSourceName(AccountDeviceInstance accountDeviceInstance, SleuthkitCase db) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java index bb63d65270..5fe069b523 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNodeFactory.java @@ -55,7 +55,7 @@ final class AccountDeviceInstanceNodeFactory extends ChildFactory { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 55d1ccb0fd..d19ad25496 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -160,13 +160,13 @@ public final class CVTTopComponent extends TopComponent { .collect(Collectors.toList()); } - private static class ProxyLookupImpl extends ProxyLookup { + final private static class ProxyLookupImpl extends ProxyLookup { ProxyLookupImpl(Lookup... l) { super(l); } - void changeLookups(Lookup... l) { + protected void changeLookups(Lookup... l) { setLookups(l); } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index eb81c23ce5..8fe4873f54 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -25,6 +25,7 @@ import java.util.stream.Stream; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; +import org.openide.util.NbBundle; import org.sleuthkit.autopsy.corecomponents.DataResultPanel; import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; @@ -43,7 +44,6 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov private final ExplorerManager tableEM; private final ExplorerManager gacExplorerManager; private final DataResultPanel messagesResultPanel; - private DataResultViewerTable dataResultViewerTable; /** * Constructs the right hand side of the Communications Visualization Tool @@ -56,6 +56,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * in the messages browser can be exposed to * context-sensitive actions. */ + @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; @@ -64,13 +65,13 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov messagesResultPanel = DataResultPanel.createInstanceUninitialized("Account", "", Node.EMPTY, 0, messageDataContent); splitPane.setTopComponent(messagesResultPanel); splitPane.setBottomComponent(messageDataContent); - dataResultViewerTable = new DataResultViewerTable(gacExplorerManager, "Messages"); - messagesResultPanel.addResultViewer(dataResultViewerTable); + messagesResultPanel.addResultViewer(new DataResultViewerTable(gacExplorerManager, + Bundle.MessageBrowser_DataResultViewerTable_title())); messagesResultPanel.open(); - tableEM.addPropertyChangeListener(pce -> { + this.tableEM.addPropertyChangeListener(pce -> { if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = tableEM.getSelectedNodes(); + final Node[] selectedNodes = this.tableEM.getSelectedNodes(); messagesResultPanel.setNumMatches(0); messagesResultPanel.setNode(null); diff --git a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java index a977ff2c09..e3dc552bab 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java +++ b/Core/src/org/sleuthkit/autopsy/communications/OpenCommVisualizationToolAction.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -48,10 +48,10 @@ import org.sleuthkit.autopsy.core.RuntimeProperties; , @ActionReference(path = "Toolbars/Case", position = 102)}) @Messages("CTL_OpenCVTAction=Communications") -public final class OpenCommVisualizationToolAction extends CallableSystemAction { +public final class OpenCommVisualizationToolAction extends CallableSystemAction { private static final long serialVersionUID = 1L; - PropertyChangeListener pcl; + private final PropertyChangeListener pcl; private final JButton toolbarButton = new JButton(getName(), new ImageIcon(getClass().getResource("images/emblem-web24.png"))); //NON-NLS diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 70d70b5325..31f5871b81 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2017 Basis Technology Corp. + * Copyright 2017-18 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -127,7 +127,7 @@ public class RelationshipNode extends BlackboardArtifactNode { * @return The display string, or an empty string if there is no such * attribute or an an error. */ - static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) { + private static String getAttributeDisplayString(final BlackboardArtifact artifact, final ATTRIBUTE_TYPE attributeType) { try { BlackboardAttribute attribute = artifact.getAttribute(new BlackboardAttribute.Type(BlackboardAttribute.ATTRIBUTE_TYPE.fromID(attributeType.getTypeID()))); if (attribute == null) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f75eb5a7ec..320e7d493b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -36,7 +36,6 @@ import java.util.Random; import java.util.Set; import java.util.logging.Level; import javax.swing.JPanel; -import org.apache.commons.lang3.StringUtils; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -47,14 +46,8 @@ import org.openide.util.Lookup; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; -import static org.sleuthkit.autopsy.communications.RelationshipNode.getAttributeDisplayString; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; -import org.sleuthkit.datamodel.BlackboardArtifact; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_FROM; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_EMAIL_TO; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_FROM; -import static org.sleuthkit.datamodel.BlackboardAttribute.ATTRIBUTE_TYPE.TSK_PHONE_NUMBER_TO; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.TskCoreException; @@ -93,7 +86,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; - void setFilterProvider(FilterProvider filterProvider) { + protected void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -148,66 +141,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return proxyLookup; } - private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - - Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - - if (edgesBetween.length == 0) { - final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); - } else if (edgesBetween.length == 1) { - final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); - } - } - - @Deprecated - private void addEdge(BlackboardArtifact artifact) throws TskCoreException { - BlackboardArtifact.ARTIFACT_TYPE artfType = BlackboardArtifact.ARTIFACT_TYPE.fromID(artifact.getArtifactTypeID()); - if (null != artfType) { - - String from = null; - String[] tos = new String[0]; - - //Consider refactoring this to reduce boilerplate - switch (artfType) { - case TSK_EMAIL_MSG: - from = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_FROM), " \t\n;"); - tos = StringUtils.strip(getAttributeDisplayString(artifact, TSK_EMAIL_TO), " \t\n;").split(";"); - break; - case TSK_MESSAGE: - from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); - tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); - break; - case TSK_CALLLOG: - from = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_FROM); - tos = getAttributeDisplayString(artifact, TSK_PHONE_NUMBER_TO).split(";"); - break; - default: - break; - } - for (String to : tos) { - if (StringUtils.isNotBlank(from) && StringUtils.isNotBlank(to)) { - - mxCell fromV = getOrCreateVertex(from, 10); - mxCell toV = getOrCreateVertex(to, 10); - - Object[] edgesBetween = graph.getEdgesBetween(fromV, toV); - - if (edgesBetween.length == 0) { - final String edgeName = from + "->" + to; - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, fromV, toV); - } else if (edgesBetween.length == 1) { - final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); - } - } - } - } - } - @Subscribe public void pinAccounts(PinAccountEvent pinEvent) { @@ -265,22 +198,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return vertex; } - @Deprecated - private mxCell getOrCreateVertex(String name, long size) { - mxCell vertex = nodeMap.get(name); - if (vertex == null) { - vertex = (mxCell) graph.insertVertex( - graph.getDefaultParent(), - name, - name, - new Random().nextInt(200), - new Random().nextInt(200), - size, - size); - graph.getView().getState(vertex, true).setLabel(name); - nodeMap.put(name, vertex); + private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { + + Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + + if (edgesBetween.length == 0) { + final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); + graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + } else if (edgesBetween.length == 1) { + final mxCell edge = (mxCell) edgesBetween[0]; + edge.setValue(1d + (double) edge.getValue()); + edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); } - return vertex; } @Override @@ -399,15 +328,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables - static class SimpleParentNode extends AbstractNode { + private static class SimpleParentNode extends AbstractNode { - static SimpleParentNode createFromChildNodes(Node... nodes) { + private static SimpleParentNode createFromChildNodes(Node... nodes) { Children.Array array = new Children.Array(); array.add(nodes); return new SimpleParentNode(array); } - public SimpleParentNode(Children children) { + private SimpleParentNode(Children children) { super(children); } } From d3ff7f78e5df15ead33c97f172dfe5315049e2fd Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 12:04:09 +0100 Subject: [PATCH 010/347] WIP link selection WIP --- .../RelaionshipSetNodeFactory.java | 35 +++++++ .../communications/VisualizationPanel.java | 98 ++++++++++--------- 2 files changed, 87 insertions(+), 46 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java b/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java new file mode 100644 index 0000000000..b147ea9537 --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/RelaionshipSetNodeFactory.java @@ -0,0 +1,35 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.sleuthkit.autopsy.communications; + +import java.util.Collection; +import java.util.List; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Node; +import org.sleuthkit.datamodel.BlackboardArtifact; + +/** + * + */ +public class RelaionshipSetNodeFactory extends ChildFactory { + + private final Collection artifacts; + + public RelaionshipSetNodeFactory(Collection artifacts) { + this.artifacts = artifacts; + } + + @Override + protected boolean createKeys(List list) { + list.addAll(artifacts); + return true; + } + + @Override + protected Node createNodeForKey(BlackboardArtifact key) { + return new RelationshipNode(key); + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 320e7d493b..78b2d23e71 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -18,6 +18,8 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.model.mxCell; @@ -28,8 +30,11 @@ import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.beans.PropertyVetoException; +import java.util.Arrays; +import java.util.Collection; import java.util.EnumSet; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Random; @@ -83,6 +88,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); + Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; @@ -126,8 +132,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { - System.out.println(selectionCell.getId()); -// explorerManager.setRootContext(new CommunicationsBundleNode(adiKey, commsManager)); + vizEM.setRootContext(new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true))); } } catch (PropertyVetoException ex) { logger.log(Level.SEVERE, "Account selection vetoed.", ex); @@ -141,43 +146,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider return proxyLookup; } - @Subscribe - public void pinAccounts(PinAccountEvent pinEvent) { - - final Set adiKeys = pinEvent.getAccountDeviceInstances(); - final CommunicationsFilter commsFilter = filterProvider.getFilter(); - - graph.getModel().beginUpdate(); - try { - nodeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - - for (AccountDeviceInstanceKey adiKey : adiKeys) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); - - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - long communicationsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); - AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - - addEdge(pinnedAccountVertex, relatedAccountVertex); - } - } - } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); - } finally { - // Updates the display - graph.getModel().endUpdate(); - - } - - applyOrganicLayout(); - revalidate(); - } - private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + @@ -197,19 +165,57 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } return vertex; } - - private void addEdge(mxCell pinnedAccountVertex, mxCell relatedAccountVertex) { - + @SuppressWarnings("unchecked") + private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); - if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - graph.insertEdge(graph.getDefaultParent(), edgeName, 1d, pinnedAccountVertex, relatedAccountVertex); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(artifact)), pinnedAccountVertex, relatedAccountVertex); + edgeMap.put(artifact, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - edge.setValue(1d + (double) edge.getValue()); - edge.setStyle("strokeWidth=" + Math.log((double) edge.getValue())); + ((Collection) edge.getValue()).add(artifact); + edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + @Subscribe + public void pinAccounts(PinAccountEvent pinEvent) { + + final Set adiKeys = pinEvent.getAccountDeviceInstances(); + final CommunicationsFilter commsFilter = filterProvider.getFilter(); + + graph.getModel().beginUpdate(); + try { + nodeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + + for (AccountDeviceInstanceKey adiKey : adiKeys) { + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); + + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + + List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); + + long communicationsCount = relationships.size(); + AccountDeviceInstanceKey relatedADIKey = + new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + for (BlackboardArtifact relationship : relationships) { + addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); + } + } + } + } catch (TskCoreException ex) { + Exceptions.printStackTrace(ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + applyOrganicLayout(); + revalidate(); } @Override From 55569a189fa924fe0ce9ff4a6e227cf6815bc119 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 9 Jan 2018 16:11:39 +0100 Subject: [PATCH 011/347] working edge selection --- .../autopsy/communications/Bundle.properties | 5 +- .../communications/MessageBrowser.java | 2 + .../communications/VisualizationPanel.form | 108 ++++++++++++- .../communications/VisualizationPanel.java | 144 ++++++++++++++++-- 4 files changed, 240 insertions(+), 19 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index d0fa082fd9..abdbd65f69 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,9 +15,12 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=redo layout +VisualizationPanel.jButton1.text=Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize +VisualizationPanel.jButton3.text=Orthogonal +VisualizationPanel.jButton4.text=- +VisualizationPanel.jButton5.text=+ diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 8fe4873f54..2f76267edf 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -103,6 +103,8 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov TableFilterNode wrappedNode = new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); messagesResultPanel.setNode(wrappedNode); + }else{ + messagesResultPanel.setNode(selectedNode); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 3262c06010..1084d8f2da 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -17,6 +17,10 @@ + + + + @@ -34,17 +38,63 @@ - - - - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -72,6 +122,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 78b2d23e71..f07eaa32f1 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,6 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.mxOrganicLayout; +import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; @@ -53,12 +54,13 @@ import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.TskCoreException; /** - * * A panel that goes in the Visualize tab of the Communications Visualization + * A panel that goes in the Visualize tab of the Communications Visualization * Tool. Hosts an JGraphX mxGraphComponent that host the communications network * visualization and a MessageBrowser for viewing details of communications. * @@ -76,6 +78,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider static { //initialize defaul cell (Vertex and/or Edge) properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); } @@ -88,11 +91,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); - Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; - protected void setFilterProvider(FilterProvider filterProvider) { + void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -100,6 +103,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public VisualizationPanel() { initComponents(); graph = new mxGraph(); + graph.setCellsCloneable(false); + graph.setDropEnabled(false); + graph.setCellsCloneable(false); graph.setCellsEditable(false); graph.setCellsResizable(false); graph.setCellsMovable(true); @@ -108,11 +114,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setDisconnectOnMove(false); graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); + graph.setAllowDanglingEdges(false); + graph.setCellsDeletable(false); + graph.setCellsBendable(true); + graph.setKeepEdgesInBackground(true); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); + + graphComponent.getGraphControl().addMouseMotionListener(graphComponent.getPanningHandler()); + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -132,7 +146,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); } else if (selectionCell.isEdge()) { - vizEM.setRootContext(new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true))); + @SuppressWarnings("unchecked") + AbstractNode abstractNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true)); + vizEM.setRootContext(abstractNode); + vizEM.setSelectedNodes(new Node[]{abstractNode}); } } catch (PropertyVetoException ex) { logger.log(Level.SEVERE, "Account selection vetoed.", ex); @@ -165,6 +182,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } return vertex; } + @SuppressWarnings("unchecked") private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); @@ -176,6 +194,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final mxCell edge = (mxCell) edgesBetween[0]; ((Collection) edge.getValue()).add(artifact); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); + } + } + @Subscribe public void pinAccounts(PinAccountEvent pinEvent) { @@ -185,6 +206,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { nodeMap.clear(); + edgeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); for (AccountDeviceInstanceKey adiKey : adiKeys) { @@ -236,6 +258,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().beginUpdate(); try { nodeMap.clear(); + edgeMap.clear(); graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); } finally { graph.getModel().endUpdate(); @@ -255,7 +278,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override - public void removeNotify() { super.removeNotify(); // IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); @@ -272,15 +294,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new javax.swing.JSplitPane(); jPanel1 = new javax.swing.JPanel(); - jToolBar1 = new javax.swing.JToolBar(); + jPanel2 = new javax.swing.JPanel(); jButton2 = new javax.swing.JButton(); jButton1 = new javax.swing.JButton(); + jButton3 = new javax.swing.JButton(); + jSeparator1 = new javax.swing.JToolBar.Separator(); + jButton4 = new javax.swing.JButton(); + jButton5 = new javax.swing.JButton(); setLayout(new java.awt.BorderLayout()); - jPanel1.setLayout(new java.awt.BorderLayout()); + splitPane.setDividerLocation(400); + splitPane.setResizeWeight(0.5); - jToolBar1.setRollover(true); + jPanel1.setLayout(new java.awt.BorderLayout()); jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); @@ -291,7 +318,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton2ActionPerformed(evt); } }); - jToolBar1.add(jButton2); jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N jButton1.setFocusable(false); @@ -302,9 +328,82 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton1ActionPerformed(evt); } }); - jToolBar1.add(jButton1); - jPanel1.add(jToolBar1, java.awt.BorderLayout.PAGE_START); + jButton3.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N + jButton3.setFocusable(false); + jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton3.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3ActionPerformed(evt); + } + }); + + jSeparator1.setPreferredSize(new java.awt.Dimension(10, 10)); + + jButton4.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N + jButton4.setFocusable(false); + jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton4.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4ActionPerformed(evt); + } + }); + + jButton5.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N + jButton5.setFocusable(false); + jButton5.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); + jButton5.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); + jButton5.addActionListener(new java.awt.event.ActionListener() { + public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton5ActionPerformed(evt); + } + }); + + javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); + jPanel2.setLayout(jPanel2Layout); + jPanel2Layout.setHorizontalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addContainerGap() + .addComponent(jButton2) + .addGap(5, 5, 5) + .addComponent(jButton1) + .addGap(5, 5, 5) + .addComponent(jButton3) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, javax.swing.GroupLayout.PREFERRED_SIZE) + .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addComponent(jButton4) + .addGap(5, 5, 5) + .addComponent(jButton5) + .addContainerGap()) + ); + jPanel2Layout.setVerticalGroup( + jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton2)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton1)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton3)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(14, 14, 14) + .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) + .addGap(10, 10, 10)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton4)) + .addGroup(jPanel2Layout.createSequentialGroup() + .addGap(5, 5, 5) + .addComponent(jButton5)) + ); + + jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH); splitPane.setLeftComponent(jPanel1); @@ -320,17 +419,38 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed + private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + applyOrthogonalLayout(); + }//GEN-LAST:event_jButton3ActionPerformed + + private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + graphComponent.zoomIn(); + }//GEN-LAST:event_jButton5ActionPerformed + + private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + graphComponent.zoomOut(); + }//GEN-LAST:event_jButton4ActionPerformed + private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); graphComponent.zoomAndCenter(); } + private void applyOrthogonalLayout() { + new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JButton jButton1; private javax.swing.JButton jButton2; + private javax.swing.JButton jButton3; + private javax.swing.JButton jButton4; + private javax.swing.JButton jButton5; private javax.swing.JPanel jPanel1; - private javax.swing.JToolBar jToolBar1; + private javax.swing.JPanel jPanel2; + private javax.swing.JToolBar.Separator jSeparator1; private javax.swing.JSplitPane splitPane; // End of variables declaration//GEN-END:variables From 7fd1562427f79b8413b0975806478470c4be730a Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 10 Jan 2018 15:28:24 +0100 Subject: [PATCH 012/347] experimenting with actions. got panning to work --- .../autopsy/communications/Bundle.properties | 1 + .../communications/RelationshipNode.java | 1 - .../communications/VisualizationPanel.form | 82 ++----- .../communications/VisualizationPanel.java | 215 ++++++++++-------- 4 files changed, 138 insertions(+), 161 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index abdbd65f69..71d89548f8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -24,3 +24,4 @@ CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ +VisualizationPanel.jButton6.text=Hierarchy diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 31f5871b81..0025455a51 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -143,5 +143,4 @@ public class RelationshipNode extends BlackboardArtifactNode { return ""; } } - } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 1084d8f2da..8a2130752f 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -4,14 +4,14 @@ - + - + - + @@ -38,63 +38,17 @@ - + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -109,6 +63,19 @@ + + + + + + + + + + + + + @@ -135,13 +102,6 @@ - - - - - - - diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index f07eaa32f1..8f39aecc37 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; +import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; @@ -30,6 +31,9 @@ import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; import java.util.Arrays; import java.util.Collection; @@ -41,7 +45,11 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; +import javax.swing.JButton; import javax.swing.JPanel; +import javax.swing.JSplitPane; +import javax.swing.JToolBar; +import javax.swing.SwingConstants; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -49,6 +57,7 @@ import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Exceptions; import org.openide.util.Lookup; +import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -119,13 +128,37 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - graphComponent = new mxGraphComponent(graph); + graphComponent = new mxGraphComponent(graph){ + @Override + public boolean isPanningEvent(MouseEvent event) { + return true; //To change body of generated methods, choose Tools | Templates. + } + + }; graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); - graphComponent.getGraphControl().addMouseMotionListener(graphComponent.getPanningHandler()); +// graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { +// @Override +// public void mouseReleased(MouseEvent e) { +// graphComponent.setPanning(false); +// } +// +// @Override +// public void mouseMoved(MouseEvent e) { +// super.mouseMoved(e); +// if (graphComponent.isPanning()) { +// graphComponent.getGraphControl().setTranslate(e.getPoint()); +// } +// } +// +// @Override +// public void mousePressed(MouseEvent e) { +// graphComponent.setPanning(true); +// } +// }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -292,145 +325,124 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // //GEN-BEGIN:initComponents private void initComponents() { - splitPane = new javax.swing.JSplitPane(); - jPanel1 = new javax.swing.JPanel(); - jPanel2 = new javax.swing.JPanel(); - jButton2 = new javax.swing.JButton(); - jButton1 = new javax.swing.JButton(); - jButton3 = new javax.swing.JButton(); - jSeparator1 = new javax.swing.JToolBar.Separator(); - jButton4 = new javax.swing.JButton(); - jButton5 = new javax.swing.JButton(); + splitPane = new JSplitPane(); + jPanel1 = new JPanel(); + jToolBar1 = new JToolBar(); + jButton2 = new JButton(); + jButton6 = new JButton(); + jButton1 = new JButton(); + jButton3 = new JButton(); + jButton4 = new JButton(); + jButton5 = new JButton(); - setLayout(new java.awt.BorderLayout()); + setLayout(new BorderLayout()); splitPane.setDividerLocation(400); splitPane.setResizeWeight(0.5); - jPanel1.setLayout(new java.awt.BorderLayout()); + jPanel1.setLayout(new BorderLayout()); - jButton2.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N + jToolBar1.setRollover(true); + + jButton2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N jButton2.setFocusable(false); - jButton2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton2.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton2.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton2.setHorizontalTextPosition(SwingConstants.CENTER); + jButton2.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton2.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton2ActionPerformed(evt); } }); + jToolBar1.add(jButton2); - jButton1.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N + jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N + jButton6.setFocusable(false); + jButton6.setHorizontalTextPosition(SwingConstants.CENTER); + jButton6.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton6.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton6ActionPerformed(evt); + } + }); + jToolBar1.add(jButton6); + + jButton1.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton1.text")); // NOI18N jButton1.setFocusable(false); - jButton1.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton1.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton1.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton1.setHorizontalTextPosition(SwingConstants.CENTER); + jButton1.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton1.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton1ActionPerformed(evt); } }); + jToolBar1.add(jButton1); - jButton3.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N + jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N jButton3.setFocusable(false); - jButton3.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton3.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton3.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton3.setHorizontalTextPosition(SwingConstants.CENTER); + jButton3.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton3.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton3ActionPerformed(evt); } }); + jToolBar1.add(jButton3); - jSeparator1.setPreferredSize(new java.awt.Dimension(10, 10)); - - jButton4.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N + jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N jButton4.setFocusable(false); - jButton4.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton4.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton4.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton4.setHorizontalTextPosition(SwingConstants.CENTER); + jButton4.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton4.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton4ActionPerformed(evt); } }); + jToolBar1.add(jButton4); - jButton5.setText(org.openide.util.NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N + jButton5.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N jButton5.setFocusable(false); - jButton5.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER); - jButton5.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM); - jButton5.addActionListener(new java.awt.event.ActionListener() { - public void actionPerformed(java.awt.event.ActionEvent evt) { + jButton5.setHorizontalTextPosition(SwingConstants.CENTER); + jButton5.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton5.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { jButton5ActionPerformed(evt); } }); + jToolBar1.add(jButton5); - javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2); - jPanel2.setLayout(jPanel2Layout); - jPanel2Layout.setHorizontalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addContainerGap() - .addComponent(jButton2) - .addGap(5, 5, 5) - .addComponent(jButton1) - .addGap(5, 5, 5) - .addComponent(jButton3) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 1, javax.swing.GroupLayout.PREFERRED_SIZE) - .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addComponent(jButton4) - .addGap(5, 5, 5) - .addComponent(jButton5) - .addContainerGap()) - ); - jPanel2Layout.setVerticalGroup( - jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton2)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton1)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton3)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(14, 14, 14) - .addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) - .addGap(10, 10, 10)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton4)) - .addGroup(jPanel2Layout.createSequentialGroup() - .addGap(5, 5, 5) - .addComponent(jButton5)) - ); - - jPanel1.add(jPanel2, java.awt.BorderLayout.NORTH); + jPanel1.add(jToolBar1, BorderLayout.NORTH); splitPane.setLeftComponent(jPanel1); - add(splitPane, java.awt.BorderLayout.CENTER); + add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed + private void jButton2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed // graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - graphComponent.getPanningHandler().setEnabled(true); + graphComponent.setPanning(true); }//GEN-LAST:event_jButton2ActionPerformed - private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed + private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed applyOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed - private void jButton3ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed + applyOrthogonalLayout(); }//GEN-LAST:event_jButton3ActionPerformed - private void jButton5ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + private void jButton5ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed graphComponent.zoomIn(); }//GEN-LAST:event_jButton5ActionPerformed - private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + private void jButton4ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed graphComponent.zoomOut(); }//GEN-LAST:event_jButton4ActionPerformed + private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed + applyHierarchicalLayout(); + }//GEN-LAST:event_jButton6ActionPerformed + private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); graphComponent.zoomAndCenter(); @@ -441,17 +453,22 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.zoomAndCenter(); } + private void applyHierarchicalLayout() { + new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); + graphComponent.zoomAndCenter(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables - private javax.swing.JButton jButton1; - private javax.swing.JButton jButton2; - private javax.swing.JButton jButton3; - private javax.swing.JButton jButton4; - private javax.swing.JButton jButton5; - private javax.swing.JPanel jPanel1; - private javax.swing.JPanel jPanel2; - private javax.swing.JToolBar.Separator jSeparator1; - private javax.swing.JSplitPane splitPane; + private JButton jButton1; + private JButton jButton2; + private JButton jButton3; + private JButton jButton4; + private JButton jButton5; + private JButton jButton6; + private JPanel jPanel1; + private JToolBar jToolBar1; + private JSplitPane splitPane; // End of variables declaration//GEN-END:variables private static class SimpleParentNode extends AbstractNode { From c13e518d6a512abbba5c009a1a870e1d1fad42ab Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 11 Jan 2018 11:46:31 +0100 Subject: [PATCH 013/347] reduce public surface of the API --- .../autopsy/communications/AccountDetailsNode.java | 2 +- .../communications/AccountDeviceInstanceNode.java | 10 +++++----- .../autopsy/communications/AccountsBrowser.java | 2 +- .../sleuthkit/autopsy/communications/CVTEvents.java | 7 +++++-- .../autopsy/communications/MessageBrowser.java | 2 +- .../autopsy/communications/MessageDataContent.java | 2 +- .../autopsy/communications/PinAccountEvent.java | 4 ++-- .../autopsy/communications/RelationshipNode.java | 4 ++-- .../autopsy/communications/VisualizationPanel.java | 11 +++++++++-- 9 files changed, 27 insertions(+), 17 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java index 88470e87e0..7bc1a0faae 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java @@ -38,7 +38,7 @@ import org.sleuthkit.datamodel.TskCoreException; * relationships of all the accounts in this node. * */ -class AccountDetailsNode extends AbstractNode { +final class AccountDetailsNode extends AbstractNode { private final static Logger logger = Logger.getLogger(AccountDetailsNode.class.getName()); diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index aa34a65e8e..5b88b01999 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -54,23 +54,23 @@ final class AccountDeviceInstanceNode extends AbstractNode { setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); } - public AccountDeviceInstance getAccountDeviceInstance() { + AccountDeviceInstance getAccountDeviceInstance() { return accountDeviceInstanceKey.getAccountDeviceInstance(); } - public AccountDeviceInstanceKey getAccountDeviceInstanceKey() { + AccountDeviceInstanceKey getAccountDeviceInstanceKey() { return accountDeviceInstanceKey; } - public CommunicationsManager getCommsManager() { + CommunicationsManager getCommsManager() { return commsManager; } - public long getMessageCount() { + long getMessageCount() { return accountDeviceInstanceKey.getMessageCount(); } - public CommunicationsFilter getFilter() { + CommunicationsFilter getFilter() { return accountDeviceInstanceKey.getCommunicationsFilter(); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 8d6407fe02..828d654298 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -70,7 +70,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } - protected void init(ExplorerManager tableExplorerManager) { + void init(ExplorerManager tableExplorerManager) { this.accountsTableEM = tableExplorerManager; tableExplorerManager.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index a8e75fffe9..663b711c99 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,14 +21,17 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.EventBus; /** - * Provide he singleton EventBus. + * Provide the singleton EventBus. */ final class CVTEvents { private final static EventBus cvtEventBus = new EventBus(); - public static EventBus getCVTEventBus() { + static EventBus getCVTEventBus() { return cvtEventBus; } + private CVTEvents() { + } + } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index 8fe4873f54..7d0146715b 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -57,7 +57,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * context-sensitive actions. */ @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) - public MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java index 1c4ea0f7b4..ac54faa6ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageDataContent.java @@ -26,7 +26,7 @@ import org.sleuthkit.autopsy.corecomponentinterfaces.DataContent; * Extends MessageContentViewer so that it implements DataContent and can be set * as the only ContentViewer for a DataResultPanel */ -public class MessageDataContent extends MessageContentViewer implements DataContent { +final class MessageDataContent extends MessageContentViewer implements DataContent { private static final long serialVersionUID = 1L; diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java index e77ab3f35d..c8ba063adb 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java @@ -11,11 +11,11 @@ import java.util.Collection; /** * */ -class PinAccountEvent { +final class PinAccountEvent { private final ImmutableSet accountDeviceInstances; - public ImmutableSet getAccountDeviceInstances() { + ImmutableSet getAccountDeviceInstances() { return accountDeviceInstances; } diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 31f5871b81..fff7dfa9ad 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -43,11 +43,11 @@ import org.sleuthkit.datamodel.TskCoreException; /** * Node for a relationship, as represented by a BlackboardArtifact. */ -public class RelationshipNode extends BlackboardArtifactNode { +final class RelationshipNode extends BlackboardArtifactNode { private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); - public RelationshipNode(BlackboardArtifact artifact) { + RelationshipNode(BlackboardArtifact artifact) { super(artifact); final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 320e7d493b..678b6eabac 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -24,6 +24,7 @@ import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -86,7 +87,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; - protected void setFilterProvider(FilterProvider filterProvider) { + void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; } private FilterProvider filterProvider; @@ -102,8 +103,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setDisconnectOnMove(false); graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); + graph.setAutoOrigin(true); graphComponent = new mxGraphComponent(graph); graphComponent.setAutoScroll(true); + graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); @@ -249,7 +252,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Override - public void removeNotify() { super.removeNotify(); // IngestManager.getInstance().removeIngestModuleEventListener(ingestListener); @@ -316,6 +318,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void applyOrganicLayout() { new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + + mxGraphView view = graphComponent.getGraph().getView(); + int compLen = graphComponent.getWidth(); + int viewLen = (int) view.getGraphBounds().getWidth(); + view.setScale((double) compLen / viewLen * view.getScale()); graphComponent.zoomAndCenter(); } From 64aa3bb929bdcb8ba4d2b25ba70065d7fa17c367 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 11 Jan 2018 17:17:37 +0100 Subject: [PATCH 014/347] first pass at pinning from the VisualizationPanel --- .../communications/VisualizationPanel.java | 87 ++++++++++++------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 7563e97a29..2dd1ecbaff 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -25,19 +25,22 @@ import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; +import com.mxgraph.model.mxICell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; import java.util.Arrays; import java.util.Collection; +import static java.util.Collections.singleton; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; @@ -46,11 +49,15 @@ import java.util.Map; import java.util.Random; import java.util.Set; import java.util.logging.Level; +import javax.swing.AbstractAction; import javax.swing.JButton; +import javax.swing.JMenuItem; import javax.swing.JPanel; +import javax.swing.JPopupMenu; import javax.swing.JSplitPane; import javax.swing.JToolBar; import javax.swing.SwingConstants; +import javax.swing.SwingUtilities; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; @@ -128,39 +135,45 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - - graphComponent = new mxGraphComponent(graph){ + graphComponent = new mxGraphComponent(graph) { @Override public boolean isPanningEvent(MouseEvent event) { - return true; //To change body of generated methods, choose Tools | Templates. + return true; } - + }; + + graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); -// graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { -// @Override -// public void mouseReleased(MouseEvent e) { -// graphComponent.setPanning(false); -// } -// -// @Override -// public void mouseMoved(MouseEvent e) { -// super.mouseMoved(e); -// if (graphComponent.isPanning()) { -// graphComponent.getGraphControl().setTranslate(e.getPoint()); -// } -// } -// -// @Override -// public void mousePressed(MouseEvent e) { -// graphComponent.setPanning(true); -// } -// }); + graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + super.mouseClicked(e); + if (SwingUtilities.isRightMouseButton(e)) { + mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); + if (cellAt != null && cellAt.isVertex()) { + JPopupMenu jPopupMenu = new JPopupMenu(); + jPopupMenu.add(new JMenuItem() { + { + setAction(new AbstractAction("Pin Account " + cellAt.getId()) { + @Override + public void actionPerformed(ActionEvent e) { + pinAccounts(new PinAccountEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + } + }); + } + }); + + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); + } + } + } + }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); CVTEvents.getCVTEventBus().register(this); @@ -446,23 +459,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void applyOrganicLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxOrganicLayout(graph).execute(graph.getDefaultParent()); - mxGraphView view = graphComponent.getGraph().getView(); - int compLen = graphComponent.getWidth(); - int viewLen = (int) view.getGraphBounds().getWidth(); - view.setScale((double) compLen / viewLen * view.getScale()); - graphComponent.zoomAndCenter(); + fitGraph(); } - private void applyOrthogonalLayout() { + private void fitGraph() { +// mxGraphView view = graphComponent.getGraph().getView(); +// int compLen = graphComponent.getWidth(); +// int viewLen = (int) view.getGraphBounds().getWidth(); +// view.setScale((double) compLen / viewLen * view.getScale()); + + } + + private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + fitGraph(); } - private void applyHierarchicalLayout() { + private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); - graphComponent.zoomAndCenter(); + fitGraph(); } From ed19487607b320a0f272eb9f9fc5f66dcdbfcfab Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 13:11:08 +0100 Subject: [PATCH 015/347] distinguish pin events that should replace the pinned nodes, and those that should append to them. --- .../AccountDeviceInstanceNode.java | 7 ++- .../autopsy/communications/CVTEvents.java | 20 +++++++ .../communications/CVTTopComponent.java | 3 +- .../communications/PinAccountEvent.java | 25 -------- .../communications/VisualizationPanel.java | 54 ++++++++++-------- .../images/icons8-neural-network.png | Bin 0 -> 1046 bytes 6 files changed, 58 insertions(+), 51 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 5b88b01999..6b60d75935 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -24,6 +24,7 @@ import java.util.Arrays; import java.util.Collection; import javax.swing.AbstractAction; import javax.swing.Action; +import javax.swing.ImageIcon; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; @@ -113,20 +114,22 @@ final class AccountDeviceInstanceNode extends AbstractNode { private static final long serialVersionUID = 1L; private static PinAccountsAction instance = new PinAccountsAction(); + static final private ImageIcon imageIcon = + new ImageIcon("images/icons8-neural-network.png"); private static PinAccountsAction getInstance() { return instance; } private PinAccountsAction() { - super("Visualize Account"); + super("Visualize Account", imageIcon); } @Override public void actionPerformed(ActionEvent e) { Collection lookupAll = Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); - CVTEvents.getCVTEventBus().post(new PinAccountEvent(lookupAll)); + CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, true)); } } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index 663b711c99..6c0001fd21 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; +import java.util.Collection; /** * Provide the singleton EventBus. @@ -34,4 +36,22 @@ final class CVTEvents { private CVTEvents() { } + static final class PinAccountsEvent { + + private final ImmutableSet accountDeviceInstances; + private final boolean replace; + + public boolean isReplace() { + return replace; + } + + ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; + } + + PinAccountsEvent(Collection accountDeviceInstances, boolean replace) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); + this.replace = replace; + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index d19ad25496..a7c55ab631 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -75,10 +75,11 @@ public final class CVTTopComponent extends TopComponent { } @Subscribe - public void pinAccount(PinAccountEvent pinEvent) { + public void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); } + /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java b/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java deleted file mode 100644 index c8ba063adb..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/PinAccountEvent.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * To change this license header, choose License Headers in Project Properties. - * To change this template file, choose Tools | Templates - * and open the template in the editor. - */ -package org.sleuthkit.autopsy.communications; - -import com.google.common.collect.ImmutableSet; -import java.util.Collection; - -/** - * - */ -final class PinAccountEvent { - - private final ImmutableSet accountDeviceInstances; - - ImmutableSet getAccountDeviceInstances() { - return accountDeviceInstances; - } - - PinAccountEvent(Collection accountDeviceInstances) { - this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); - } -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 2dd1ecbaff..39fc676f80 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -50,6 +50,7 @@ import java.util.Random; import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractAction; +import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JMenuItem; import javax.swing.JPanel; @@ -88,7 +89,10 @@ import org.sleuthkit.datamodel.TskCoreException; final public class VisualizationPanel extends JPanel implements Lookup.Provider { private static final long serialVersionUID = 1L; - private Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); + private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); + + static final private ImageIcon imageIcon = + new ImageIcon("images/icons8-neural-network.png"); static final private mxStylesheet mxStylesheet = new mxStylesheet(); @@ -143,7 +147,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }; - + graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); graphComponent.setOpaque(true); @@ -158,12 +162,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { JPopupMenu jPopupMenu = new JPopupMenu(); - jPopupMenu.add(new JMenuItem() { + jPopupMenu.add(new JMenuItem(imageIcon) { { setAction(new AbstractAction("Pin Account " + cellAt.getId()) { @Override public void actionPerformed(ActionEvent e) { - pinAccounts(new PinAccountEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } }); } @@ -215,20 +219,18 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - mxCell vertex = nodeMap.get(name); - if (vertex == null) { + return nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - vertex = (mxCell) graph.insertVertex( + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), - name, accountDeviceInstanceKey, + vertexName, accountDeviceInstanceKey, new Random().nextInt(200), new Random().nextInt(200), size, size); - graph.getView().getState(vertex, true).setLabel(name); - nodeMap.put(name, vertex); - } - return vertex; + graph.getView().getState(vertex, true).setLabel(vertexName); + return vertex; + }); } @SuppressWarnings("unchecked") @@ -246,17 +248,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void pinAccounts(PinAccountEvent pinEvent) { + public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { final Set adiKeys = pinEvent.getAccountDeviceInstances(); final CommunicationsFilter commsFilter = filterProvider.getFilter(); graph.getModel().beginUpdate(); try { - nodeMap.clear(); - edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); - + if (pinEvent.isReplace()) { + clearGraph(); + } else { + } for (AccountDeviceInstanceKey adiKey : adiKeys) { mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); @@ -288,6 +290,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider revalidate(); } + private void clearGraph() { + nodeMap.clear(); + edgeMap.clear(); + graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + } + @Override public void addNotify() { super.addNotify(); @@ -305,9 +313,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { graph.getModel().beginUpdate(); try { - nodeMap.clear(); - edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + clearGraph(); } finally { graph.getModel().endUpdate(); } @@ -459,7 +465,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void applyOrganicLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrganicLayout(graph).execute(graph.getDefaultParent()); @@ -474,13 +480,15 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } - private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + private void applyOrthogonalLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); fitGraph(); } - private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + private void applyHierarchicalLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); fitGraph(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png b/Core/src/org/sleuthkit/autopsy/communications/images/icons8-neural-network.png new file mode 100644 index 0000000000000000000000000000000000000000..331be052314b9451e92d0378ce954e9a4ea56e4c GIT binary patch literal 1046 zcmV+x1nK*UP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1FK0yK~z{r#g|P? z6HyR=F?uk1(3?L|6Au_K8gC|g;6i^ev<*K6^dLs?00%+68I>4*T1va6g##*r33?$) zqQMv~2}av)yKf8SPeBv^BD9qf3Q3JbrM_8aYpHE1hJ`PgWZ%y0%-c7!@6Bec1-Ui+ zwwwm{p{$zzEqH7F&^8nEnbv{nOAp=zv{pV)z>rc@NlzZ*F2B+9e(uBUV&r3JF)|+f z6)C$j6jU16qi7;6B~}kNIQ2saF3RJfnMk3PG@*&KZ1@E7n@L%G3eMm526J@IBWNN$ zS_?6jv<`%;?~jFO5&H8?p-@Q0Sdry!?N%%>6s%jgDN9Y(2jN zZ?8ypqkXSj=j_E{Qd0WY7`YS0R*#Q){%IZruHhc4Q=-thGzvX?6Nd>&?LQLPRA}M9 z3{L+2oZ!01LOz3T$tXDx943Loe_6@DtjTq-x@*iYDn+^2H5vdNx><(2mfNpp_T#nSXfnFaRKnkusnwU!~1eV5BhAVx3iGXyZ&U;42A1LX5BaV1i QRR91007*qoM6N<$g5AQ^PXGV_ literal 0 HcmV?d00001 From 330d9ad4f6f95b7da1400b84b3a9ef0bbacd6c63 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 15:49:05 +0100 Subject: [PATCH 016/347] simplify panning (use default ctrl+shift + drag) reinstate fiting in pinning, sore pinned nodes. --- .../communications/VisualizationPanel.java | 51 ++++++++----------- 1 file changed, 22 insertions(+), 29 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 39fc676f80..bc686fcd47 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,7 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; -import com.mxgraph.layout.mxOrganicLayout; +import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; @@ -30,6 +30,7 @@ import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -46,7 +47,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.logging.Level; import javax.swing.AbstractAction; @@ -115,6 +115,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; + private final HashSet pinnedAccountDevices = new HashSet<>(); void setFilterProvider(FilterProvider filterProvider) { this.filterProvider = filterProvider; @@ -139,17 +140,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); - graphComponent = new mxGraphComponent(graph) { - @Override - public boolean isPanningEvent(MouseEvent event) { - return true; - } - - }; - - graphComponent.setAutoExtend(true); - graphComponent.setAutoScroll(true); - + graphComponent = new mxGraphComponent(graph); + graphComponent.setConnectable(false); + graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); @@ -164,7 +157,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider JPopupMenu jPopupMenu = new JPopupMenu(); jPopupMenu.add(new JMenuItem(imageIcon) { { - setAction(new AbstractAction("Pin Account " + cellAt.getId()) { + setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { @Override public void actionPerformed(ActionEvent e) { pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); @@ -219,18 +212,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - return nodeMap.computeIfAbsent(name, vertexName -> { + final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, - new Random().nextInt(200), - new Random().nextInt(200), + 0, + 0, size, size); graph.getView().getState(vertex, true).setLabel(vertexName); return vertex; }); + return computeIfAbsent; } @SuppressWarnings("unchecked") @@ -250,28 +245,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { - final Set adiKeys = pinEvent.getAccountDeviceInstances(); final CommunicationsFilter commsFilter = filterProvider.getFilter(); graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { + pinnedAccountDevices.clear(); clearGraph(); - } else { } - for (AccountDeviceInstanceKey adiKey : adiKeys) { + pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances()); + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); - long communicationsCount = relationships.size(); AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, commsFilter, communicationsCount); + new AccountDeviceInstanceKey(relatedADI, commsFilter, adiRelationshipsCount); mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); for (BlackboardArtifact relationship : relationships) { addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); @@ -467,17 +461,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void applyOrganicLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); - new mxOrganicLayout(graph).execute(graph.getDefaultParent()); + new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); fitGraph(); } private void fitGraph() { -// mxGraphView view = graphComponent.getGraph().getView(); -// int compLen = graphComponent.getWidth(); -// int viewLen = (int) view.getGraphBounds().getWidth(); -// view.setScale((double) compLen / viewLen * view.getScale()); - + mxGraphView view = graphComponent.getGraph().getView(); + int compLen = graphComponent.getWidth(); + int viewLen = (int) view.getGraphBounds().getWidth(); + view.setScale((double) compLen / viewLen * view.getScale()); } private void applyOrthogonalLayout() { From 79194a2758a796901e2582d78ea63a9475da5534 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 12 Jan 2018 16:25:11 +0100 Subject: [PATCH 017/347] wire filters via the Eventbus instead of by passing a reference to the ExplorerManager --- .../communications/AccountsBrowser.java | 31 ++++++-- .../autopsy/communications/CVTEvents.java | 15 ++++ .../communications/CVTTopComponent.java | 22 ++---- .../communications/FilterProvider.java | 26 ------- .../autopsy/communications/FiltersPanel.java | 37 ++------- .../communications/VisualizationPanel.java | 75 +++++++++++-------- 6 files changed, 96 insertions(+), 110 deletions(-) delete mode 100644 Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java index 828d654298..b1d705f6e7 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountsBrowser.java @@ -18,7 +18,9 @@ */ package org.sleuthkit.autopsy.communications; +import com.google.common.eventbus.Subscribe; import java.awt.Component; +import java.util.logging.Level; import javax.swing.JPanel; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; @@ -27,8 +29,14 @@ import org.netbeans.swing.outline.DefaultOutlineModel; import org.netbeans.swing.outline.Outline; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.Children; import org.openide.util.Lookup; import org.openide.util.lookup.ProxyLookup; +import org.sleuthkit.autopsy.casemodule.Case; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.TskCoreException; /** * A panel that goes in the Browse tab of the Communications Visualization Tool. @@ -42,17 +50,18 @@ import org.openide.util.lookup.ProxyLookup; public final class AccountsBrowser extends JPanel implements ExplorerManager.Provider, Lookup.Provider { private static final long serialVersionUID = 1L; + private static final Logger logger = Logger.getLogger(AccountsBrowser.class.getName()); private final Outline outline; private final ExplorerManager messageBrowserEM = new ExplorerManager(); - private ExplorerManager accountsTableEM; + private final ExplorerManager accountsTableEM = new ExplorerManager(); /* * This lookup proxies the selection lookup of both he accounts table and * the messages table. */ - private ProxyLookup proxyLookup; + private final ProxyLookup proxyLookup; public AccountsBrowser() { initComponents(); @@ -68,11 +77,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro outline.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); outline.setColumnSorted(3, false, 1); //it would be nice if the column index wasn't hardcoded - } - - void init(ExplorerManager tableExplorerManager) { - this.accountsTableEM = tableExplorerManager; - tableExplorerManager.addPropertyChangeListener(evt -> { + accountsTableEM.addPropertyChangeListener(evt -> { if (ExplorerManager.PROP_ROOT_CONTEXT.equals(evt.getPropertyName())) { SwingUtilities.invokeLater(this::setColumnWidths); } else if (ExplorerManager.PROP_EXPLORED_CONTEXT.equals(evt.getPropertyName())) { @@ -80,7 +85,7 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } }); - jSplitPane1.setRightComponent(new MessageBrowser(tableExplorerManager, messageBrowserEM)); + jSplitPane1.setRightComponent(new MessageBrowser(accountsTableEM, messageBrowserEM)); proxyLookup = new ProxyLookup( ExplorerUtils.createLookup(messageBrowserEM, getActionMap()), @@ -111,6 +116,16 @@ public final class AccountsBrowser extends JPanel implements ExplorerManager.Pro } } + @Subscribe + public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + try { + final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); + accountsTableEM.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, filterChangeEvent.getNewFilter()), true))); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); + } + } + /** * This method is called from within the constructor to initialize the form. * WARNING: Do NOT modify this code. The content of this method is always diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index 6c0001fd21..f13128f5c3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; import java.util.Collection; +import org.sleuthkit.datamodel.CommunicationsFilter; /** * Provide the singleton EventBus. @@ -36,6 +37,20 @@ final class CVTEvents { private CVTEvents() { } + static final class FilterChangeEvent { + + private final CommunicationsFilter newFilter; + + CommunicationsFilter getNewFilter() { + return newFilter; + } + + FilterChangeEvent(CommunicationsFilter newFilter) { + this.newFilter = newFilter; + } + + } + static final class PinAccountsEvent { private final ImmutableSet accountDeviceInstances; diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index a7c55ab631..39361de47e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -21,7 +21,6 @@ package org.sleuthkit.autopsy.communications; import com.google.common.eventbus.Subscribe; import java.util.List; import java.util.stream.Collectors; -import org.openide.explorer.ExplorerManager; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -42,22 +41,13 @@ public final class CVTTopComponent extends TopComponent { private static final long serialVersionUID = 1L; - private final ExplorerManager filterToTableEXplorerManager = new ExplorerManager(); - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) public CVTTopComponent() { initComponents(); setName(Bundle.CVTTopComponent_name()); - /* - * Connect the filtersPane and the accountsBrowser via a shared - * ExplorerMmanager - */ - filtersPane.setExplorerManager(filterToTableEXplorerManager); - accountsBrowser.init(filterToTableEXplorerManager); - /* - * Associate an Lookup with the GlobalActionContext (GAC) so that + * Associate a Lookup with the GlobalActionContext (GAC) so that * selections in the sub views can be exposed to context-sensitive * actions. */ @@ -69,9 +59,14 @@ public final class CVTTopComponent extends TopComponent { proxyLookup.changeLookups(selectedComponent.getLookup()); }); - vizPanel.setFilterProvider(filtersPane); - CVTEvents.getCVTEventBus().register(this); + /* + * Connect the filtersPane to the accountsBrowser and visualizaionPanel + * via an Eventbus + */ + CVTEvents.getCVTEventBus().register(this); + CVTEvents.getCVTEventBus().register(vizPanel); + CVTEvents.getCVTEventBus().register(accountsBrowser); } @Subscribe @@ -79,7 +74,6 @@ public final class CVTTopComponent extends TopComponent { browseVisualizeTabPane.setSelectedIndex(1); } - /** * 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 diff --git a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java b/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java deleted file mode 100644 index 54f48f9dd1..0000000000 --- a/Core/src/org/sleuthkit/autopsy/communications/FilterProvider.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Autopsy Forensic Browser - * - * Copyright 2018 Basis Technology Corp. - * Contact: carrier sleuthkit 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.communications; - -import org.sleuthkit.datamodel.CommunicationsFilter; - -interface FilterProvider { - - CommunicationsFilter getFilter(); -} diff --git a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java index 24e589290c..36232eaf15 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/FiltersPanel.java @@ -31,9 +31,6 @@ import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.JCheckBox; import javax.swing.JPanel; -import org.openide.explorer.ExplorerManager; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.Children; import org.openide.util.NbBundle; import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; @@ -49,7 +46,6 @@ import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsFilter.AccountTypeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DateRangeFilter; import org.sleuthkit.datamodel.CommunicationsFilter.DeviceFilter; -import org.sleuthkit.datamodel.CommunicationsManager; import org.sleuthkit.datamodel.DataSource; import static org.sleuthkit.datamodel.Relationship.Type.CALL_LOG; import static org.sleuthkit.datamodel.Relationship.Type.MESSAGE; @@ -60,13 +56,11 @@ import org.sleuthkit.datamodel.TskCoreException; * Panel that holds the Filter control widgets and triggers queries against the * CommunicationsManager on user filtering changes. */ -final public class FiltersPanel extends JPanel implements FilterProvider { +final public class FiltersPanel extends JPanel { private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(FiltersPanel.class.getName()); - private ExplorerManager em; - @ThreadConfined(type = ThreadConfined.ThreadType.AWT) private final Map accountTypeMap = new HashMap<>(); @ThreadConfined(type = ThreadConfined.ThreadType.AWT) @@ -134,10 +128,6 @@ final public class FiltersPanel extends JPanel implements FilterProvider { refreshButton.addActionListener(e -> applyFilters()); } - void setExplorerManager(ExplorerManager explorerManager) { - em = explorerManager; - } - /** * Validate that filters are in a consistent state and will result in some * results. Checks that at least one device and at least one account type is @@ -161,9 +151,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider { */ void updateAndApplyFilters() { updateFilters(); - if (em != null) { - applyFilters(); - } + applyFilters(); } private void updateTimeZone() { @@ -224,8 +212,7 @@ final public class FiltersPanel extends JPanel implements FilterProvider { return jCheckBox; }); } - } - ); + }); } /** @@ -490,27 +477,15 @@ final public class FiltersPanel extends JPanel implements FilterProvider { }// //GEN-END:initComponents /** - * Push a new root AccountDeviceInstanceNodeFactory with he current filters - * into the explorer manager. The factory will do he actual queries. - * - * + * Post an event with the new filters. */ private void applyFilters() { - CommunicationsFilter commsFilter = getFilter(); - - try { - final CommunicationsManager commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - em.setRootContext(new AbstractNode(Children.create(new AccountDeviceInstanceNodeFactory(commsManager, commsFilter), true))); - } catch (TskCoreException ex) { - logger.log(Level.SEVERE, "There was an error getting the CommunicationsManager for the current case.", ex); - } - + CVTEvents.getCVTEventBus().post(new CVTEvents.FilterChangeEvent(getFilter())); needsRefresh = false; validateFilters(); } - @Override - public CommunicationsFilter getFilter() { + private CommunicationsFilter getFilter() { CommunicationsFilter commsFilter = new CommunicationsFilter(); commsFilter.addAndFilter(getDeviceFilter()); commsFilter.addAndFilter(getAccountTypeFilter()); diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index bc686fcd47..d202b54ee8 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -64,7 +64,6 @@ import org.openide.explorer.ExplorerUtils; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Node; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.NbBundle; import org.openide.util.lookup.ProxyLookup; @@ -116,11 +115,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); - - void setFilterProvider(FilterProvider filterProvider) { - this.filterProvider = filterProvider; - } - private FilterProvider filterProvider; + private CommunicationsFilter currentFilter; public VisualizationPanel() { initComponents(); @@ -160,7 +155,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { @Override public void actionPerformed(ActionEvent e) { - pinAccounts(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } }); } @@ -173,7 +168,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); - CVTEvents.getCVTEventBus().register(this); graph.setStylesheet(mxStylesheet); @@ -243,9 +237,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void pinAccounts(CVTEvents.PinAccountsEvent pinEvent) { - - final CommunicationsFilter commsFilter = filterProvider.getFilter(); + public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { @@ -254,36 +246,57 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider clearGraph(); } pinnedAccountDevices.addAll(pinEvent.getAccountDeviceInstances()); - for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); - - List relatedAccountDeviceInstances = - commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), commsFilter); - - for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { - long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, commsFilter); - List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, commsFilter); - - AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, commsFilter, adiRelationshipsCount); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (BlackboardArtifact relationship : relationships) { - addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); - } - } - } + rebuildGraph(); } catch (TskCoreException ex) { - Exceptions.printStackTrace(ex); + logger.log(Level.SEVERE, "Error pinning accounts", ex); } finally { // Updates the display graph.getModel().endUpdate(); - } applyOrganicLayout(); revalidate(); } + @Subscribe + public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + graph.getModel().beginUpdate(); + try { + clearGraph(); + currentFilter = filterChangeEvent.getNewFilter(); + rebuildGraph(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error filtering accounts", ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + + applyOrganicLayout(); + revalidate(); + } + + private void rebuildGraph() throws TskCoreException { + for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { + mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + + List relatedAccountDeviceInstances = + commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + + for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { + long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); + List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + + AccountDeviceInstanceKey relatedADIKey = + new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); + for (BlackboardArtifact relationship : relationships) { + addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); + } + } + } + } + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); From b3faf438af043385aca74088a97724adab790cea Mon Sep 17 00:00:00 2001 From: millmanorama Date: Mon, 15 Jan 2018 17:06:46 +0100 Subject: [PATCH 018/347] fix filtering begin adding rubberband and multiselect --- .../communications/VisualizationPanel.java | 94 ++++++++++++------- 1 file changed, 62 insertions(+), 32 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index d202b54ee8..ff25a753a0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -26,8 +26,11 @@ import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; +import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxEventObject; +import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; @@ -49,6 +52,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; +import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -116,6 +120,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; + private final mxRubberband rubberband; public VisualizationPanel() { initComponents(); @@ -132,16 +137,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setEdgeLabelsMovable(false); graph.setVertexLabelsMovable(false); graph.setAllowDanglingEdges(false); - graph.setCellsDeletable(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); + graph.setStylesheet(mxStylesheet); graphComponent = new mxGraphComponent(graph); + graphComponent.setAutoExtend(true); + graphComponent.setAutoScroll(true); + graphComponent.setAutoscrolls(true); graphComponent.setConnectable(false); graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); + //install rubber band selection handler + rubberband = new mxRubberband(graphComponent); + + //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { @Override public void mouseClicked(MouseEvent e) { @@ -169,32 +181,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); - graph.setStylesheet(mxStylesheet); - - graph.getSelectionModel().addListener(null, (sender, evt) -> { - Object[] selectionCells = graph.getSelectionCells(); - if (selectionCells.length == 1) { - mxCell selectionCell = (mxCell) selectionCells[0]; - try { - - if (selectionCell.isVertex()) { - final AccountDeviceInstanceNode accountDeviceInstanceNode = - new AccountDeviceInstanceNode(((AccountDeviceInstanceKey) selectionCell.getValue()), - commsManager); - vizEM.setRootContext(SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode)); - vizEM.setSelectedNodes(new Node[]{accountDeviceInstanceNode}); - - } else if (selectionCell.isEdge()) { - @SuppressWarnings("unchecked") - AbstractNode abstractNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory((Set) selectionCell.getValue()), true)); - vizEM.setRootContext(abstractNode); - vizEM.setSelectedNodes(new Node[]{abstractNode}); - } - } catch (PropertyVetoException ex) { - logger.log(Level.SEVERE, "Account selection vetoed.", ex); - } - } - }); + //feed selection to explorermanager + graph.getSelectionModel().addListener(null, new SelectionListener()); } @Override @@ -238,7 +226,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider @Subscribe public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { - graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -255,11 +242,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } applyOrganicLayout(); - revalidate(); } @Subscribe public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + graph.getModel().beginUpdate(); try { clearGraph(); @@ -273,7 +260,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } applyOrganicLayout(); - revalidate(); } private void rebuildGraph() throws TskCoreException { @@ -300,7 +286,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private void clearGraph() { nodeMap.clear(); edgeMap.clear(); - graph.removeCells(graph.getChildCells(graph.getDefaultParent(), true, true)); + graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } @Override @@ -483,7 +469,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxGraphView view = graphComponent.getGraph().getView(); int compLen = graphComponent.getWidth(); int viewLen = (int) view.getGraphBounds().getWidth(); - view.setScale((double) compLen / viewLen * view.getScale()); +// view.setScale((double) compLen / viewLen * view.getScale()); + } private void applyOrthogonalLayout() { @@ -525,4 +512,47 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider super(children); } } + + private class SelectionListener implements mxEventSource.mxIEventListener { + + @Override + + public void invoke(Object sender, mxEventObject evt) { + Object[] selectionCells = graph.getSelectionCells(); + if (selectionCells.length == 0) { + vizEM.setRootContext(Node.EMPTY); + } else { + Node rootNode; + Node[] selectedNodes; + mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); + + if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { + HashSet adis = new HashSet<>(); + for (mxICell vertex : selectedCells) { + adis.add(((AccountDeviceInstanceKey) vertex.getValue()).getAccountDeviceInstance()); + } + + final AccountDetailsNode accountDeviceInstanceNode = + new AccountDetailsNode(adis, currentFilter, commsManager); + rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); + selectedNodes = new Node[]{accountDeviceInstanceNode}; + + } else { + HashSet relationshipArtifacts = new HashSet<>(); + for (mxICell edge : selectedCells) { + relationshipArtifacts.addAll((Set) edge.getValue()); + } + + rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); + selectedNodes = new Node[]{rootNode}; + } + vizEM.setRootContext(rootNode); + try { + vizEM.setSelectedNodes(selectedNodes); + } catch (PropertyVetoException ex) { + logger.log(Level.SEVERE, "Account selection vetoed.", ex); + } + } + } + } } From fcd170a393bd88517785b8f5c0f969c429726367 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 12:57:09 +0100 Subject: [PATCH 019/347] refactoring multi select in MessageBrowser --- .../communications/AccountDetailsNode.java | 5 ++ .../AccountDeviceInstanceNode.java | 1 + .../autopsy/communications/Bundle.properties | 1 + .../communications/MessageBrowser.java | 66 ++++++++----------- .../communications/VisualizationPanel.form | 13 ++++ .../communications/VisualizationPanel.java | 50 ++++++++++---- 6 files changed, 85 insertions(+), 51 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java index 7bc1a0faae..39eae2b9f3 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDetailsNode.java @@ -25,6 +25,7 @@ import org.openide.nodes.AbstractNode; import org.openide.nodes.ChildFactory; import org.openide.nodes.Children; import org.openide.nodes.Node; +import org.python.google.common.collect.Iterables; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; import org.sleuthkit.datamodel.BlackboardArtifact; @@ -44,6 +45,10 @@ final class AccountDetailsNode extends AbstractNode { AccountDetailsNode(Set accountDeviceInstances, CommunicationsFilter filter, CommunicationsManager commsManager) { super(Children.create(new AccountRelationshipChildren(accountDeviceInstances, commsManager, filter), true)); + String displayName = (accountDeviceInstances.size() == 1) + ? Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID() + : accountDeviceInstances.size() + " accounts"; + setDisplayName(displayName); } /** diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index 6b60d75935..c0208b1bce 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -52,6 +52,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { this.commsManager = commsManager; this.account = accountDeviceInstanceKey.getAccountDeviceInstance().getAccount(); setName(account.getTypeSpecificID()); + setDisplayName(getName()); setIconBaseWithExtension("org/sleuthkit/autopsy/communications/images/" + Utils.getIconFileName(account.getAccountType())); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 71d89548f8..63f58253a0 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -25,3 +25,4 @@ VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy +VisualizationPanel.jButton7.text=Compact Tree diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index d0cbdc6e64..b04f232a29 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -18,7 +18,8 @@ */ package org.sleuthkit.autopsy.communications; -import java.util.Collections; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -31,8 +32,6 @@ import org.sleuthkit.autopsy.corecomponents.DataResultViewerTable; import org.sleuthkit.autopsy.corecomponents.TableFilterNode; import org.sleuthkit.autopsy.directorytree.DataResultFilterNode; import org.sleuthkit.datamodel.AccountDeviceInstance; -import org.sleuthkit.datamodel.CommunicationsFilter; -import org.sleuthkit.datamodel.CommunicationsManager; /** * The right hand side of the CVT. Has a DataResultPanel to show messages and @@ -57,7 +56,7 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov * context-sensitive actions. */ @NbBundle.Messages({"MessageBrowser.DataResultViewerTable.title=Messages"}) - MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { + MessageBrowser(ExplorerManager tableEM, ExplorerManager gacExplorerManager) { this.tableEM = tableEM; this.gacExplorerManager = gacExplorerManager; initComponents(); @@ -69,45 +68,38 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov Bundle.MessageBrowser_DataResultViewerTable_title())); messagesResultPanel.open(); - this.tableEM.addPropertyChangeListener(pce -> { - if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { - final Node[] selectedNodes = this.tableEM.getSelectedNodes(); - - messagesResultPanel.setNumMatches(0); - messagesResultPanel.setNode(null); - - if (selectedNodes.length == 0) { - //reset panel when there is no selection + this.tableEM.addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent pce) { + if (pce.getPropertyName().equals(ExplorerManager.PROP_SELECTED_NODES)) { + final Node[] selectedNodes = MessageBrowser.this.tableEM.getSelectedNodes(); + messagesResultPanel.setNumMatches(0); + messagesResultPanel.setNode(null); messagesResultPanel.setPath(""); - } else { - final Node selectedNode = selectedNodes[0]; - if (selectedNode instanceof AccountDeviceInstanceNode) { - AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNode; - CommunicationsFilter filter = adiNode.getFilter(); - CommunicationsManager commsManager = adiNode.getCommsManager(); - final Set accountDeviceInstances; - - if (selectedNodes.length == 1) { - final AccountDeviceInstance accountDeviceInstance = adiNode.getAccountDeviceInstance(); - accountDeviceInstances = Collections.singleton(accountDeviceInstance); - messagesResultPanel.setPath(accountDeviceInstance.getAccount().getTypeSpecificID()); + if (selectedNodes.length > 0) { + Node rootNode; + final Node selectedNode = selectedNodes[0]; + String path = selectedNode.getDisplayName(); + if (selectedNode instanceof AccountDeviceInstanceNode) { + rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes); } else { - accountDeviceInstances = Stream.of(selectedNodes) - .map(node -> (AccountDeviceInstanceNode) node) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - messagesResultPanel.setPath(selectedNodes.length + " accounts"); + rootNode = selectedNode; } - AccountDetailsNode accountDetailsNode = - new AccountDetailsNode(accountDeviceInstances, filter, commsManager); - TableFilterNode wrappedNode = - new TableFilterNode(new DataResultFilterNode(accountDetailsNode, gacExplorerManager), true); - messagesResultPanel.setNode(wrappedNode); - }else{ - messagesResultPanel.setNode(selectedNode); + messagesResultPanel.setPath(path); + messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true)); } } } + + private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) { + AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; + final Set accountDeviceInstances; + accountDeviceInstances = Stream.of(selectedNodes) + .map(AccountDeviceInstanceNode.class::cast) + .map(AccountDeviceInstanceNode::getAccountDeviceInstance) + .collect(Collectors.toSet()); + return new AccountDetailsNode(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); + } }); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 8a2130752f..da37189ec5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -102,6 +102,19 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index ff25a753a0..1f9fcf66ee 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -22,6 +22,7 @@ import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; +import com.mxgraph.layout.mxCompactTreeLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; @@ -346,6 +347,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton6 = new JButton(); jButton1 = new JButton(); jButton3 = new JButton(); + jButton7 = new JButton(); jButton4 = new JButton(); jButton5 = new JButton(); @@ -402,6 +404,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton3); + jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N + jButton7.setFocusable(false); + jButton7.setHorizontalTextPosition(SwingConstants.CENTER); + jButton7.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton7.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton7ActionPerformed(evt); + } + }); + jToolBar1.add(jButton7); + jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N jButton4.setFocusable(false); jButton4.setHorizontalTextPosition(SwingConstants.CENTER); @@ -457,6 +470,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider applyHierarchicalLayout(); }//GEN-LAST:event_jButton6ActionPerformed + private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed + applyCompactTreeLayout(); + }//GEN-LAST:event_jButton7ActionPerformed + private void applyOrganicLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); @@ -487,6 +504,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider fitGraph(); } + private void applyCompactTreeLayout() { + graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), + graphComponent.getHeight())); + new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); + fitGraph(); + } + // Variables declaration - do not modify//GEN-BEGIN:variables private JButton jButton1; @@ -495,6 +519,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton jButton4; private JButton jButton5; private JButton jButton6; + private JButton jButton7; private JPanel jPanel1; private JToolBar jToolBar1; private JSplitPane splitPane; @@ -516,14 +541,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private class SelectionListener implements mxEventSource.mxIEventListener { @Override - + public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); - if (selectionCells.length == 0) { - vizEM.setRootContext(Node.EMPTY); - } else { - Node rootNode; - Node[] selectedNodes; + Node rootNode = Node.EMPTY; + Node[] selectedNodes = new Node[0]; + if (selectionCells.length > 0) { mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { @@ -537,21 +560,20 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); selectedNodes = new Node[]{accountDeviceInstanceNode}; - } else { + } else if (Stream.of(selectedCells).allMatch(mxICell::isEdge)) { HashSet relationshipArtifacts = new HashSet<>(); for (mxICell edge : selectedCells) { relationshipArtifacts.addAll((Set) edge.getValue()); } - rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); selectedNodes = new Node[]{rootNode}; } - vizEM.setRootContext(rootNode); - try { - vizEM.setSelectedNodes(selectedNodes); - } catch (PropertyVetoException ex) { - logger.log(Level.SEVERE, "Account selection vetoed.", ex); - } + } + vizEM.setRootContext(rootNode); + try { + vizEM.setSelectedNodes(selectedNodes); + } catch (PropertyVetoException ex) { + logger.log(Level.SEVERE, "Selection vetoed.", ex); } } } From 85fb7ee11e06c08fb61cebcde0104b0a03837ee7 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 13:46:28 +0100 Subject: [PATCH 020/347] fix Type column of RelationshipNode --- .../autopsy/communications/RelationshipNode.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java index 27c39d80ec..e952573f50 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/RelationshipNode.java @@ -47,7 +47,7 @@ final class RelationshipNode extends BlackboardArtifactNode { private static final Logger logger = Logger.getLogger(RelationshipNode.class.getName()); - RelationshipNode(BlackboardArtifact artifact) { + RelationshipNode(BlackboardArtifact artifact) { super(artifact); final String stripEnd = StringUtils.stripEnd(artifact.getDisplayName(), "s"); String removeEndIgnoreCase = StringUtils.removeEndIgnoreCase(stripEnd, "message"); @@ -143,4 +143,15 @@ final class RelationshipNode extends BlackboardArtifactNode { return ""; } } + + /** + * Circumvent DataResultFilterNode's slightly odd delegation to + * BlackboardArtifactNode.getSourceName(). + * + * @return the displayName of this Node, which is the type. + */ + @Override + public String getSourceName() { + return getDisplayName(); + } } From f63d76a9673238d39c6efd74e6a1247cb4c2655b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 17:15:55 +0100 Subject: [PATCH 021/347] uniform multiselect using SelectionNode --- .../communications/CVTTopComponent.java | 2 +- .../communications/MessageBrowser.java | 21 +-- .../autopsy/communications/SelectionNode.java | 133 ++++++++++++++++++ .../communications/VisualizationPanel.java | 64 +++------ 4 files changed, 165 insertions(+), 55 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java index 39361de47e..6585f6324a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTTopComponent.java @@ -70,7 +70,7 @@ public final class CVTTopComponent extends TopComponent { } @Subscribe - public void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { + void pinAccount(CVTEvents.PinAccountsEvent pinEvent) { browseVisualizeTabPane.setSelectedIndex(1); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java index b04f232a29..226c0cf508 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java +++ b/Core/src/org/sleuthkit/autopsy/communications/MessageBrowser.java @@ -20,9 +20,8 @@ package org.sleuthkit.autopsy.communications; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; +import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; import javax.swing.JPanel; import org.openide.explorer.ExplorerManager; import org.openide.nodes.Node; @@ -79,26 +78,28 @@ public final class MessageBrowser extends JPanel implements ExplorerManager.Prov if (selectedNodes.length > 0) { Node rootNode; final Node selectedNode = selectedNodes[0]; - String path = selectedNode.getDisplayName(); + if (selectedNode instanceof AccountDeviceInstanceNode) { rootNode = makeRootNodeFromAccountDeviceInstanceNodes(selectedNodes); } else { rootNode = selectedNode; } - messagesResultPanel.setPath(path); + messagesResultPanel.setPath(rootNode.getDisplayName()); messagesResultPanel.setNode(new TableFilterNode(new DataResultFilterNode(rootNode, gacExplorerManager), true)); } } } private Node makeRootNodeFromAccountDeviceInstanceNodes(final Node[] selectedNodes) { + //Use lookup here? AccountDeviceInstanceNode adiNode = (AccountDeviceInstanceNode) selectedNodes[0]; - final Set accountDeviceInstances; - accountDeviceInstances = Stream.of(selectedNodes) - .map(AccountDeviceInstanceNode.class::cast) - .map(AccountDeviceInstanceNode::getAccountDeviceInstance) - .collect(Collectors.toSet()); - return new AccountDetailsNode(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); + + final Set accountDeviceInstances = new HashSet<>(); + for (Node n : selectedNodes) { + //Use lookup here? + accountDeviceInstances.add(((AccountDeviceInstanceNode) n).getAccountDeviceInstance()); + } + return SelectionNode.createFromAccounts(accountDeviceInstances, adiNode.getFilter(), adiNode.getCommsManager()); } }); } diff --git a/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java b/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java new file mode 100644 index 0000000000..40739b49cc --- /dev/null +++ b/Core/src/org/sleuthkit/autopsy/communications/SelectionNode.java @@ -0,0 +1,133 @@ +/* + * Autopsy Forensic Browser + * + * Copyright 2018 Basis Technology Corp. + * Contact: carrier sleuthkit 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.communications; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.logging.Level; +import org.openide.nodes.AbstractNode; +import org.openide.nodes.ChildFactory; +import org.openide.nodes.Children; +import org.openide.nodes.Node; +import org.sleuthkit.autopsy.coreutils.Logger; +import org.sleuthkit.datamodel.AccountDeviceInstance; +import org.sleuthkit.datamodel.BlackboardArtifact; +import org.sleuthkit.datamodel.CommunicationsFilter; +import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; +import org.sleuthkit.datamodel.TskCoreException; + +/** + * 'Root' Node for the Account/Messages area. Represents all the relationships + * that are selected in the AccountsBrowser or the VisualizationPanel. Can be + * populated with AccountDeviceInstance and/or directly with relationships + * (Content). + */ +final class SelectionNode extends AbstractNode { + + private SelectionNode(Children children) { + super(children); + } + + static SelectionNode createFromAccountsAndRelationships( + Set edgeRelationshipArtifacts, + Set accountDeviceInstances, + CommunicationsFilter filter, + CommunicationsManager commsManager) { + + SelectionNode node = new SelectionNode(Children.create( + new RelationshipChildren( + edgeRelationshipArtifacts, + accountDeviceInstances, + commsManager, + filter), + true)); + + //This is not good for internationalization!!! + String name = ""; + final int accounts = accountDeviceInstances.size(); + if (accounts > 1) { + name = accounts + " accounts"; + } else if (accounts == 1) { + name = Iterables.getOnlyElement(accountDeviceInstances).getAccount().getTypeSpecificID(); + } + + final int edges = edgeRelationshipArtifacts.size(); + + if (edges > 0) { + name = name + (name.isEmpty() ? "" : " and ") + edges + " relationship" + (edges > 1 ? "s" : ""); + } + + node.setDisplayName(name); + return node; + } + + static SelectionNode createFromAccounts( + Set accountDeviceInstances, + CommunicationsFilter filter, + CommunicationsManager commsManager) { + + return createFromAccountsAndRelationships(Collections.emptySet(), accountDeviceInstances, filter, commsManager); + } + + /** + * Children object for the relationships that the accounts are part of. + */ + private static class RelationshipChildren extends ChildFactory { + + static final private Logger logger = Logger.getLogger(RelationshipChildren.class.getName()); + + private final Set edgeRelationshipArtifacts; + + private final Set accountDeviceInstances; + + private final CommunicationsManager commsManager; + private final CommunicationsFilter filter; + + private RelationshipChildren(Set selectedEdgeRelationshipSources, Set selecedAccountDeviceInstances, CommunicationsManager commsManager, CommunicationsFilter filter) { + this.edgeRelationshipArtifacts = selectedEdgeRelationshipSources; + this.accountDeviceInstances = selecedAccountDeviceInstances; + this.commsManager = commsManager; + this.filter = filter; + } + + @Override + protected boolean createKeys(List list) { + try { + final Set relationshipSources = commsManager.getRelationshipSources(accountDeviceInstances, filter); + list.addAll(Sets.union(relationshipSources, edgeRelationshipArtifacts)); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error getting communications", ex); + } + return true; + } + + @Override + protected Node createNodeForKey(Content t) { + if (t instanceof BlackboardArtifact) { + return new RelationshipNode((BlackboardArtifact) t); + } else { + throw new UnsupportedOperationException("Cannot create a RelationshipNode for non BlackboardArtifact content."); + } + } + } +} diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 1f9fcf66ee..5db792c344 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -53,7 +53,6 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; -import java.util.stream.Stream; import javax.swing.AbstractAction; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -66,8 +65,6 @@ import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import org.openide.explorer.ExplorerManager; import org.openide.explorer.ExplorerUtils; -import org.openide.nodes.AbstractNode; -import org.openide.nodes.Children; import org.openide.nodes.Node; import org.openide.util.Lookup; import org.openide.util.NbBundle; @@ -76,9 +73,9 @@ import org.sleuthkit.autopsy.casemodule.Case; import static org.sleuthkit.autopsy.casemodule.Case.Events.CURRENT_CASE; import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.datamodel.AccountDeviceInstance; -import org.sleuthkit.datamodel.BlackboardArtifact; import org.sleuthkit.datamodel.CommunicationsFilter; import org.sleuthkit.datamodel.CommunicationsManager; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; /** @@ -116,7 +113,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); - private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); @@ -212,21 +209,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(BlackboardArtifact artifact, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { + private void addEdge(Content relSource, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); if (edgesBetween.length == 0) { final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(artifact)), pinnedAccountVertex, relatedAccountVertex); - edgeMap.put(artifact, edge); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), pinnedAccountVertex, relatedAccountVertex); + edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).add(artifact); + ((Collection) edge.getValue()).add(relSource); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } @Subscribe - public void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { + void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -246,7 +243,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - public void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); try { @@ -272,12 +269,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - List relationships = commsManager.getRelationships(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + List relationships = commsManager.getRelationshipSources(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (BlackboardArtifact relationship : relationships) { + for (Content relationship : relationships) { addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); } } @@ -525,49 +522,28 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JSplitPane splitPane; // End of variables declaration//GEN-END:variables - private static class SimpleParentNode extends AbstractNode { - - private static SimpleParentNode createFromChildNodes(Node... nodes) { - Children.Array array = new Children.Array(); - array.add(nodes); - return new SimpleParentNode(array); - } - - private SimpleParentNode(Children children) { - super(children); - } - } - private class SelectionListener implements mxEventSource.mxIEventListener { @Override + @SuppressWarnings("unchecked") public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); Node rootNode = Node.EMPTY; Node[] selectedNodes = new Node[0]; if (selectionCells.length > 0) { mxICell[] selectedCells = Arrays.asList(selectionCells).toArray(new mxCell[selectionCells.length]); - - if (Stream.of(selectedCells).allMatch(mxICell::isVertex)) { - HashSet adis = new HashSet<>(); - for (mxICell vertex : selectedCells) { - adis.add(((AccountDeviceInstanceKey) vertex.getValue()).getAccountDeviceInstance()); + HashSet relationshipSources = new HashSet<>(); + HashSet adis = new HashSet<>(); + for (mxICell cell : selectedCells) { + if (cell.isEdge()) { + relationshipSources.addAll((Set) cell.getValue()); + } else if (cell.isVertex()) { + adis.add(((AccountDeviceInstanceKey) cell.getValue()).getAccountDeviceInstance()); } - - final AccountDetailsNode accountDeviceInstanceNode = - new AccountDetailsNode(adis, currentFilter, commsManager); - rootNode = SimpleParentNode.createFromChildNodes(accountDeviceInstanceNode); - selectedNodes = new Node[]{accountDeviceInstanceNode}; - - } else if (Stream.of(selectedCells).allMatch(mxICell::isEdge)) { - HashSet relationshipArtifacts = new HashSet<>(); - for (mxICell edge : selectedCells) { - relationshipArtifacts.addAll((Set) edge.getValue()); - } - rootNode = new AbstractNode(Children.create(new RelaionshipSetNodeFactory(relationshipArtifacts), true)); - selectedNodes = new Node[]{rootNode}; } + rootNode = SelectionNode.createFromAccountsAndRelationships(relationshipSources, adis, currentFilter, commsManager); + selectedNodes = new Node[]{rootNode}; } vizEM.setRootContext(rootNode); try { From 055931b81d20143cd15465f99f1f743584306949 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Tue, 16 Jan 2018 22:56:40 +0100 Subject: [PATCH 022/347] Cartesian product WIP --- .../communications/VisualizationPanel.java | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 5db792c344..68c2a1efce 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -20,6 +20,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; +import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCompactTreeLayout; @@ -34,7 +35,6 @@ import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxRectangle; import com.mxgraph.view.mxGraph; -import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -102,6 +102,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); } private final ExplorerManager vizEM = new ExplorerManager(); @@ -209,11 +210,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(Content relSource, mxCell pinnedAccountVertex, mxCell relatedAccountVertex) throws TskCoreException { - Object[] edgesBetween = graph.getEdgesBetween(pinnedAccountVertex, relatedAccountVertex); + private void addEdge(Content relSource, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + mxCell vertex1 = getOrCreateVertex(account1); + mxCell vertex2 = getOrCreateVertex(account2); + Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); if (edgesBetween.length == 0) { - final String edgeName = pinnedAccountVertex.getId() + " <-> " + relatedAccountVertex.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), pinnedAccountVertex, relatedAccountVertex); + final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), vertex1, vertex2); edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; @@ -223,7 +226,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @Subscribe - void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { + void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); try { if (pinEvent.isReplace()) { @@ -239,11 +242,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyHierarchicalLayout(); } @Subscribe - void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { + void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { graph.getModel().beginUpdate(); try { @@ -257,26 +260,34 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyOrganicLayout(); + applyHierarchicalLayout(); } private void rebuildGraph() throws TskCoreException { + + Set allAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - mxCell pinnedAccountVertex = getOrCreateVertex(adiKey); + allAccounts.add(adiKey); List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - List relationships = commsManager.getRelationshipSources(adiKey.getAccountDeviceInstance(), relatedADI, currentFilter); + allAccounts.add(new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount)); + } + } - AccountDeviceInstanceKey relatedADIKey = - new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); - mxCell relatedAccountVertex = getOrCreateVertex(relatedADIKey); - for (Content relationship : relationships) { - addEdge(relationship, pinnedAccountVertex, relatedAccountVertex); - } + Set> cartesianProduct = Sets.cartesianProduct(Arrays.asList(allAccounts, allAccounts)); + for (List pair : cartesianProduct) { + AccountDeviceInstanceKey adiKey1 = pair.get(0); + AccountDeviceInstanceKey adiKey2 = pair.get(1); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + for (Content relationship : relationships) { + addEdge(relationship, adiKey1, adiKey2); } } } @@ -476,36 +487,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.getHeight())); new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } - private void fitGraph() { - mxGraphView view = graphComponent.getGraph().getView(); - int compLen = graphComponent.getWidth(); - int viewLen = (int) view.getGraphBounds().getWidth(); -// view.setScale((double) compLen / viewLen * view.getScale()); - - } private void applyOrthogonalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } private void applyHierarchicalLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } private void applyCompactTreeLayout() { graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), graphComponent.getHeight())); new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - fitGraph(); } From 19f13a0b30dd113abdb6c90845e9588eacd5b682 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 17 Jan 2018 12:04:57 +0100 Subject: [PATCH 023/347] simplify getting relationships to improve graph loading speed. It is still to slow! --- .../autopsy/communications/Bundle.properties | 2 +- .../communications/VisualizationPanel.java | 108 +++++++++++------- 2 files changed, 68 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 63f58253a0..61ee014750 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -25,4 +25,4 @@ VisualizationPanel.jButton3.text=Orthogonal VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy -VisualizationPanel.jButton7.text=Compact Tree +VisualizationPanel.jButton7.text=Circle diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 68c2a1efce..69129f8e4a 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -20,20 +20,22 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.Multimap; import com.google.common.collect.MultimapBuilder; -import com.google.common.collect.Sets; import com.google.common.eventbus.Subscribe; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; -import com.mxgraph.layout.mxCompactTreeLayout; +import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; +import com.mxgraph.layout.mxIGraphLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; +import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; -import com.mxgraph.util.mxRectangle; +import com.mxgraph.util.mxEventSource.mxIEventListener; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; @@ -43,6 +45,7 @@ import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.beans.PropertyVetoException; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import static java.util.Collections.singleton; @@ -199,8 +202,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, - 0, - 0, + Math.random() * graphComponent.getWidth(), + Math.random() * graphComponent.getHeight(), size, size); graph.getView().getState(vertex, true).setLabel(vertexName); @@ -210,17 +213,19 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } @SuppressWarnings("unchecked") - private void addEdge(Content relSource, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { + private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); mxCell vertex2 = getOrCreateVertex(account2); Object[] edgesBetween = graph.getEdgesBetween(vertex1, vertex2); if (edgesBetween.length == 0) { final String edgeName = vertex1.getId() + " <-> " + vertex2.getId(); - mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, new HashSet<>(Arrays.asList(relSource)), vertex1, vertex2); - edgeMap.put(relSource, edge); + final HashSet hashSet = new HashSet<>(relSources); + mxCell edge = (mxCell) graph.insertEdge(graph.getDefaultParent(), edgeName, hashSet, vertex1, vertex2, + "strokeWidth=" + Math.sqrt(hashSet.size())); +// edgeMap.put(relSource, edge); } else if (edgesBetween.length == 1) { final mxCell edge = (mxCell) edgesBetween[0]; - ((Collection) edge.getValue()).add(relSource); + ((Collection) edge.getValue()).addAll(relSources); edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } @@ -242,7 +247,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyHierarchicalLayout(); +// applyOrganicLayout(); } @Subscribe @@ -260,34 +265,46 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.getModel().endUpdate(); } - applyHierarchicalLayout(); +// applyOrganicLayout(); } private void rebuildGraph() throws TskCoreException { - Set allAccounts = new HashSet<>(); + /** + * set to keep track of accounts related to pinned accounts + */ + Set relatedAccounts = new HashSet<>(); for (AccountDeviceInstanceKey adiKey : pinnedAccountDevices) { - allAccounts.add(adiKey); - List relatedAccountDeviceInstances = commsManager.getRelatedAccountDeviceInstances(adiKey.getAccountDeviceInstance(), currentFilter); + //get accounts related to pinned account for (AccountDeviceInstance relatedADI : relatedAccountDeviceInstances) { long adiRelationshipsCount = commsManager.getRelationshipSourcesCount(relatedADI, currentFilter); - allAccounts.add(new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount)); + final AccountDeviceInstanceKey relatedADIKey = new AccountDeviceInstanceKey(relatedADI, currentFilter, adiRelationshipsCount); + + //add and edge between pinned and related accounts + List relationships = commsManager.getRelationshipSources( + adiKey.getAccountDeviceInstance(), + relatedADIKey.getAccountDeviceInstance(), + currentFilter); + addEdge(relationships, adiKey, relatedADIKey); + relatedAccounts.add(relatedADIKey); //store related accounts } } - Set> cartesianProduct = Sets.cartesianProduct(Arrays.asList(allAccounts, allAccounts)); - for (List pair : cartesianProduct) { - AccountDeviceInstanceKey adiKey1 = pair.get(0); - AccountDeviceInstanceKey adiKey2 = pair.get(1); - List relationships = commsManager.getRelationshipSources( - adiKey1.getAccountDeviceInstance(), - adiKey2.getAccountDeviceInstance(), - currentFilter); - for (Content relationship : relationships) { - addEdge(relationship, adiKey1, adiKey2); + //for each pair of related accounts add edges if they are related o each other. + // this is O(n^2) in the number of related accounts!!! + List relatedAccountsList = new ArrayList<>(relatedAccounts); + for (int i = 0; i < relatedAccountsList.size(); i++) { + for (int j = i; j < relatedAccountsList.size(); j++) { + AccountDeviceInstanceKey adiKey1 = relatedAccountsList.get(i); + AccountDeviceInstanceKey adiKey2 = relatedAccountsList.get(j); + List relationships = commsManager.getRelationshipSources( + adiKey1.getAccountDeviceInstance(), + adiKey2.getAccountDeviceInstance(), + currentFilter); + addEdge(relationships, adiKey1, adiKey2); } } } @@ -479,35 +496,44 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton6ActionPerformed private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed - applyCompactTreeLayout(); + morph(new mxCircleLayout(graph)); }//GEN-LAST:event_jButton7ActionPerformed private void applyOrganicLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxFastOrganicLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxFastOrganicLayout(graph)); } - private void applyOrthogonalLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxOrthogonalLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxOrthogonalLayout(graph)); } private void applyHierarchicalLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxHierarchicalLayout(graph).execute(graph.getDefaultParent()); + + morph(new mxHierarchicalLayout(graph));//.execute(graph.getDefaultParent()); } - private void applyCompactTreeLayout() { - graph.setMaximumGraphBounds(new mxRectangle(0, 0, graphComponent.getWidth(), - graphComponent.getHeight())); - new mxCompactTreeLayout(graph).execute(graph.getDefaultParent()); - } + private void morph(mxIGraphLayout layout) { + // layout using morphing + graph.getModel().beginUpdate(); + try { + layout.execute(graph.getDefaultParent()); + } finally { + mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); + morph.addListener(mxEvent.DONE, new mxIEventListener() { + @Override + public void invoke(Object sender, mxEventObject event) { + graph.getModel().endUpdate(); + // fitViewport(); + } + }); + + morph.startAnimation(); + } + } // Variables declaration - do not modify//GEN-BEGIN:variables private JButton jButton1; From f49bf59f9189c4ef69e40bc79835f4b46aa83b4b Mon Sep 17 00:00:00 2001 From: millmanorama Date: Wed, 17 Jan 2018 13:06:31 +0100 Subject: [PATCH 024/347] don't add edges with no relationships --- .../autopsy/communications/Bundle.properties | 3 +- .../communications/VisualizationPanel.form | 13 +++ .../communications/VisualizationPanel.java | 102 +++++++++++------- 3 files changed, 76 insertions(+), 42 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 61ee014750..4935475284 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -15,7 +15,7 @@ FiltersPanel.refreshButton.text=Refresh FiltersPanel.deviceRequiredLabel.text=Select at least one. FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. -VisualizationPanel.jButton1.text=Organic +VisualizationPanel.jButton1.text=Fast Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse @@ -26,3 +26,4 @@ VisualizationPanel.jButton4.text=- VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle +VisualizationPanel.jButton8.text=Organic diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index da37189ec5..2facc5f66e 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -89,6 +89,19 @@ + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 69129f8e4a..6accd8965c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -25,6 +25,7 @@ import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxIGraphLayout; +import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; @@ -91,15 +92,15 @@ import org.sleuthkit.datamodel.TskCoreException; * actions to work correctly. */ final public class VisualizationPanel extends JPanel implements Lookup.Provider { - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - + static final private ImageIcon imageIcon = new ImageIcon("images/icons8-neural-network.png"); - + static final private mxStylesheet mxStylesheet = new mxStylesheet(); - + static { //initialize defaul cell (Vertex and/or Edge) properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); @@ -107,23 +108,23 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); } - + private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); private final ProxyLookup proxyLookup = new ProxyLookup( ExplorerUtils.createLookup(gacEM, getActionMap()), ExplorerUtils.createLookup(vizEM, getActionMap())); - + private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); - + private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; private final mxRubberband rubberband; - + public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -174,31 +175,31 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); } }); - + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } } }); - + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); } - + @Override public Lookup getLookup() { return proxyLookup; } - + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - + mxCell vertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, @@ -211,7 +212,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); return computeIfAbsent; } - + @SuppressWarnings("unchecked") private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); @@ -229,7 +230,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } - + @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); @@ -249,10 +250,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + @Subscribe void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { - + graph.getModel().beginUpdate(); try { clearGraph(); @@ -267,7 +268,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + private void rebuildGraph() throws TskCoreException { /** @@ -304,31 +305,33 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider adiKey1.getAccountDeviceInstance(), adiKey2.getAccountDeviceInstance(), currentFilter); - addEdge(relationships, adiKey1, adiKey2); + if (relationships.size() > 0) { + addEdge(relationships, adiKey1, adiKey2); + } } } } - + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } - + @Override public void addNotify() { super.addNotify(); // IngestManager.getInstance().addIngestModuleEventListener(ingestListener); try { commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - + } catch (IllegalStateException ex) { logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); - + } - + Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { graph.getModel().beginUpdate(); try { @@ -346,10 +349,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } else { commsManager = null; } - + }); } - + @Override public void removeNotify() { super.removeNotify(); @@ -371,6 +374,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jButton2 = new JButton(); jButton6 = new JButton(); jButton1 = new JButton(); + jButton8 = new JButton(); jButton3 = new JButton(); jButton7 = new JButton(); jButton4 = new JButton(); @@ -418,6 +422,17 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton1); + jButton8.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton8.text")); // NOI18N + jButton8.setFocusable(false); + jButton8.setHorizontalTextPosition(SwingConstants.CENTER); + jButton8.setVerticalTextPosition(SwingConstants.BOTTOM); + jButton8.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + jButton8ActionPerformed(evt); + } + }); + jToolBar1.add(jButton8); + jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N jButton3.setFocusable(false); jButton3.setHorizontalTextPosition(SwingConstants.CENTER); @@ -475,11 +490,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }//GEN-LAST:event_jButton2ActionPerformed private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - applyOrganicLayout(); + applyFastOrganicLayout(); }//GEN-LAST:event_jButton1ActionPerformed private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - + applyOrthogonalLayout(); }//GEN-LAST:event_jButton3ActionPerformed @@ -499,22 +514,26 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider morph(new mxCircleLayout(graph)); }//GEN-LAST:event_jButton7ActionPerformed - private void applyOrganicLayout() { - + private void jButton8ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton8ActionPerformed + mxOrganicLayout mxOrganicLayout = new mxOrganicLayout(graph); + mxOrganicLayout.setMaxIterations(10); + morph(mxOrganicLayout); + }//GEN-LAST:event_jButton8ActionPerformed + + private void applyFastOrganicLayout() { morph(new mxFastOrganicLayout(graph)); - } - + private void applyOrthogonalLayout() { - + morph(new mxOrthogonalLayout(graph)); } - + private void applyHierarchicalLayout() { - - morph(new mxHierarchicalLayout(graph));//.execute(graph.getDefaultParent()); + + morph(new mxHierarchicalLayout(graph)); } - + private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); @@ -523,14 +542,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } finally { mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, new mxIEventListener() { - + @Override public void invoke(Object sender, mxEventObject event) { graph.getModel().endUpdate(); // fitViewport(); } }); - + morph.startAnimation(); } } @@ -543,15 +562,16 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private JButton jButton5; private JButton jButton6; private JButton jButton7; + private JButton jButton8; private JPanel jPanel1; private JToolBar jToolBar1; private JSplitPane splitPane; // End of variables declaration//GEN-END:variables private class SelectionListener implements mxEventSource.mxIEventListener { - + @Override - + @SuppressWarnings("unchecked") public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); From 6260c8d842ddf53126237a1d147a0b482a1a86f6 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Thu, 18 Jan 2018 10:26:01 +0100 Subject: [PATCH 025/347] add pinned status icon; actions for adding to pinned accounts, unpinning; refined zooming buttons; refined styling and polish of visualization; improve layout animation --- .../AccountDeviceInstanceNode.java | 33 +- .../autopsy/communications/Bundle.properties | 7 +- .../autopsy/communications/CVTEvents.java | 14 + .../communications/VisualizationPanel.form | 62 ++-- .../communications/VisualizationPanel.java | 304 ++++++++++-------- .../images/icons8-mind-map-16.png | Bin 0 -> 663 bytes .../communications/images/magnifier-left.png | Bin 0 -> 1106 bytes .../images/magnifier-zoom-actual-equal.png | Bin 0 -> 1147 bytes .../images/magnifier-zoom-actual.png | Bin 0 -> 1189 bytes .../images/magnifier-zoom-fit.png | Bin 0 -> 1215 bytes .../images/magnifier-zoom-in-green.png | Bin 0 -> 1271 bytes .../images/magnifier-zoom-in.png | Bin 0 -> 1225 bytes .../images/magnifier-zoom-out-red.png | Bin 0 -> 1053 bytes .../images/magnifier-zoom-out.png | Bin 0 -> 1150 bytes .../communications/images/magnifier-zoom.png | Bin 0 -> 1079 bytes .../communications/images/magnifier.png | Bin 0 -> 615 bytes .../images/magnifier_zoom_in.png | Bin 0 -> 680 bytes .../images/magnifier_zoom_out.png | Bin 0 -> 657 bytes .../communications/images/marker--arrow.png | Bin 0 -> 672 bytes .../images/marker--exclamation.png | Bin 0 -> 695 bytes .../communications/images/marker--minus.png | Bin 0 -> 567 bytes .../communications/images/marker--pencil.png | Bin 0 -> 660 bytes .../communications/images/marker--pin.png | Bin 0 -> 497 bytes .../communications/images/marker--plus.png | Bin 0 -> 656 bytes .../autopsy/communications/images/marker.png | Bin 0 -> 622 bytes 25 files changed, 250 insertions(+), 170 deletions(-) create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-left.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier_zoom_in.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/magnifier_zoom_out.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--arrow.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--minus.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--pin.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png create mode 100644 Core/src/org/sleuthkit/autopsy/communications/images/marker.png diff --git a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java index c0208b1bce..eec27f30e5 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java +++ b/Core/src/org/sleuthkit/autopsy/communications/AccountDeviceInstanceNode.java @@ -24,7 +24,6 @@ import java.util.Arrays; import java.util.Collection; import javax.swing.AbstractAction; import javax.swing.Action; -import javax.swing.ImageIcon; import org.openide.nodes.AbstractNode; import org.openide.nodes.Children; import org.openide.nodes.Sheet; @@ -104,6 +103,7 @@ final class AccountDeviceInstanceNode extends AbstractNode { public Action[] getActions(boolean context) { ArrayList actions = new ArrayList<>(Arrays.asList(super.getActions(context))); actions.add(PinAccountsAction.getInstance()); + actions.add(ResetAndPinAccountsAction.getInstance()); return actions.toArray(new Action[actions.size()]); } @@ -114,16 +114,39 @@ final class AccountDeviceInstanceNode extends AbstractNode { static private class PinAccountsAction extends AbstractAction { private static final long serialVersionUID = 1L; - private static PinAccountsAction instance = new PinAccountsAction(); - static final private ImageIcon imageIcon = - new ImageIcon("images/icons8-neural-network.png"); + private final static PinAccountsAction instance = new PinAccountsAction(); private static PinAccountsAction getInstance() { return instance; } private PinAccountsAction() { - super("Visualize Account", imageIcon); + super("Add Account(s) to Visualization"); + } + + @Override + public void actionPerformed(ActionEvent e) { + Collection lookupAll = + Utilities.actionsGlobalContext().lookupAll(AccountDeviceInstanceKey.class); + CVTEvents.getCVTEventBus().post(new CVTEvents.PinAccountsEvent(lookupAll, false)); + } + } + + /** + * Action that pins the selected AccountDeviceInstances to the + * visualization. + */ + static private class ResetAndPinAccountsAction extends AbstractAction { + + private static final long serialVersionUID = 1L; + private final static ResetAndPinAccountsAction instance = new ResetAndPinAccountsAction(); + + private static ResetAndPinAccountsAction getInstance() { + return instance; + } + + private ResetAndPinAccountsAction() { + super("Visualize Account(s)"); } @Override diff --git a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties index 4935475284..0161fd855c 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties +++ b/Core/src/org/sleuthkit/autopsy/communications/Bundle.properties @@ -17,13 +17,12 @@ FiltersPanel.accountTypeRequiredLabel.text=Select at least one. FiltersPanel.needsRefreshLabel.text=Displayed data is out of date. Press Refresh. VisualizationPanel.jButton1.text=Fast Organic CVTTopComponent.vizPanel.TabConstraints.tabTitle=Visualize -VisualizationPanel.jButton2.text=pan CVTTopComponent.accountsBrowser.TabConstraints.tabTitle_1=Browse CVTTopComponent.browseVisualizeTabPane.AccessibleContext.accessibleName=Visualize CVTTopComponent.vizPanel.TabConstraints.tabTitle_1=Visualize -VisualizationPanel.jButton3.text=Orthogonal -VisualizationPanel.jButton4.text=- -VisualizationPanel.jButton5.text=+ VisualizationPanel.jButton6.text=Hierarchy VisualizationPanel.jButton7.text=Circle VisualizationPanel.jButton8.text=Organic +VisualizationPanel.zoomOutButton.text= +VisualizationPanel.zoomInButton.text= +VisualizationPanel.fitGraphButton.text= diff --git a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java index f13128f5c3..e0f945bb69 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java +++ b/Core/src/org/sleuthkit/autopsy/communications/CVTEvents.java @@ -21,6 +21,7 @@ package org.sleuthkit.autopsy.communications; import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.EventBus; import java.util.Collection; +import java.util.Set; import org.sleuthkit.datamodel.CommunicationsFilter; /** @@ -69,4 +70,17 @@ final class CVTEvents { this.replace = replace; } } + + static final class UnpinAccountsEvent { + + private final ImmutableSet accountDeviceInstances; + + public ImmutableSet getAccountDeviceInstances() { + return accountDeviceInstances; + } + + public UnpinAccountsEvent(Set accountDeviceInstances) { + this.accountDeviceInstances = ImmutableSet.copyOf(accountDeviceInstances); + } + } } diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 2facc5f66e..0e1d7ff407 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -50,19 +50,6 @@ - - - - - - - - - - - - - @@ -102,19 +89,6 @@ - - - - - - - - - - - - - @@ -128,30 +102,54 @@ - + + + + + + - + - + - + + + + - + - + + + + + + + + + + + + + + + + + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index 6accd8965c..af8d915f65 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -26,18 +26,19 @@ import com.mxgraph.layout.mxCircleLayout; import com.mxgraph.layout.mxFastOrganicLayout; import com.mxgraph.layout.mxIGraphLayout; import com.mxgraph.layout.mxOrganicLayout; -import com.mxgraph.layout.orthogonal.mxOrthogonalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; +import com.mxgraph.swing.util.mxCellOverlay; import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; -import com.mxgraph.util.mxEventSource.mxIEventListener; +import com.mxgraph.util.mxPoint; import com.mxgraph.view.mxGraph; +import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; import java.awt.BorderLayout; import java.awt.Color; @@ -45,6 +46,7 @@ import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; import java.util.ArrayList; import java.util.Arrays; @@ -92,39 +94,48 @@ import org.sleuthkit.datamodel.TskCoreException; * actions to work correctly. */ final public class VisualizationPanel extends JPanel implements Lookup.Provider { - + private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - - static final private ImageIcon imageIcon = - new ImageIcon("images/icons8-neural-network.png"); - + + static final private ImageIcon pinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png")); + static final private ImageIcon addPinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--plus.png")); + static final private ImageIcon unpinIcon = + new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--minus.png")); + static final private mxStylesheet mxStylesheet = new mxStylesheet(); - + static { //initialize defaul cell (Vertex and/or Edge) properties mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); - mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "#000000"); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); + mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); +// mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_PERIMETER_SPACING, 0); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ENDARROW, mxConstants.NONE); + mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_STARTARROW, mxConstants.NONE); } - + private final ExplorerManager vizEM = new ExplorerManager(); private final ExplorerManager gacEM = new ExplorerManager(); private final ProxyLookup proxyLookup = new ProxyLookup( ExplorerUtils.createLookup(gacEM, getActionMap()), ExplorerUtils.createLookup(vizEM, getActionMap())); - + private final mxGraphComponent graphComponent; private final mxGraph graph; private final Map nodeMap = new HashMap<>(); private final Multimap edgeMap = MultimapBuilder.hashKeys().hashSetValues().build(); - + private CommunicationsManager commsManager; private final HashSet pinnedAccountDevices = new HashSet<>(); private CommunicationsFilter currentFilter; private final mxRubberband rubberband; - + public VisualizationPanel() { initComponents(); graph = new mxGraph(); @@ -142,7 +153,9 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setAllowDanglingEdges(false); graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); + graph.setResetEdgesOnMove(true); graph.setStylesheet(mxStylesheet); + graphComponent = new mxGraphComponent(graph); graphComponent.setAutoExtend(true); graphComponent.setAutoScroll(true); @@ -158,61 +171,92 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider //right click handler graphComponent.getGraphControl().addMouseListener(new MouseAdapter() { + @Override + public void mouseWheelMoved(MouseWheelEvent e) { + super.mouseWheelMoved(e); + if (e.getPreciseWheelRotation() > 0) { + graphComponent.zoomIn(); + } else if (e.getPreciseWheelRotation() < 0) { + graphComponent.zoomOut(); + } + + } + @Override public void mouseClicked(MouseEvent e) { super.mouseClicked(e); if (SwingUtilities.isRightMouseButton(e)) { mxICell cellAt = (mxICell) graphComponent.getCellAt(e.getX(), e.getY()); if (cellAt != null && cellAt.isVertex()) { + JPopupMenu jPopupMenu = new JPopupMenu(); - jPopupMenu.add(new JMenuItem(imageIcon) { - { - setAction(new AbstractAction("Pin Account " + graph.getLabel(cellAt)) { - @Override - public void actionPerformed(ActionEvent e) { - handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); - } - }); - } - }); - + + if (pinnedAccountDevices.contains(cellAt.getValue())) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + graph.getLabel(cellAt), unpinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); + } + })); + } else { + + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + graph.getLabel(cellAt), addPinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); + } + })); + jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + graph.getLabel(cellAt), pinIcon) { + @Override + public void actionPerformed(ActionEvent e) { + handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true)); + } + })); + } + jPopupMenu.show(graphComponent.getGraphControl(), e.getX(), e.getY()); } } } }); - + splitPane.setRightComponent(new MessageBrowser(vizEM, gacEM)); //feed selection to explorermanager graph.getSelectionModel().addListener(null, new SelectionListener()); } - + @Override public Lookup getLookup() { return proxyLookup; } - + private mxCell getOrCreateVertex(AccountDeviceInstanceKey accountDeviceInstanceKey) { final AccountDeviceInstance accountDeviceInstance = accountDeviceInstanceKey.getAccountDeviceInstance(); final String name =// accountDeviceInstance.getDeviceId() + ":" + accountDeviceInstance.getAccount().getTypeSpecificID(); - final mxCell computeIfAbsent = nodeMap.computeIfAbsent(name, vertexName -> { + final mxCell vertex = nodeMap.computeIfAbsent(name, vertexName -> { double size = Math.sqrt(accountDeviceInstanceKey.getMessageCount()) + 10; - - mxCell vertex = (mxCell) graph.insertVertex( + + mxCell newVertex = (mxCell) graph.insertVertex( graph.getDefaultParent(), vertexName, accountDeviceInstanceKey, Math.random() * graphComponent.getWidth(), Math.random() * graphComponent.getHeight(), size, size); - graph.getView().getState(vertex, true).setLabel(vertexName); - return vertex; + graph.getView().getState(newVertex, true).setLabel(vertexName); + + return newVertex; }); - return computeIfAbsent; + if (pinnedAccountDevices.contains(accountDeviceInstanceKey)) { + graphComponent.addCellOverlay(vertex, new mxCellOverlay(pinIcon, "pinned")); + } else { + graphComponent.removeCellOverlays(vertex); + } + return vertex; } - + @SuppressWarnings("unchecked") private void addEdge(Collection relSources, AccountDeviceInstanceKey account1, AccountDeviceInstanceKey account2) throws TskCoreException { mxCell vertex1 = getOrCreateVertex(account1); @@ -230,7 +274,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider edge.setStyle("strokeWidth=" + Math.sqrt(((Collection) edge.getValue()).size())); } } - + + @Subscribe + void handleUnPinEvent(CVTEvents.UnpinAccountsEvent pinEvent) { + graph.getModel().beginUpdate(); + try { + + pinnedAccountDevices.removeAll(pinEvent.getAccountDeviceInstances()); + clearGraph(); + rebuildGraph(); + } catch (TskCoreException ex) { + logger.log(Level.SEVERE, "Error pinning accounts", ex); + } finally { + // Updates the display + graph.getModel().endUpdate(); + } + +// applyOrganicLayout(); + } + @Subscribe void handlePinEvent(CVTEvents.PinAccountsEvent pinEvent) { graph.getModel().beginUpdate(); @@ -250,10 +312,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + @Subscribe void handleFilterEvent(CVTEvents.FilterChangeEvent filterChangeEvent) { - + graph.getModel().beginUpdate(); try { clearGraph(); @@ -268,7 +330,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider // applyOrganicLayout(); } - + private void rebuildGraph() throws TskCoreException { /** @@ -311,27 +373,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } } - + private void clearGraph() { nodeMap.clear(); edgeMap.clear(); graph.removeCells(graph.getChildVertices(graph.getDefaultParent())); } - + @Override public void addNotify() { super.addNotify(); // IngestManager.getInstance().addIngestModuleEventListener(ingestListener); try { commsManager = Case.getCurrentCase().getSleuthkitCase().getCommunicationsManager(); - + } catch (IllegalStateException ex) { logger.log(Level.SEVERE, "Can't get CommunicationsManager when there is no case open.", ex); } catch (TskCoreException ex) { logger.log(Level.SEVERE, "Error getting CommunicationsManager for the current case.", ex); - + } - + Case.addEventTypeSubscriber(EnumSet.of(CURRENT_CASE), evt -> { graph.getModel().beginUpdate(); try { @@ -349,10 +411,10 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } else { commsManager = null; } - + }); } - + @Override public void removeNotify() { super.removeNotify(); @@ -371,14 +433,14 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider splitPane = new JSplitPane(); jPanel1 = new JPanel(); jToolBar1 = new JToolBar(); - jButton2 = new JButton(); jButton6 = new JButton(); jButton1 = new JButton(); jButton8 = new JButton(); - jButton3 = new JButton(); jButton7 = new JButton(); - jButton4 = new JButton(); - jButton5 = new JButton(); + jSeparator1 = new JToolBar.Separator(); + zoomOutButton = new JButton(); + fitGraphButton = new JButton(); + zoomInButton = new JButton(); setLayout(new BorderLayout()); @@ -389,17 +451,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider jToolBar1.setRollover(true); - jButton2.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton2.text")); // NOI18N - jButton2.setFocusable(false); - jButton2.setHorizontalTextPosition(SwingConstants.CENTER); - jButton2.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton2.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - jButton2ActionPerformed(evt); - } - }); - jToolBar1.add(jButton2); - jButton6.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton6.text")); // NOI18N jButton6.setFocusable(false); jButton6.setHorizontalTextPosition(SwingConstants.CENTER); @@ -433,17 +484,6 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider }); jToolBar1.add(jButton8); - jButton3.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton3.text")); // NOI18N - jButton3.setFocusable(false); - jButton3.setHorizontalTextPosition(SwingConstants.CENTER); - jButton3.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton3.addActionListener(new ActionListener() { - public void actionPerformed(ActionEvent evt) { - jButton3ActionPerformed(evt); - } - }); - jToolBar1.add(jButton3); - jButton7.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton7.text")); // NOI18N jButton7.setFocusable(false); jButton7.setHorizontalTextPosition(SwingConstants.CENTER); @@ -454,28 +494,43 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } }); jToolBar1.add(jButton7); + jToolBar1.add(jSeparator1); - jButton4.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton4.text")); // NOI18N - jButton4.setFocusable(false); - jButton4.setHorizontalTextPosition(SwingConstants.CENTER); - jButton4.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton4.addActionListener(new ActionListener() { + zoomOutButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png"))); // NOI18N + zoomOutButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomOutButton.text")); // NOI18N + zoomOutButton.setFocusable(false); + zoomOutButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomOutButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomOutButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton4ActionPerformed(evt); + zoomOutButtonActionPerformed(evt); } }); - jToolBar1.add(jButton4); + jToolBar1.add(zoomOutButton); - jButton5.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.jButton5.text")); // NOI18N - jButton5.setFocusable(false); - jButton5.setHorizontalTextPosition(SwingConstants.CENTER); - jButton5.setVerticalTextPosition(SwingConstants.BOTTOM); - jButton5.addActionListener(new ActionListener() { + fitGraphButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-fit.png"))); // NOI18N + fitGraphButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.fitGraphButton.text")); // NOI18N + fitGraphButton.setFocusable(false); + fitGraphButton.setHorizontalTextPosition(SwingConstants.CENTER); + fitGraphButton.setVerticalTextPosition(SwingConstants.BOTTOM); + fitGraphButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { - jButton5ActionPerformed(evt); + fitGraphButtonActionPerformed(evt); } }); - jToolBar1.add(jButton5); + jToolBar1.add(fitGraphButton); + + zoomInButton.setIcon(new ImageIcon(getClass().getResource("/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in-green.png"))); // NOI18N + zoomInButton.setText(NbBundle.getMessage(VisualizationPanel.class, "VisualizationPanel.zoomInButton.text")); // NOI18N + zoomInButton.setFocusable(false); + zoomInButton.setHorizontalTextPosition(SwingConstants.CENTER); + zoomInButton.setVerticalTextPosition(SwingConstants.BOTTOM); + zoomInButton.addActionListener(new ActionListener() { + public void actionPerformed(ActionEvent evt) { + zoomInButtonActionPerformed(evt); + } + }); + jToolBar1.add(zoomInButton); jPanel1.add(jToolBar1, BorderLayout.NORTH); @@ -484,30 +539,25 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider add(splitPane, BorderLayout.CENTER); }// //GEN-END:initComponents - private void jButton2ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed -// graphComponent.addMouseListener(new mxPanningHandler(graphComponent)); - graphComponent.setPanning(true); - }//GEN-LAST:event_jButton2ActionPerformed - private void jButton1ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton1ActionPerformed - applyFastOrganicLayout(); + morph(new mxFastOrganicLayout(graph) { + @Override + public boolean isVertexMovable(Object vertex) { + return super.isVertexMovable(vertex) && false == pinnedAccountDevices.contains((AccountDeviceInstanceKey) ((mxICell) vertex).getValue()); + } + }); }//GEN-LAST:event_jButton1ActionPerformed - private void jButton3ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton3ActionPerformed - - applyOrthogonalLayout(); - }//GEN-LAST:event_jButton3ActionPerformed - - private void jButton5ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton5ActionPerformed + private void zoomInButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomInButtonActionPerformed graphComponent.zoomIn(); - }//GEN-LAST:event_jButton5ActionPerformed + }//GEN-LAST:event_zoomInButtonActionPerformed - private void jButton4ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton4ActionPerformed + private void zoomOutButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_zoomOutButtonActionPerformed graphComponent.zoomOut(); - }//GEN-LAST:event_jButton4ActionPerformed + }//GEN-LAST:event_zoomOutButtonActionPerformed private void jButton6ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton6ActionPerformed - applyHierarchicalLayout(); + morph(new mxHierarchicalLayout(graph)); }//GEN-LAST:event_jButton6ActionPerformed private void jButton7ActionPerformed(ActionEvent evt) {//GEN-FIRST:event_jButton7ActionPerformed @@ -519,21 +569,21 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxOrganicLayout.setMaxIterations(10); morph(mxOrganicLayout); }//GEN-LAST:event_jButton8ActionPerformed - - private void applyFastOrganicLayout() { - morph(new mxFastOrganicLayout(graph)); + + private void fitGraphButtonActionPerformed(ActionEvent evt) {//GEN-FIRST:event_fitGraphButtonActionPerformed + fitGraph(); + }//GEN-LAST:event_fitGraphButtonActionPerformed + + private void fitGraph() { + mxGraphView view = graphComponent.getGraph().getView(); + view.setTranslate(new mxPoint(-view.getGraphBounds().getX(), -view.getGraphBounds().getY())); + +// final double widthFactor = (double) graphComponent.getWidth() / (int) view.getGraphBounds().getWidth(); +// final double heightFactor = (double) graphComponent.getHeight() / (int) view.getGraphBounds().getHeight(); +// +// view.setScale(Math.min(widthFactor, heightFactor) * view.getScale()); } - - private void applyOrthogonalLayout() { - - morph(new mxOrthogonalLayout(graph)); - } - - private void applyHierarchicalLayout() { - - morph(new mxHierarchicalLayout(graph)); - } - + private void morph(mxIGraphLayout layout) { // layout using morphing graph.getModel().beginUpdate(); @@ -541,37 +591,33 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider layout.execute(graph.getDefaultParent()); } finally { mxMorphing morph = new mxMorphing(graphComponent, 20, 1.2, 20); - morph.addListener(mxEvent.DONE, new mxIEventListener() { - - @Override - public void invoke(Object sender, mxEventObject event) { - graph.getModel().endUpdate(); - // fitViewport(); - } + morph.addListener(mxEvent.DONE, (Object sender, mxEventObject event) -> { + graph.getModel().endUpdate(); +// fitGraph(); }); - + morph.startAnimation(); } } // Variables declaration - do not modify//GEN-BEGIN:variables + private JButton fitGraphButton; private JButton jButton1; - private JButton jButton2; - private JButton jButton3; - private JButton jButton4; - private JButton jButton5; private JButton jButton6; private JButton jButton7; private JButton jButton8; private JPanel jPanel1; + private JToolBar.Separator jSeparator1; private JToolBar jToolBar1; private JSplitPane splitPane; + private JButton zoomInButton; + private JButton zoomOutButton; // End of variables declaration//GEN-END:variables private class SelectionListener implements mxEventSource.mxIEventListener { - + @Override - + @SuppressWarnings("unchecked") public void invoke(Object sender, mxEventObject evt) { Object[] selectionCells = graph.getSelectionCells(); diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png b/Core/src/org/sleuthkit/autopsy/communications/images/icons8-mind-map-16.png new file mode 100644 index 0000000000000000000000000000000000000000..4d9188c24518a204fa39ef11bd7c3734bd319f4a GIT binary patch literal 663 zcmV;I0%-k-P)E~JSA^7~fRx!6Lk{2l z^5gFNzyBN7-C2iC4HG^C7@3%u{xdK#FfcGM{A2i!RV@oX17;`duKGLcyf+so2kXZ# zzY1_FbZ$K%?%aAp9Gjd+$FX#L>RGbpU5T{R6zyU7_n$#y&XwY+DLN~V761Rw$cW5G zSI;EC%jROEDJo^7Ehfdr!JOvSa!Qtgfq@}<=Joi3Rgcc9DN0nv&A1xFz`(%Z)^bXg zgPl1Qrk))iAtY3|wo|1&V$eEKQ<#mBE4fB!SesL4sR>8Q%dF)%PNT)y?_ z;oV0sYZ#gSJ(dvQ{jDL*GYzUf3B`HHAaB)!i&{#udJGH<4A<{IxiB$Y-VmEQoU!oc z?dQ3d|2`^aVqpC7>dl88xYXfH2X8-pW;*fdt;*y`1@#Z#zCHEpJi<$~0q*U`R2cp- zJwBUm@|%Hyfq{wh!|b07O!3&%qJ%-@gcIUo!u;*;zy1_ml&^R@{eS50m!AoUZP-E!df9C%TPzU{isAFJ2i-kLn-=RTaHVVaSii z<3SWfA&jy11c(49nAQP|OJ~D?X-Fstm?eqHvW#*i==b}d5~K+a*lI4c)xNrQr^8#*LEpTE7?<5pFL8tS`1+SZqgfF?iC%XI}}k=y;&9v2BtbUL2F45sMkG6J1m;FxWV zd#a(8ZsIFR63T>B1#oEdmih(zb> zu&poKSXGp6V4+8A*YK>K$1KOtINcUcDAL`TeA;F{;Ol27$vy2GcwV2#b$` z3uOcr>s2Xg_}q6q@S3yy{mRP9O|V`XAcT1}yC@ou$Gf45@YUX)x&0q}=2`I9M>~_0$ zDdBJ!EiNuBn3z#r zF4rd{cD5;6CWWumBWvk)RNeTo_F<5e(cZ5#q)91Uve#fRD55A{9v>g?qh9uB%YOn4 Y0Q}V0z?sHwE&u=k07*qoM6N<$f>jC|UjP6A literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-actual-equal.png new file mode 100644 index 0000000000000000000000000000000000000000..52972052c5e9f68880c12353b5008eab9df665d8 GIT binary patch literal 1147 zcmV->1cdvEP)%QbAO_Ma) zI=iM#nyejRt#%)-RB2bGbZpi6$Dl9~9dwzv3Z+n7-58~?B7%y7A{iq2qZUMT>ex`~ z99>A)2os8yjcL|R+OCZxS)1hLy}b84FI{6h-rN4zcHq2wx%Zskx%YhB^9V&z(32!u z{rLTU6pzPIDwRUk@@LSl180f?6h$G9Ga-zzI^Ri>WH&4~1H12u#DK%HEC(e?iVA{& zGMO}jIr{!1|B8WCvRCXtJ32qtI*1JbPE(|^uf|2pJ+%?dY^lVHHZx{t7i9jAzlFxD z)A?^?Svd!?1Hg%OH?Y?nL>r&3MP_HcCb%dHqs}tfv8Rc>X5tWI<*;Rw&A_RfJ{QUQ zTr~3gvNygPTiP`{pC5(gu2mzX`Y~6&j96(!J)Pz)HKyAz8XqE8Ad7*aAnlb}uQ_?);!JbVMY)`)r{ngxN)};M5@{iMHuwT6 zGh3*iyS*%W zJ(fZY+F$+YfibvxwzGhG^VYHmn6aV(l4SLC!c%|V-Ckd3lOG~iAPM}>G8CSgUNVDh zxM+Z;4SH!B3`dvuzS1Gj@dhb-A=EMhDM~}CYF>GX&o7}o!Cst5*%-zcEE=#SPU;=% z24B|uTX4kTe&@{!aX!hXctJv?8g&4HC#AfCgCh%!BpZKZ7%5t;WF4}@`_o#@{hW|; z>4p!^{Hrhu%^5Idp(&D80pW%O)Ey1kY2vep8~W^yeElYxqbNK8{C|)&$N}JOn>Sas zy>{rOH$MF2y|0q^kY(V`_J%#Xjd!1KG>Th2*s6N*dQ41BPG!;g%V}elC&yH9NfOsT zcK262fVaC`RnHwhd?YYDJQRvXCx5Y5mz=YfH@U|mO7~bK14WeCa8G_2_>!V9L4pvY zn*Cr6l>_+R>T1ukN00Ud1_$3&!7sxbe;)SYDuY*|T36QVTWbw$0`N|c$J^M`(;ql- zqAzsi%ETmq{{VXnkJQnCDggX|*IT=-ukU1_zyGaJBoh7|z`upP`A2S<0DjQt+gf-0 z_)ws?_edxl9-jvAuVHKUvGin76!EE3?+1<@dp$Hh{_9@=J_5`CJhr9}fRUD#hQUN4 zLBl)*OSjggRn>0vV%Kot2S8=bXF^(GK~SLSi-o3NcnzNPZ_9rI3;?UOuLfW8&zk@M N002ovPDHLkV1fwg8$vaAx7z{)QT_&zVDHK;XMk%a_pn|AKhDiRX6A{HZCP=5V z3+bAHLea7@*KJChHq|uEcixxhCcd{;lfSkd_`P@EJLmk)JNKS@&m&Y-MUPWxeDQj{ zXnA=V#p7{gt$7M{9y+fZP!xrD-h^0|#pCTHN%q0(T>#g=CUbzGq9}e@mc#jc9;MSM z1Y`8gd){IIP3(2s(6)mgm--MJ5S*h(^?=7oENrYs_cqtFQJcVW^Gizh*E{)+pJ$~n z6-B)Sw!?tfx(w`|!>Ibn79><|ElDnl`3sd5ilcicchkfp#>%snTAP8#%4R3YH9KkK zZO+{OdLq#|ACWG=>myna5?_S+7Z7J{ML<*v=j^^Vu&^AoyCIT zt`c}Cd$pxWSi!#f6y2h%{Bp6LBf&}{G+vJ$XPfkJ>_?96P!hJEz_G;JsehB5ks;bc`h zt*Azp;;^kGsS3;TXe~66WJN&|!Tm55w_#s>4Q#P9<(A-fwqoFhf861E zIxqhX<+_;2N|97fiV`q8*$m=GKU!37-9-x&#SQ}=?Bo*S0P>zqo9cUBI{N%8@4o-m zXDjSc%kbjU?cF^^qvxNmH1G!PC?U$HluFIj7Lx+X$u-$V#+qI8wE_W)N_mGQ$AhD-K?HBtlQ`x_hG z&zv~X?;9C;6O&(qGyXE@mNDRb3YSW`T;Ez7pccpvy4_73{ry9})29am*RM}a1NlYJ zEj?661E~Y@LrqOBTLuTu_=bjF3xqR9K0|Unc!QkX9 zkbeQH`A5=|MNwqWo_*VQ^5n~b$;lso2l8=v{pTZV`al@kv!{I|7K_m^4#2DX>&mKO z2YT+vSm6f%qm)38FCd>+pC4OL7w>bVFMB;GEf-|7mA2XRTF(_*P&?Vc{XqIUV zLu3_3|8NGCjJPqzLV=Q%LO~=evot;I2KmcXsK8?2S|2zr67$c2Fix`HH`;{a~Ho~w2)UpH_19gj{*d+*phvPUD z4u=r*(T^Y77ab(TxNZyDvhTB03o!se2PUPuA~WHAA_t9c$Y*>89m7t}h|zy;aTR}# z#4n1XbP2F+p#Bv*Fcuv^IZu@!U3OVYa-QeTWv>xUH9OcFS`DJJG>ksapwh@jo0(*7 zW(wJDk^g?^S=c%`6+Z{V^$91616}@$3KI}Lz%GioBgaITUad2t>`ca{c+}7|g-LQb zuH1riw0nu^?h7-fiAk;lLUbhQAQTFvJRpJ~Dp?lS)mEEs$m9+0{#B5$T|85%w68RK zlKAcq%lX~a`kSAAHNH0*6S`r@qd160qv?{Mv0{_So0)+b`6C<-qhG$L3iw0ZTp-3J zNRdIyWIg{@U#1cb9j3}k)l+?gzQ&}3NFWu*o&fn#Y>r$=<&a*#YG+XRR|OJinuWI_IRCmm3fDqWg( zxjn%>FYOk+tdXP{MuRF|{#_?0OYhe^6->?Re<-bNgRn>_PO!a$0Rv5|yA%%jv2!>a z>2_d)&ld^%t3wI^yc!K?dthwE7bf(E0ZNJd~}Qt z3=K!n<*OleBue(k{qetIx_4ogF z6Ue`T;k%El=mX(ERaJSr-|wfO?}DK_E7Gc9J9@6ZQ~3iRYbt>nuPDb!P&as`=qGRT daYB8!Wf&|L}6AZHCCH}1KG-)vwm#$b4!{gH_18op1U8lS(O9^>H{z5 z&-ikGlz>&Zd_r4(+r2db)qQo2%-q)q@W z0BRql3;~D4 z>PmXtM4=KvRZtK4bfwa#E0vl;y#P>K;qq;2*C)&UR)}+UoO6Z(K_-(a8m4KkWX#;S zq1F43OhEy_t;y7I$z_oXZ55FVZ55g<82}(tB6;1i($VTma%OHaO><@8h;q4HsV3-b zUJ@EE^HSAu-~hl>mfPb~`8-LL0RYCQ^7%WX>C{YtVNiK#lRk3!+IS~`xC{WPS88lggONOc>0V1gK|aq* z5!BX&z2g9!fJgPO?Q5@oG9i3sjRC*_fWeL{0>D^(wO7yQd2NBfwo4D2ED;GH|7#j$ ziIAC^nLT9yAcU5x85(+!<_&c|HIZS-tLrWzdvy7-w$*xSU9-*?*QtI0N;`aSTr6VxEQSQp7mF(sqO7|I(z%g z7QmeQC_fG0vg0T{gz$J#KbVCIA;jx7HFYoV-o3Z$=+XD1@%X?M#@IOk696U%A@O38 zOZnDpf=VI8=DNCu=Dm9lbR9ahKYIQ8wE@Q1cL2to%FzH+2_d#NG%Q-OfB)gG0|(xX z_V>qrW{iCeVC1PSQwXuGv2k(J!Gp)T_U+phjm7!~8Dn1pc=${_nRBilIr3rGo;`0x z`}%(TgE4jrz@N{|^ciFQ>(?(knn)yM##j`<{m0s>dNp3#egc$IP+I)mvhC?$%z>UpoK* literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-in.png new file mode 100644 index 0000000000000000000000000000000000000000..281af0cde172dac2a38734350dfd35d840881fde GIT binary patch literal 1225 zcmV;)1UCDLP)`wXRCe@1VJ{T#-aUC3T1YRl!9dpw!uD(Q9lLNIfl5+IR)vQ z)R49j9F$om&iZHT|1C+Iyd>}Cy|;6pP1Z`nKBxzN?;h?w=Xc&c@7#MHq3b$&nL^`7 zU0oeoTwFwvNCer6-auOqoX;3g6omxAj98Y%`WBKTJ7BsA;9fDA0{Ase8&p*_D9bXE zq!*fur&uTK9SlvJ}*3C07*rLP5Uf+_jzi-7Fx+Ca~6GyGg){)h?2& zcG0M=I)4AV;pMH^LecfL{Orxq1zZe4BS1VZ&2DCGcrqXKv`?vn&o2-; z4(m&CB{cch(tJ&oC4BA1qo&Lbl1U|30Jp~_F2JZ`^425Q%AYlOt?d#{k&&jci~BSf z<}1rZ9yr~Z0IHh%u+by|--*!w&u+z*Nc*I1jQ|8eHX+$_j?z2mQflp(JTK&r-%)C@D?FBg+Z~ zR!d6NG?XH^8>10B_RZ|T9y*h22lo^CUAG5^obC_aFM$jzL_$$DoQl9VJ7!m>1r|w~ zAWefq^ALyskzv$eW(PUM0ryXHg>XHjCSwhsoc~*AbXqX6lvUMqIhsh#Ehkd5z!o<# zlsOScLjMeZE(17s+fRRn0#~YAk n9U7HlG=1Zqd)dD&{|YbwP~{7JH}}k800000NkvXXu0mjf$H7Yd literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out-red.png new file mode 100644 index 0000000000000000000000000000000000000000..04446b048ca454541a9bc74b5f34200a35afe041 GIT binary patch literal 1053 zcmV+&1mgRNP)FL;JH_4ypkk+S{tCg zj#ee@U`X;XneOu1*dK8>mhzcPqnlN?R;)GCG+e_sa?#JbtysFmgu@x4Y7m0097C3MG`F2Y8eq z>|#{vZg*!)BiD2A_|+Bw09sBUpU)3I9ri6(xri(T2mpZ1yjh%CGB-+boK8ZU_uE&# zolFe^K%#;`u5I5Q$07jOy-wNVb;uq70GVL0$%H5r|B1hmH}#pNjn&8ZIx}C6-E6D4 zL7`xVb_ZQ401#e>>{(PJ4dw97$s`AW)jbU^d!b;4D*i#SD1c&w0nj7anc{mmiV-dr z1ytMsAu>0&m^JtAcFX#fk*S~j5qvI1MkVZBuy_^#V16NMB1GmY3Sg{^C#N?e&HKGv z&l(v$Yh)yXT^K!FfC~_cMC9bmrW0djyy6o{l07z_$og|RF6=T<002yj7fa!-BIjTs zWo&ZYD@pbk0NnF-scQ1w$Xjx_WnsnPi?(_d0U#(r!X}Xico-oJ0@>e+0$|j}07sVf6F&FWyY~3lrCC8n<##aN3Evvqtg|jM?mbvGB!r zdHUYR>ue0_s9nxAuiI2dd=aPs7-7l((>ew0Z3@J)5&(aTM(!y$wo zey)Y!v9Sw*L|Gqenif!1^+a!P-^i6K6QZNzP@FL~NGbLI>*UciZI`O5r~3N(MB1bY-DO`R&;iD#TjF-P)cjJB{x-7eR*Ku#K`pYucE8# z**IhDbxLX7wgqz}5_xTQc1?75_rw`vZ&6Bv+Zy`eaQK;YIvv$@of?L5-mF00000NkvXXu0mjfQxfOs literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom-out.png new file mode 100644 index 0000000000000000000000000000000000000000..c11249f5286caa237c1128a6e500fc0e6e9619fb GIT binary patch literal 1150 zcmV-^1cCdBP)4vp`NX3u&k1!B~2{j#~f7C*mog$^6jKMb8A7jM70_%h!8{0%d zs*@VhHiLbXttf8sW45*JmX9VcXR&jj{^S$SP^H>g6&Q~LgJEmug7hw2}l^`_vSZZHKyuAqlke;4(t}D7a+f7NcNU@lvP^FDG zJ-rG~waK=>Lr7KFi!S0re9Hl{*{rTglC-^0koN6qxBkUogo&-lGnl~?{mfy)m{nxh z&URZ;E=c={Z+VOI`MePcx>~omX4Y9T2V^VJZ80Y+QxD~|k%Ps8ndT;8_Qu#eAw#Sb zAeWPj?Ataqm>)nZz=7HwX3kCqIZPuHhcVC4t4Na0$%(0l>A6OqC3)rgy|!`&OG~m* z0FT#gorhK<;GIXW)cqUh$1fgKp_yx_vkHLeDF;v#<6>iIvfq^UUv|s3)QZoFZ7Bdn zVI!Y-{6R+cd2L)GTS#90Y%g+Ic@9u0J>#XKnj!7o`xzN9@Y05ArU1eDz6p3Z3BuUi-4wqIT7gfb*HAO{L zQIX=WkEI;Mx150^aoWh|PQIe|`_Qn<^Uhl}u){Kymlc&~pD678h(J#pgK3@sQXo*-jETkOd=802pFC1;-$gSR za|57{4_QZC0N$~2V{OOLBM0C7@ZC zUA~zWa(Q}$fGdi;`m%dUssMbC+gA&U))5d-YDwNL1?@iAqpJ zQq{6OVa^RrV?(foG2Qpz38@C~z4i6pm%F=rf`fza67ZXF$6toI1){JkF};+H-mNDC zYyj{BUazmUr>8%7>QrBNe0*#Iz`r5YxuzElqz1qb`FxFA`ua`>`}^MxN28G+0sKpt zn_csh3E+qQ{>@D%PYwlpdyj`BkiDVrn|j`VTmBYc0P$hN7tzCR Q_y7O^07*qoM6N<$f*p@6M*si- literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier-zoom.png new file mode 100644 index 0000000000000000000000000000000000000000..a705d33d9231f2ca64f90a4505422f45bec80935 GIT binary patch literal 1079 zcmV-71jze|P)V!R}{W;?>u&9vuie4 z(^$42Td)HBFtBjc)}A7 zzw|Ld=TUfJdxLk9YU(Z)YnLdO%Q=;&scwURbeV^U&}>$_&19)Q^q5XN1r!w#{=K#G zSa1IXYlftiAe&X4{Oea&yC1?`o>#CMm3kz>EfGQFF9@wB?8rfBlgmHYMf z3T9`iQ-WY9;F*BCy;&;!3RGz^@xn5(dv&Ep2F{?JK-Zl&8;@BK2M%*P(uv&KGk7im zi<#%N6dXsKFg*M)P3Hq&Rqj1VQx7qd|nXrq`d+MDS`8+p-TB9rO#$+zy0Z%DVV(C z5{Z{5SYm`62z(#jB@lNmiguG1wCyD}i(76?>+bm-9G zNLN>9ESVg*TvFw^P+Yz_*po1udJ-yZBDL&=e;UwhnnDyxm@GIC7Dzde@2IK@eR$+Z zbEKo=D@J||&iIR9d&&S+NfuhN)7)B^UEFSM00`gzL zH1bAfGNn{Jb?TeQ(W3`qeSKH|0rGB8KYC+B9|#j$w$yf{QYjvuW1xDlsI4kDp-=X8 xSw8>_g#>221x+(yP*H2>Tj$){{%v_KzyRdFwPn-lWzqlu002ovPDHLkV1h5({M`Tm literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png b/Core/src/org/sleuthkit/autopsy/communications/images/magnifier.png new file mode 100644 index 0000000000000000000000000000000000000000..cf3d97f75e9cde9c143980d89272fe61fc2d64ee GIT binary patch literal 615 zcmV-t0+{`YP)gNuvOO$0ks zMIj=HnnBRUR?tKXG11rxCU4&7dG4NbuvR2_mEvc)n?Cow;~Wve|KR^>9@p5l)|QB+ z$jmun3q#x>;ss-PW_mnr2MHVzLAl1RW&0?VkixF*4t!St0YVb2wnKdU(kmOHiL;aW zK8Xte%(k>MVGG$E4no6dcNnb>BhVHHGD&1pv4YZ68kE2V03t5#PCEFm7=ad$6)+3B zTCmn*?A?=u(o~ET7~-7g0)ZB=6|lumi4}B}MLgy~Ysy6)Q5%Al7|05&1z3Jpu>cF8 z3?VXs*3<}%h3`5Wld)N2zJnk%Agw<~3k)sPTLFd=F5;d8-bj-09SkQuynfflNcZLN z!^_37fdZvzrq=9~mp*($%mcDRKC&qvaaZuX+C=AT6O*~tHl>0mcP<_q>-z%$xO(@! zYluq5a8VQI$S@4?r*v;gPo!QQ%pX3A#>xx4t=w-L6COWx?aj&`f+!YePsFtj=hOQR zP3=E2j@9L7s8;T^&s?u(Hdpu?CubjMrGn{t_37>9$|AD)QE08weJlKn8|OyjL~7oP zC8mPT`jzuH*Dh^I0048RGafUIT)4H~*m8m>egI0iH=(LB%b@@O002ovPDHLkV1lw0 B3FJNMp+(Bt!=q9U!ZZOlw$c zuAy5i+nTd|<_>NivLu&tYWf+obh7aHN%Hi45`pBR)x`tA#^U98gM4FFC6h~&)aWQw>e5Y84Gj%C?Fa5wL3#v12nvm3<6OafjJt}U((Qj zn8!nMmXr-qoCO7XcZRS8(x9RlIA>F^1(GoPldw}sc)rpQ>IL9yYf!7MN);5mno3dL zFr9-f3^@5I0h2d@QBNW#I`RB4IwvonO1T#W1?;?jrZNjp_!1ar;E|a)8g&BH^;Scq zt%uAgf}pb+yKn5ouFDnCJb}hGpY=s(m>77B`PIn4hUqw48S;@<+#YViwZYT4_>vEC z?=frJc<3Fn+HA3jXTwUklhgJ-dYkmNL^YBTW!uzZM O00008VI0S0!CoiyA}k0DS`b78y(oGU6a>AkUK9~n*^7|=kr7cwRFWv6%Z}9bXJxHq zv!bQ7t!|lawm;5>vpILy?iOC&HfG!Hc8BBJ-HKqfMb?WCJa0aq=l6SfssL2|4?ho1 zYF_hZS|)5^i5(2(3eSVtLjTwt+viCU4@P*+9|NQ!zmCYHkC8wnWH2cSOj5$smpmL3 zY1}4f$SympsTgoXWWvkj!KP${W<>az96Yfc0&EV^Sqav$1oSUxqGI6Xq{vddRFu7n z`2LdsHzi;=Dtr$y0$QI$*opyl-1%;k%_tFkSWDaJm;MHp-}I<6Z; z`=uSavTxAsh-+>P#z@O32VYD;@Uw_<1$Q)qo>w&5O)gAWuE2V33*Ucwqps{ny7nxp zX|aTf%a1~AQ*W?v_D-PC*yD@0=#7-ucnX}S5B~d&FdbXR-#fBe_gkP615I1CPtR;N z4-*FwcN*ZjSr5aZ75rZZR34vLukCGEW45>LLezQ{#QvpNP(bBYeXX{uZkgF|xEA`o r)y)eIsC4dIvZ!Ov;+nFL_^*5eZM*&99sh^s^i~2rXbB>z#ZtkF!9$9ORD+0!@uy{_ zwuz9nWs~h@V|Jf2yKcxXwgVsY=9@PkGxHwD7z4+)d7fMsB7r!GXh&2L9}x2h89e6q z#65^xfzi>(p;)ZX*VYy^biJ%*v&SkcD-q01BMJuz#DfzP{lS5OD3fKN-ms@&$MEne zuBF9aUS5tPb+@^IZw?G>TNoS+o8>a3QQ(RsS)O9zDR0>K8gL2JaAuC@1!frVq*#Q4 zqFA0{;wfLV?=|2O&;csCEbmgm89@ME(=1Oh@mAix*MMsWxb8Q_)zuS+W3f=8yBi2( zthE)2*{nqQ1786zEEaOPv5+W++c~bo;k8OdLH;Km7k?Z*4au92LC5a)a5vs1mNdqe zzGpJu!0!jMUI+3NTYTS0iHi4q1qDAf)FUD^Ge!L{&;(rA+ z8!C>nkY8Kd1dappUylqe%w4;$e(5<5wQ5yFOgSQ4^H)w`!9NQk`~3XmM4X literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--exclamation.png new file mode 100644 index 0000000000000000000000000000000000000000..b225a567b382355128ef59bf63c2ebc2ea84624f GIT binary patch literal 695 zcmV;o0!aOdP)X( zGJS~B8ef#OwY9C{Vk*`}Z7FVCltK~x3@*gAxX_PK5L_k{r7B%ym8>L?g`iN;ltA;c zI#n_gl1VbTo;$H26KF5|IGo4*&$(yrC6rP)T1AdyCX8$#lZc4ak+hJAZxL5P#PwefJ}g_zpY| z?N$qz&88ArZqa34Q8(uZ;TQ1gi0t|>90C=L)G3V>v)O&EP&i~~XZCPlkA~*%xJ1#f z1eJxWRvYfN@M{c*K&?X+j}qTar}u#n_?AiM-7BOqaPIbnwTCeX58b-dw#a>}1w7&u zsQ(A2`@WFRS1mk(7B29?sRTN)k5udB`D1P~oXToeU_8*nB#933tG zNyEnvPvBR57lhDY1XB;1O}rkx0!=4eMLq!O;%SNUg|M5`V0q;wNP-SN$qWmkZxTgr zIs|ARMI{R((V*uPR|20k@7DNO=`~yNA930$SogD;!f{Pg(Dx%b(gHxSE=pZgWq0lE{iZ-pW)%5e+)RcU& zUN}A6N6vS?n??~rkP|soW$Fhazy#0%d;&Ls2I$W0;{s7?Ns!_Z7zFKhFE5XEg~Ox1 z_V$ofts0d?;=UY@-=enzc}t_ew?hC3^&8WfgAr-cILOC_5*=Q{UmjkzWy zfhtkCs_Sq3z+x~+s%hHHIoIueW3CBl2Uz!2;qr2PC>-vK3=9y1Lj(d;NF-9+_v}n| zfPs^IGMV`(mrH!A1J1e5edlBYc)%O7{9My z5-6egDMU2){G8Ua*&?6L%sJO}^ou~v_1}>M%mym3dVF_%ohC&gX3n|(MSsJ)L?_O( znaq}jD}XB)T2Y9$y${1j@;?FIkxxJX3-jULBkPgKj91`L(_{x8iz@E`HMNM7_B5@P zec6HVTCCTd?_twyasSR1ACo%{j-pYUd-3!B&(Mzm0|0im4AhcuK!X4P002ovPDHLk FV1mN&0+Ij# literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--pencil.png new file mode 100644 index 0000000000000000000000000000000000000000..c18784e7cf00714b60bb72d00af053214a58fa13 GIT binary patch literal 660 zcmV;F0&D$=P)xv&t%*aJlNe+5RBp`nXPclQOM>p;C>Ps-80 zz8=Zvla2ZLQFJ}47qEJRW7~n=-VVnwAc_TUCK4`_O@4~=YMzTdZ-Fv?v&2|Ha1OCt z4zl^Y%Vd+E;^k@{I0(aApafu+bbXfuR{{ZG#iGk(li!VFo>V7nD1hVsl3QL*2)`f3 z+S?%(4!cY?`6*7nTWcVj{1ktyX_R`Y0uaMF^m~W6T@>Qo z&&9xcW?9y?$$0$9z|_?99U;aW3KXj-V5_t1D=S+dNkCjVC(X+DpvNk}ow@0*7pkg0 z{A$~;vEvWk8MwenzfY%91&*IU>>q(W5)TaDy*~45GOnJVPKj8lO}&M-^u0x7r=H6H3;V}zqVeKeb05CkA5 zO}+yF+jbBDxBaU3dLs{?Kumhsif`0R;rh6g00000NkvXXu0mjfyRpv7 literal 0 HcmV?d00001 diff --git a/Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png b/Core/src/org/sleuthkit/autopsy/communications/images/marker--plus.png new file mode 100644 index 0000000000000000000000000000000000000000..cb98edc78ae1bd4c79064cf09091b9864d2feacf GIT binary patch literal 656 zcmV;B0&o3^P)1RCwBaQ(bG*U=%)Ynyj_j73Red z1#gNvwz;`%?6L}au`2b>o52ti2E%_Kx+`zQKTz;$s9?raCNiPo=&ocoN<-tN@(;1SrznVBGjxoyMCN(IXGy36EK zobvmfx*j|N7C^(&wBPjLaySfvX}U~4#oc_Tt_RN!aNQp&TU+N(#p6Q@!^1!z;{yXw z$z<}BKlBP%uvS*p#E>k1a{lU9I{gzw z5uA1#h{>n;o#kkC>3$UcdxbixHaS;HJ3G5tu~?(oiOK&sA}XIpq!ivZi>0Z53YZ-j zILhkx?d@G)42VCSlUAQxKVm?O%eSxGl!?LO2L+mcf!lpuEEEhIM<9Nl3|;#g6SH7B zuxIZ9F|>IWyGi+%*fkvy;3$A%fp4b+XD%f!%9N}iyT8r57G?)(5>>3ceK8HpB&(v>^pDBvViL?c8 q#(*BFrH4Q8v`j?+oD9Mr0R{lL<06*LsCZ%k0000&kijW3b2_y{?RmcE%+Miz+;LyA5TDSD9_t1+Mq z&5*VXn7taX2b@B8R~0!4?IHnk;A9Fjo4A5+z#RAstbtOgI^=-7CAb7gfs=;(XXQ&^ zqjep6NZLUKGoWb5odXl)GVSHH0opeEF;Qi6Q*Dk>LG~P;>HSHY1KI+tpBelL*v8CV z*DbCE#*c;ATWL?-rbTU7JkY#dh`Aq>p8>ytJ4wyI7@%1P09_~CyGmebG;nFa1xMb& z{GnOfsHJXf)_S&$5$G<38X%p*FV9K7dw`E3)I1~9XTZn^^-);ttZvYd`v{Dci`K)g zoTRBjV>wCIJ4x>(y_57#(mP4-B)yaLPV#=8B<}#I6X_-JptY;u(i7|8v+Q4g4AT-r zgvz)HT32RI7SZN97sxbH4(#pLt>+|{h9qgk?N;po4y0Fw0UJWH92hp_zUcW7SP5Xr z3~qrkX)6OS9Vu@--aNnr_$m!MlYbTgWN|(`oWiw7lAro$oofcG1INkD=>}Nj(yLSt zthtAfWYmzmmo7d?4@*xE4{@h;!tN$Cl*QIb#{9qX{REsUKL`E@=)wqgFpG;w9dvsO z;B*Q%UWA}$h($07*qo IM6N<$f*xZGIRF3v literal 0 HcmV?d00001 From b318327727c45e26fad537d159c2ee7322022e14 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Fri, 19 Jan 2018 15:42:08 +0100 Subject: [PATCH 026/347] add type and pin icons to vertex labels as html --- .../communications/VisualizationPanel.form | 4 +- .../communications/VisualizationPanel.java | 51 +++++++++++++------ 2 files changed, 37 insertions(+), 18 deletions(-) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form index 0e1d7ff407..5085589359 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.form @@ -11,14 +11,14 @@ - + - + diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index af8d915f65..be4ae4c388 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -30,13 +30,13 @@ import com.mxgraph.model.mxCell; import com.mxgraph.model.mxICell; import com.mxgraph.swing.handler.mxRubberband; import com.mxgraph.swing.mxGraphComponent; -import com.mxgraph.swing.util.mxCellOverlay; import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxConstants; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEventSource; import com.mxgraph.util.mxPoint; +import com.mxgraph.view.mxCellState; import com.mxgraph.view.mxGraph; import com.mxgraph.view.mxGraphView; import com.mxgraph.view.mxStylesheet; @@ -48,6 +48,7 @@ import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseWheelEvent; import java.beans.PropertyVetoException; +import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -97,9 +98,8 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider private static final long serialVersionUID = 1L; private static final Logger logger = Logger.getLogger(VisualizationPanel.class.getName()); - - static final private ImageIcon pinIcon = - new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png")); + private static final URL MARKER_PIN_URL = VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--pin.png"); + static final private ImageIcon pinIcon = new ImageIcon(MARKER_PIN_URL); static final private ImageIcon addPinIcon = new ImageIcon(VisualizationPanel.class.getResource("/org/sleuthkit/autopsy/communications/images/marker--plus.png")); static final private ImageIcon unpinIcon = @@ -112,6 +112,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_SHAPE, mxConstants.SHAPE_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_PERIMETER, mxConstants.PERIMETER_ELLIPSE); mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_FONTCOLOR, "000000"); +// mxStylesheet.getDefaultVertexStyle().put(mxConstants.STYLE_WHITE_SPACE, "wrap"); mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_NOLABEL, true); // mxStylesheet.getDefaultEdgeStyle().put(mxConstants.STYLE_ROUNDED, true); @@ -138,7 +139,27 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider public VisualizationPanel() { initComponents(); - graph = new mxGraph(); + graph = new mxGraph() { + @Override + public String convertValueToString(Object cell) { + Object value = getModel().getValue(cell); + if (value instanceof AccountDeviceInstanceKey) { + final AccountDeviceInstanceKey adiKey = (AccountDeviceInstanceKey) value; + final String accountName = adiKey.getAccountDeviceInstance().getAccount().getTypeSpecificID(); + String iconFileName = Utils.getIconFileName(adiKey.getAccountDeviceInstance().getAccount().getAccountType()); + String label = "" + accountName; + if (pinnedAccountDevices.contains(adiKey)) { + label += ""; + } + return "" + label + ""; + } else { + return ""; + } + } + + }; graph.setCellsCloneable(false); graph.setDropEnabled(false); graph.setCellsCloneable(false); @@ -154,6 +175,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graph.setCellsBendable(true); graph.setKeepEdgesInBackground(true); graph.setResetEdgesOnMove(true); + graph.setHtmlLabels(true); graph.setStylesheet(mxStylesheet); graphComponent = new mxGraphComponent(graph); @@ -192,7 +214,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider JPopupMenu jPopupMenu = new JPopupMenu(); if (pinnedAccountDevices.contains(cellAt.getValue())) { - jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + graph.getLabel(cellAt), unpinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Unpin Account " + cellAt.getId(), unpinIcon) { @Override public void actionPerformed(ActionEvent e) { handleUnPinEvent(new CVTEvents.UnpinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()))); @@ -200,13 +222,13 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider })); } else { - jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + graph.getLabel(cellAt), addPinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Pin Account " + cellAt.getId(), addPinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), false)); } })); - jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + graph.getLabel(cellAt), pinIcon) { + jPopupMenu.add(new JMenuItem(new AbstractAction("Reset and Pin Account " + cellAt.getId(), pinIcon) { @Override public void actionPerformed(ActionEvent e) { handlePinEvent(new CVTEvents.PinAccountsEvent(singleton((AccountDeviceInstanceKey) cellAt.getValue()), true)); @@ -245,15 +267,12 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider Math.random() * graphComponent.getHeight(), size, size); - graph.getView().getState(newVertex, true).setLabel(vertexName); - return newVertex; }); - if (pinnedAccountDevices.contains(accountDeviceInstanceKey)) { - graphComponent.addCellOverlay(vertex, new mxCellOverlay(pinIcon, "pinned")); - } else { - graphComponent.removeCellOverlays(vertex); - } + final mxCellState state = graph.getView().getState(vertex, true); + + graph.getView().updateLabel(state); + graph.getView().updateLabelBounds(state); return vertex; } @@ -444,7 +463,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider setLayout(new BorderLayout()); - splitPane.setDividerLocation(400); + splitPane.setDividerLocation(800); splitPane.setResizeWeight(0.5); jPanel1.setLayout(new BorderLayout()); From 70ed9b48551800857a56da59908c218f0f0bc863 Mon Sep 17 00:00:00 2001 From: millmanorama Date: Sat, 20 Jan 2018 12:40:49 +0100 Subject: [PATCH 027/347] tooltips in VisualizationPanel --- .../autopsy/communications/VisualizationPanel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java index be4ae4c388..f268cee083 100644 --- a/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java +++ b/Core/src/org/sleuthkit/autopsy/communications/VisualizationPanel.java @@ -159,6 +159,11 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider } } + @Override + public String getToolTipForCell(Object cell) { + return ((mxCell) cell).getId(); + } + }; graph.setCellsCloneable(false); graph.setDropEnabled(false); @@ -185,6 +190,7 @@ final public class VisualizationPanel extends JPanel implements Lookup.Provider graphComponent.setConnectable(false); graphComponent.setKeepSelectionVisibleOnZoom(true); graphComponent.setOpaque(true); + graphComponent.setToolTips(true); graphComponent.setBackground(Color.WHITE); jPanel1.add(graphComponent, BorderLayout.CENTER); From 77fd9b4436a2d2a3a8a76bad9021cd80a442a1ac Mon Sep 17 00:00:00 2001 From: esaunders Date: Tue, 23 Jan 2018 16:18:58 -0500 Subject: [PATCH 028/347] Changed TextExtractors to handle Content instead of AbstractFile. --- ...tractor.java => ContentTextExtractor.java} | 16 +++++----- .../keywordsearch/HtmlTextExtractor.java | 17 +++++----- .../KeywordSearchIngestModule.java | 10 +++--- .../keywordsearch/StringsTextExtractor.java | 31 ++++++++++--------- .../keywordsearch/TikaTextExtractor.java | 27 ++++++++-------- 5 files changed, 52 insertions(+), 49 deletions(-) rename KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/{FileTextExtractor.java => ContentTextExtractor.java} (88%) diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/FileTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ContentTextExtractor.java similarity index 88% rename from KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/FileTextExtractor.java rename to KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ContentTextExtractor.java index 689f42591b..b855ae317f 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/FileTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/ContentTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -21,13 +21,13 @@ package org.sleuthkit.autopsy.keywordsearch; import java.io.Reader; import java.util.Arrays; import java.util.List; -import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; /** * Common methods for utilities that extract text and content and divide into * chunks */ -abstract class FileTextExtractor implements TextExtractor< AbstractFile> { +abstract class ContentTextExtractor implements TextExtractor { static final List BLOB_MIME_TYPES @@ -86,25 +86,25 @@ abstract class FileTextExtractor implements TextExtractor< AbstractFile> { * Determines if the file content is supported by the extractor if * isContentTypeSpecific() returns true. * - * @param file to test if its content should be supported + * @param content to test if its content should be supported * @param detectedFormat mime-type with detected format (such as text/plain) * or null if not detected * * @return true if the file content is supported, false otherwise */ - abstract boolean isSupported(AbstractFile file, String detectedFormat); + abstract boolean isSupported(Content file, String detectedFormat); @Override - public abstract Reader getReader(AbstractFile source) throws TextExtractorException; + public abstract Reader getReader(Content source) throws TextExtractorException; @Override - public long getID(AbstractFile source) { + public long getID(Content source) { return source.getId(); } @Override - public String getName(AbstractFile source) { + public String getName(Content source) { return source.getName(); } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java index e5db5b6a10..32842dbc03 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/HtmlTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -32,13 +32,13 @@ import net.htmlparser.jericho.Source; import net.htmlparser.jericho.StartTag; import net.htmlparser.jericho.StartTagType; import org.sleuthkit.autopsy.coreutils.Logger; -import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.ReadContentInputStream; /** - * Extracts text from AbstractFile HTML content. + * Extracts text from HTML content. */ -class HtmlTextExtractor extends FileTextExtractor { +class HtmlTextExtractor extends ContentTextExtractor { static final private Logger logger = Logger.getLogger(HtmlTextExtractor.class.getName()); private static final int MAX_SIZE = 50_000_000; //50MB @@ -63,15 +63,15 @@ class HtmlTextExtractor extends FileTextExtractor { } @Override - boolean isSupported(AbstractFile file, String detectedFormat) { + boolean isSupported(Content content, String detectedFormat) { return detectedFormat != null && WEB_MIME_TYPES.contains(detectedFormat) - && file.getSize() <= MAX_SIZE; + && content.getSize() <= MAX_SIZE; } @Override - public Reader getReader(AbstractFile sourceFile) throws TextExtractorException { - ReadContentInputStream stream = new ReadContentInputStream(sourceFile); + public Reader getReader(Content content) throws TextExtractorException { + ReadContentInputStream stream = new ReadContentInputStream(content); //Parse the stream with Jericho and put the results in a Reader try { @@ -173,6 +173,7 @@ class HtmlTextExtractor extends FileTextExtractor { return false; } + @Override public void logWarning(final String msg, Exception ex) { logger.log(Level.WARNING, msg, ex); //NON-NLS } } diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java index ea45987a4d..8316a598fb 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/KeywordSearchIngestModule.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2017 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -89,7 +89,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { //accessed read-only by searcher thread private boolean startedSearching = false; - private List textExtractors; + private List textExtractors; private StringsTextExtractor stringExtractor; private final KeywordSearchJobSettings settings; private boolean initialized = false; @@ -422,10 +422,10 @@ public final class KeywordSearchIngestModule implements FileIngestModule { * @throws IngesterException exception thrown if indexing failed */ private boolean extractTextAndIndex(AbstractFile aFile, String detectedFormat) throws IngesterException { - FileTextExtractor extractor = null; + ContentTextExtractor extractor = null; //go over available text extractors in order, and pick the first one (most specific one) - for (FileTextExtractor fe : textExtractors) { + for (ContentTextExtractor fe : textExtractors) { if (fe.isSupported(aFile, detectedFormat)) { extractor = fe; break; @@ -515,7 +515,7 @@ public final class KeywordSearchIngestModule implements FileIngestModule { // we skip archive formats that are opened by the archive module. // @@@ We could have a check here to see if the archive module was enabled though... - if (FileTextExtractor.ARCHIVE_MIME_TYPES.contains(fileType)) { + if (ContentTextExtractor.ARCHIVE_MIME_TYPES.contains(fileType)) { try { if (context.fileIngestIsCancelled()) { return; diff --git a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java index 7183cf7346..1737af61ee 100644 --- a/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java +++ b/KeywordSearch/src/org/sleuthkit/autopsy/keywordsearch/StringsTextExtractor.java @@ -1,7 +1,7 @@ /* * Autopsy Forensic Browser * - * Copyright 2011-2016 Basis Technology Corp. + * Copyright 2011-2018 Basis Technology Corp. * Contact: carrier sleuthkit org * * Licensed under the Apache License, Version 2.0 (the "License"); @@ -30,13 +30,14 @@ import org.sleuthkit.autopsy.coreutils.Logger; import org.sleuthkit.autopsy.coreutils.StringExtract; import org.sleuthkit.autopsy.coreutils.StringExtract.StringExtractUnicodeTable.SCRIPT; import org.sleuthkit.datamodel.AbstractFile; +import org.sleuthkit.datamodel.Content; import org.sleuthkit.datamodel.TskCoreException; import org.sleuthkit.datamodel.TskException; /** - * Extracts raw strings from AbstractFile content. + * Extracts raw strings from content. */ -class StringsTextExtractor extends FileTextExtractor { +class StringsTextExtractor extends ContentTextExtractor { static final private Logger logger = Logger.getLogger(StringsTextExtractor.class.getName()); @@ -108,20 +109,20 @@ class StringsTextExtractor extends FileTextExtractor { } @Override - public InputStreamReader getReader(AbstractFile sourceFile) throws TextExtractorException { - InputStream stringStream = getInputStream(sourceFile); + public InputStreamReader getReader(Content content) throws TextExtractorException { + InputStream stringStream = getInputStream(content); return new InputStreamReader(stringStream, Server.DEFAULT_INDEXED_TEXT_CHARSET); } - InputStream getInputStream(AbstractFile sourceFile) { + InputStream getInputStream(Content content) { //check which extract stream to use if (extractScripts.size() == 1 && extractScripts.get(0).equals(SCRIPT.LATIN_1)) { - return new EnglishOnlyStream(sourceFile);//optimal for english, english only + return new EnglishOnlyStream(content);//optimal for english, english only } else { boolean extractUTF8 = Boolean.parseBoolean(extractOptions.get(ExtractOptions.EXTRACT_UTF8.toString())); boolean extractUTF16 = Boolean.parseBoolean(extractOptions.get(ExtractOptions.EXTRACT_UTF16.toString())); - return new InternationalStream(sourceFile, extractScripts, extractUTF8, extractUTF16); + return new InternationalStream(content, extractScripts, extractUTF8, extractUTF16); } } @@ -131,13 +132,13 @@ class StringsTextExtractor extends FileTextExtractor { } @Override - public boolean isSupported(AbstractFile file, String detectedFormat) { + public boolean isSupported(Content content, String detectedFormat) { // strings can be run on anything. return true; } /** - * AbstractFile input string stream reader/converter - given AbstractFile, + * Content input string stream reader/converter - given Content, * extract strings from it and return encoded bytes via read() * * Note: the utility supports extraction of only LATIN script and UTF8, @@ -156,7 +157,7 @@ class StringsTextExtractor extends FileTextExtractor { private static final int MIN_PRINTABLE_CHARS = 4; //num. of chars needed to qualify as a char string //args - private final AbstractFile content; + private final Content content; //internal working data private long contentOffset = 0; //offset in fscontent read into curReadBuf @@ -174,12 +175,12 @@ class StringsTextExtractor extends FileTextExtractor { private final byte[] oneCharBuf = new byte[1]; /** - * Construct new string stream from FsContent. Do not attempt to fill + * Construct new string stream from Content. Do not attempt to fill * entire read buffer if that would break a string * * @param content Content object from which to extract strings. */ - private EnglishOnlyStream(AbstractFile content) { + private EnglishOnlyStream(Content content) { this.content = content; } @@ -372,7 +373,7 @@ class StringsTextExtractor extends FileTextExtractor { private static final Logger logger = Logger.getLogger(InternationalStream.class.getName()); private static final int FILE_BUF_SIZE = 1024 * 1024; - private final AbstractFile content; + private final Content content; private final byte[] oneCharBuf = new byte[1]; private final StringExtract stringExtractor; /** @@ -400,7 +401,7 @@ class StringsTextExtractor extends FileTextExtractor { * @param extractUTF8 whether to extract utf8 encoding * @param extractUTF16 whether to extract utf16 encoding */ - private InternationalStream(AbstractFile content, List